import { useCallback, useEffect } from 'react'; import useLatest from '../../hooks/useLatest'; import { useShortcutsManagerStoreRef } from '../ShortcutsManager'; import type { ShortcutEventDetail } from '../types'; import { calculateShortcutEventName } from '../utils'; import type { ShortcutCallbackArgs } from './types'; const useShortcut = ( name: string, callback: (args: ShortcutCallbackArgs) => void, disabled: boolean = false, capture: boolean = false, ) => { const { store } = useShortcutsManagerStoreRef(); const nameRef = useLatest(name); const callbackRef = useLatest(callback); const onCallback = useCallback( (e: CustomEvent) => { const shortcut = store.current.shortcuts[nameRef.current]; if (!shortcut) { throw new Error(`Shortcut with name: ${nameRef.current} does not exist`); } const scope = store.current.scopes[shortcut.scope || '']; if (shortcut.scope && !scope) return; // if there is node, we check if one of the elements instead it is focused if ( scope?.node && !( scope.node === document.activeElement || scope.node.contains(document.activeElement) ) ) return; callbackRef.current({ ...e.detail, shortcut: shortcut, event: e }); }, [callbackRef, nameRef, store], ); useEffect(() => { if (disabled) return; const eventName = calculateShortcutEventName(nameRef.current); document.addEventListener(eventName, onCallback as (e: Event) => void, capture); return () => { document.removeEventListener(eventName, onCallback as (e: Event) => void, capture); }; // eslint-disable-next-line }, [onCallback, nameRef, disabled]); }; export default useShortcut;