/**
* Integration Examples
* Complete examples showing all 4 keyboard/event hooks working together
*
* These examples demonstrate real-world usage patterns and best practices.
*/
import { useEffect, useRef, useState } from 'react'
import { useKeyboardShortcuts } from './useKeyboardShortcuts'
import { useClickOutside } from './useClickOutside'
import { useHotkeys } from './useHotkeys'
import { useEventListener } from './useEventListener'
/**
* Example 1: Command Palette with Keyboard Shortcuts
*
* Features:
* - Open with Ctrl+K
* - Navigate with arrow keys
* - Select with Enter
* - Close with Escape
*/
export function CommandPaletteExample() {
const [isOpen, setIsOpen] = useState(false)
const [selectedIndex, setSelectedIndex] = useState(0)
const { registerShortcut, unregister } = useKeyboardShortcuts()
useEffect(() => {
// Open palette
const openId = registerShortcut({
key: 'k',
ctrl: true,
onPress: () => setIsOpen(true),
preventDefault: true,
})
// Navigation
const upId = registerShortcut({
key: 'ArrowUp',
onPress: () => setSelectedIndex((prev) => Math.max(0, prev - 1)),
})
const downId = registerShortcut({
key: 'ArrowDown',
onPress: () => setSelectedIndex((prev) => prev + 1),
})
// Selection
const selectId = registerShortcut({
key: 'Enter',
onPress: () => handleSelect(),
})
// Close
const closeId = registerShortcut({
key: 'Escape',
onPress: () => setIsOpen(false),
})
return () => {
unregister(openId)
unregister(upId)
unregister(downId)
unregister(selectId)
unregister(closeId)
}
}, [registerShortcut])
const handleSelect = () => {
console.log('Selected index:', selectedIndex)
setIsOpen(false)
}
return (
Command Palette (Ctrl+K)
{isOpen && (
)}
)
}
/**
* Example 2: Advanced Modal with Click Outside Detection
*
* Features:
* - Click outside closes modal
* - Trigger button excluded from close
* - Backdrop animation delay
* - Touch and mouse support
*/
export function AdvancedModalExample() {
const triggerButtonRef = useRef(null)
const modal = useClickOutside({
excludeRefs: [triggerButtonRef],
onClickOutside: () => {
console.log('Modal closed by outside click')
},
includeTouch: true,
delayMs: 0, // Immediate close
})
return (
{modal.isOpen && (
Modal Content
)}
)
}
/**
* Example 3: IDE-like Editor with Multiple Hotkeys
*
* Features:
* - Save: Ctrl+S / Cmd+S
* - Find: Ctrl+F / Cmd+F
* - Replace: Ctrl+H / Cmd+H
* - Close: Escape
* - Format: Ctrl+Shift+I / Cmd+Shift+I
*/
export function IDEEditorExample() {
const hotkeys = useHotkeys()
useEffect(() => {
// Save file
hotkeys.register('ctrl+s', () => {
console.log('Saving file...')
}, {
preventDefault: true,
})
// Find dialog
hotkeys.register('ctrl+f', () => {
console.log('Opening find...')
}, {
preventDefault: true,
})
// Find & Replace
hotkeys.register('ctrl+h', () => {
console.log('Opening find & replace...')
}, {
preventDefault: true,
})
// Format code
hotkeys.register('ctrl+shift+i', () => {
console.log('Formatting code...')
}, {
preventDefault: true,
})
// Close
hotkeys.register('Escape', () => {
console.log('Closing dialogs...')
})
// Cleanup all hotkeys on unmount
return () => hotkeys.unregisterAll()
}, [hotkeys])
return (
IDE Editor
Try: Ctrl+S, Ctrl+F, Ctrl+H, Ctrl+Shift+I
)
}
/**
* Example 4: Window Resize Listener with Performance
*
* Features:
* - Track window size
* - Passive listener for performance
* - Debounced updates
* - Responsive breakpoints
*/
export function ResponsiveLayoutExample() {
const [windowSize, setWindowSize] = useState({
width: typeof window !== 'undefined' ? window.innerWidth : 0,
height: typeof window !== 'undefined' ? window.innerHeight : 0,
})
const debounceRef = useRef>()
const { add } = useEventListener()
useEffect(() => {
// Add passive resize listener
return add(window, 'resize', (e: UIEvent) => {
// Debounce the update
if (debounceRef.current) {
clearTimeout(debounceRef.current)
}
debounceRef.current = setTimeout(() => {
const target = e.target as Window
setWindowSize({
width: target.innerWidth,
height: target.innerHeight,
})
}, 100)
}, {
passive: true,
})
}, [add])
const isMobile = windowSize.width < 640
const isTablet = windowSize.width >= 640 && windowSize.width < 1024
const isDesktop = windowSize.width >= 1024
return (
Responsive Layout
Size: {windowSize.width}x{windowSize.height}
{isMobile &&
Mobile view
}
{isTablet &&
Tablet view
}
{isDesktop &&
Desktop view
}
)
}
/**
* Example 5: Complete Dashboard with All Hooks
*
* Features:
* - Command palette (Ctrl+K)
* - Dropdown menus (click outside)
* - Global hotkeys (save, search)
* - Event listeners (resize, scroll)
*/
export function CompleteDashboardExample() {
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false)
const [userMenuOpen, setUserMenuOpen] = useState(false)
const [scrollPosition, setScrollPosition] = useState(0)
const userMenuRef = useClickOutside({
onClickOutside: () => setUserMenuOpen(false),
})
const { registerShortcut, unregister } = useKeyboardShortcuts()
const hotkeys = useHotkeys()
const { add } = useEventListener()
useEffect(() => {
// Command palette shortcut
const cmdId = registerShortcut({
key: 'k',
ctrl: true,
onPress: () => setCommandPaletteOpen(true),
preventDefault: true,
})
// Close command palette with Escape
const escapeId = registerShortcut({
key: 'Escape',
onPress: () => setCommandPaletteOpen(false),
})
return () => {
unregister(cmdId)
unregister(escapeId)
}
}, [registerShortcut, unregister])
useEffect(() => {
// Global hotkeys for dashboard
hotkeys.register('ctrl+s', () => {
console.log('Saving dashboard...')
}, { preventDefault: true })
hotkeys.register('ctrl+f', () => {
console.log('Opening search...')
}, { preventDefault: true })
return () => hotkeys.unregisterAll()
}, [hotkeys])
useEffect(() => {
// Track scroll position
return add(window, 'scroll', (e: Event) => {
setScrollPosition(window.scrollY)
}, {
passive: true,
})
}, [add])
return (
Dashboard
{userMenuRef.isOpen && (
)}
Scroll position: {scrollPosition}
Try Ctrl+K for command palette, Ctrl+S to save
{commandPaletteOpen && (
)}
)
}
/**
* Example 6: Custom Hook Composition
*
* Shows how to create a higher-level hook that combines
* all 4 keyboard/event hooks for common patterns.
*/
export function useDialogWithShortcuts() {
const [isOpen, setIsOpen] = useState(false)
const { ref, isOpen: clickOutsideOpen, setIsOpen: setClickOutsideOpen } =
useClickOutside({
onClickOutside: () => setIsOpen(false),
})
const { registerShortcut, unregister } = useKeyboardShortcuts()
useEffect(() => {
if (!isOpen) return
// Close with Escape
const id = registerShortcut({
key: 'Escape',
onPress: () => setIsOpen(false),
})
return () => unregister(id)
}, [isOpen, registerShortcut, unregister])
return {
ref,
isOpen,
setIsOpen,
}
}
// Usage of custom hook
export function DialogWithShortcutsExample() {
const dialog = useDialogWithShortcuts()
return (
{dialog.isOpen && (
Dialog (Click outside or press Escape to close)
)}
)
}
/**
* Example 7: Form with Save Hotkey
*
* Features:
* - Auto-save on Ctrl+S
* - Debounced keyboard input
* - Click outside closes suggestions
*/
export function FormWithHotkeysExample() {
const [formData, setFormData] = useState({ name: '', email: '' })
const [showSuggestions, setShowSuggestions] = useState(false)
const suggestionsRef = useClickOutside({
onClickOutside: () => setShowSuggestions(false),
})
const { registerShortcut, unregister } = useKeyboardShortcuts()
const { add } = useEventListener()
useEffect(() => {
// Save with Ctrl+S
const saveId = registerShortcut({
key: 's',
ctrl: true,
onPress: () => {
console.log('Saving form:', formData)
},
preventDefault: true,
})
return () => unregister(saveId)
}, [formData, registerShortcut, unregister])
useEffect(() => {
// Listen for input changes to show suggestions
const inputElement = document.getElementById('email-input') as HTMLInputElement
if (!inputElement) return
return add(inputElement, 'focus', () => {
setShowSuggestions(true)
})
}, [add])
return (
)
}