65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
import { useEffect, useRef, useCallback } from 'react';
|
|
|
|
export interface ShortcutBinding {
|
|
keys: string; // e.g. "Control+k", " ", "m", "alt+1"
|
|
description: string;
|
|
action: () => void;
|
|
}
|
|
|
|
export const useKeyboardShortcuts = (bindings: ShortcutBinding[], enabled: boolean = true) => {
|
|
const bindingsRef = useRef<ShortcutBinding[]>(bindings);
|
|
|
|
useEffect(() => {
|
|
bindingsRef.current = bindings;
|
|
}, [bindings]);
|
|
|
|
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
|
if (!enabled) return;
|
|
|
|
// Avoid hijacking keystrokes when editing inputs
|
|
const active = document.activeElement;
|
|
if (active) {
|
|
const name = active.tagName.toLowerCase();
|
|
if (name === 'input' || name === 'textarea' || active.getAttribute('contenteditable') === 'true') {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const pressedKey = e.key.toLowerCase();
|
|
const isCtrl = e.ctrlKey || e.metaKey;
|
|
const isAlt = e.altKey;
|
|
const isShift = e.shiftKey;
|
|
|
|
for (const binding of bindingsRef.current) {
|
|
const keys = binding.keys.toLowerCase().split('+');
|
|
|
|
const requiresCtrl = keys.includes('control') || keys.includes('ctrl');
|
|
const requiresAlt = keys.includes('alt');
|
|
const requiresShift = keys.includes('shift');
|
|
|
|
const baseKey = keys.filter(k => !['control', 'ctrl', 'alt', 'shift'].includes(k))[0];
|
|
|
|
const matchesCtrl = requiresCtrl ? isCtrl : !isCtrl;
|
|
const matchesAlt = requiresAlt ? isAlt : !isAlt;
|
|
const matchesShift = requiresShift ? isShift : !isShift;
|
|
|
|
const normalizedBaseKey = baseKey === 'space' ? ' ' : baseKey;
|
|
const matchesBase = pressedKey === normalizedBaseKey;
|
|
|
|
if (matchesCtrl && matchesAlt && matchesShift && matchesBase) {
|
|
e.preventDefault();
|
|
binding.action();
|
|
break;
|
|
}
|
|
}
|
|
}, [enabled]);
|
|
|
|
useEffect(() => {
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
return () => {
|
|
window.removeEventListener('keydown', handleKeyDown);
|
|
};
|
|
}, [handleKeyDown]);
|
|
};
|
|
export default useKeyboardShortcuts;
|