UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

73 lines (67 loc) 2.58 kB
'use strict'; var React = require('react'); /** * Calls all handlers in reverse order * @param event The KeyboardEvent generated by the Escape keydown. */ function handleEscape(event) { if (!event.defaultPrevented) { for (const handler of Object.values(registry).reverse()) { handler(event); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (event.defaultPrevented) break; } } } const registry = {}; function register(id, handler) { registry[id] = handler; } function deregister(id) { delete registry[id]; } // For auto-incrementing unique identifiers for registered handlers. let handlerId = 0; /** * Sets up a `keydown` listener on `window.document`. If * 1) The pressed key is "Escape", and * 2) The event has not had `.preventDefault()` called * The given callback will be executed. * * Note: If multiple `useOnEscapePress` hooks are active simultaneously, the * callbacks will occur in reverse order. In other words, if a parent component * and a child component both call `useOnEscapePress`, when the user presses * Escape, the child component's callback will execute, followed by the parent's * callback. Each callback has the chance to call `.preventDefault()` on the * event to prevent further callbacks. * * @param callback {(e: KeyboardEvent) => void} The callback that gets executed * when the Escape key is pressed. The KeyboardEvent generated by the Escape * keypress is passed as the only argument. * * @param callbackDependencies {React.DependencyList} The dependencies of the given * `onEscape` callback for memoization. Omit this param if the callback is already * memoized. See `React.useCallback` for more info on memoization. */ const useOnEscapePress = (onEscape, callbackDependencies = [onEscape]) => { // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps const escapeCallback = React.useCallback(onEscape, callbackDependencies); const handler = React.useCallback(event => { if (event.key === 'Escape') escapeCallback(event); }, [escapeCallback]); const id = React.useMemo(() => handlerId++, []); React.useEffect(() => { if (Object.keys(registry).length === 0) { document.addEventListener('keydown', handleEscape); } register(id, handler); return () => { deregister(id); if (Object.keys(registry).length === 0) { document.removeEventListener('keydown', handleEscape); } }; }, [id, handler]); }; exports.useOnEscapePress = useOnEscapePress;