Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 54x 54x 54x 54x 34x 34x 54x 8x 1x 7x 7x 8x 1x 7x 7x 7x 5x 5x 4x 5x 5x 5x 54x 34x 1x 33x 33x 33x 33x 33x | /**
* Custom hook for keyboard shortcut handling
*
* @module hooks/useKeyboardShortcuts
*/
import { useEffect, useCallback, useRef } from 'react';
import { ShortcutMap, UseKeyboardShortcutsOptions } from '../types/keyboard';
import {
getKeyCombination,
shortcutsMatch,
shouldIgnoreKeyboardEvent,
getPlatformShortcut,
areKeyboardShortcutsSupported,
} from '../utils/keyboardUtils';
import logger from '../utils/logger';
/**
* Custom hook for registering and handling keyboard shortcuts
*
* @param options - Configuration options for keyboard shortcuts
*
* @example
* ```tsx
* const shortcuts = {
* 'save': {
* id: 'save',
* keys: 'ctrl+s',
* description: 'Save document',
* category: 'Actions',
* handler: () => handleSave(),
* }
* };
*
* useKeyboardShortcuts({
* shortcuts,
* enabled: true,
* preventDefault: true,
* });
* ```
*/
export function useKeyboardShortcuts(options: UseKeyboardShortcutsOptions): void {
const {
shortcuts,
enabled = true,
preventDefault = true,
stopPropagation = false,
} = options;
// Use ref to avoid recreating handler on every render
const shortcutsRef = useRef<ShortcutMap>(shortcuts);
const optionsRef = useRef({ preventDefault, stopPropagation });
// Update refs when values change
useEffect(() => {
shortcutsRef.current = shortcuts;
optionsRef.current = { preventDefault, stopPropagation };
}, [shortcuts, preventDefault, stopPropagation]);
/**
* Handle keyboard event and match against registered shortcuts
*/
const handleKeyDown = useCallback((event: KeyboardEvent): void => {
// Skip if shortcuts are not supported or event should be ignored
if (!areKeyboardShortcutsSupported() || shouldIgnoreKeyboardEvent(event)) {
return;
}
// Get the key combination from the event
const keyCombination = getKeyCombination(event);
// Find matching shortcut
const matchingShortcut = Object.values(shortcutsRef.current).find(shortcut => {
if (shortcut.enabled === false) {
return false;
}
// Get platform-specific keys if available
const shortcutKeys = getPlatformShortcut(
shortcut.keys,
shortcut.platformKeys
);
// Check if keys match
return shortcutsMatch(keyCombination, shortcutKeys);
});
// Execute handler if match found
if (matchingShortcut) {
logger.debug('Keyboard shortcut triggered', {
id: matchingShortcut.id,
keys: keyCombination,
description: matchingShortcut.description,
});
if (optionsRef.current.preventDefault) {
event.preventDefault();
}
Iif (optionsRef.current.stopPropagation) {
event.stopPropagation();
}
try {
matchingShortcut.handler();
} catch (error) {
logger.error('Error executing keyboard shortcut handler', {
id: matchingShortcut.id,
error,
});
}
}
}, []);
// Register and cleanup event listener
useEffect(() => {
if (!enabled || !areKeyboardShortcutsSupported()) {
return;
}
logger.debug('Registering keyboard shortcuts');
window.addEventListener('keydown', handleKeyDown);
return () => {
logger.debug('Unregistering keyboard shortcuts');
window.removeEventListener('keydown', handleKeyDown);
};
}, [enabled, handleKeyDown]);
}
|