veza/apps/web/src/hooks/useKeyboardNavigation.ts
2025-12-12 21:34:34 -05:00

88 lines
1.9 KiB
TypeScript

import { useEffect, useCallback } from 'react';
interface KeyboardNavigationOptions {
onEscape?: () => void;
onEnter?: () => void;
onArrowUp?: () => void;
onArrowDown?: () => void;
onArrowLeft?: () => void;
onArrowRight?: () => void;
onTab?: () => void;
onShiftTab?: () => void;
enabled?: boolean;
}
export function useKeyboardNavigation(options: KeyboardNavigationOptions) {
const {
onEscape,
onEnter,
onArrowUp,
onArrowDown,
onArrowLeft,
onArrowRight,
onTab,
onShiftTab,
enabled = true,
} = options;
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (!enabled) return;
switch (event.key) {
case 'Escape':
onEscape?.();
break;
case 'Enter':
if (
event.target instanceof HTMLButtonElement ||
event.target instanceof HTMLAnchorElement
) {
onEnter?.();
}
break;
case 'ArrowUp':
event.preventDefault();
onArrowUp?.();
break;
case 'ArrowDown':
event.preventDefault();
onArrowDown?.();
break;
case 'ArrowLeft':
event.preventDefault();
onArrowLeft?.();
break;
case 'ArrowRight':
event.preventDefault();
onArrowRight?.();
break;
case 'Tab':
if (event.shiftKey) {
onShiftTab?.();
} else {
onTab?.();
}
break;
}
},
[
enabled,
onEscape,
onEnter,
onArrowUp,
onArrowDown,
onArrowLeft,
onArrowRight,
onTab,
onShiftTab,
],
);
useEffect(() => {
if (enabled) {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}
}, [enabled, handleKeyDown]);
}