import { useHtmlElementEventListener } from '../useHtmlElementEventListener' ; import { useMemo , useRef } from 'react' ; import { KeyboardBindings } from '../types' ; import { useTreeEnvironment } from '../controlledEnvironment/ControlledTreeEnvironment' ; import { useKeyboardBindings } from './useKeyboardBindings' ; const elementsThatCanTakeText = [ 'input' , 'textarea' ] ; export const useHotkey = ( combinationName , onHit , active ? , activatableWhileFocusingInput = false , deps ? ) => { const environment = useTreeEnvironment ( ) ; const pressedKeys = useRef < string [ ] > ( [ ] ) ; const keyboardBindings = useKeyboardBindings ( ) ; const possibleCombinations = useMemo ( ( ) => keyboardBindings [ combinationName ] . map ( combination => combination . split ( '+' ) ) , [ combinationName , environment . keyboardBindings ] ) ; useHtmlElementEventListener ( document , 'keydown' , e => { if ( active === false ) { return ; } if ( ( elementsThatCanTakeText . includes ( ( e . target as HTMLElement ) . tagName . toLowerCase ( ) ) || ( e . target as HTMLElement ) . isContentEditable ) && ! activatableWhileFocusingInput ) { return ; } if ( ! pressedKeys . current . includes ( e . key ) ) { pressedKeys . current . push ( e . key ) ; const pressedKeysLowercase = pressedKeys . current . map ( key => key . toLowerCase ( ) ) ; const partialMatch = possibleCombinations . map ( combination => pressedKeysLowercase . map ( key => combination . includes ( key . toLowerCase ( ) ) ) . reduce ( ( a , b ) => a && b , true ) ) . reduce ( ( a , b ) => a || b , false ) ; if ( partialMatch ) { if ( pressedKeys . current . length > 1 || ! /^[a-zA-Z]$/ . test ( e . key ) ) { e . preventDefault ( ) ; } } } } , [ active ] ) ; useHtmlElementEventListener ( document , 'keyup' , e => { if ( active === false ) { return ; } const pressedKeysLowercase = pressedKeys . current . map ( key => key . toLowerCase ( ) ) ; const match = possibleCombinations . map ( combination => combination . map ( key => pressedKeysLowercase . includes ( key . toLowerCase ( ) ) ) . reduce ( ( a , b ) => a && b , true ) ) . reduce ( ( a , b ) => a || b , false ) ; if ( match ) { requestAnimationFrame ( ( ) => onHit ( e ) ) ; } pressedKeys . current = pressedKeys . current . filter ( key => key !== e . key ) ; } , [ possibleCombinations , onHit , active , ... ( deps ?? [ ] ) ] ) ; } ;