UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

77 lines (69 loc) 3.25 kB
'use strict'; var React = require('react'); var utils = require('@primer/behaviors/utils'); var useMenuInitialFocus = require('./useMenuInitialFocus.js'); var useMnemonics = require('./useMnemonics.js'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React__default = /*#__PURE__*/_interopDefault(React); /** * Keyboard navigation is a mix of 4 hooks * 1. useMenuInitialFocus * 2. useTypeaheadFocus * 3. useCloseMenuOnTab * 4. useMoveFocusToMenuItem */ const useMenuKeyboardNavigation = (open, onClose, containerRef, anchorRef) => { useMenuInitialFocus.useMenuInitialFocus(open, containerRef, anchorRef); useMnemonics.useMnemonics(open, containerRef); useCloseMenuOnTab(open, onClose, containerRef, anchorRef); useMoveFocusToMenuItem(open, containerRef, anchorRef); }; /** * When Tab or Shift+Tab is pressed, the menu should close * and the focus should naturally move to the next item */ const useCloseMenuOnTab = (open, onClose, containerRef, anchorRef) => { React__default.default.useEffect(() => { const container = containerRef.current; const anchor = anchorRef.current; const handler = event => { if (open && event.key === 'Tab') onClose === null || onClose === void 0 ? void 0 : onClose('tab'); }; container === null || container === void 0 ? void 0 : container.addEventListener('keydown', handler); anchor === null || anchor === void 0 ? void 0 : anchor.addEventListener('keydown', handler); return () => { container === null || container === void 0 ? void 0 : container.removeEventListener('keydown', handler); anchor === null || anchor === void 0 ? void 0 : anchor.removeEventListener('keydown', handler); }; }, [open, onClose, containerRef, anchorRef]); }; /** * When Arrow Keys are pressed and the focus is on the anchor, * focus should move to a menu item */ const useMoveFocusToMenuItem = (open, containerRef, anchorRef) => { React__default.default.useEffect(() => { const container = containerRef.current; const anchor = anchorRef.current; const handler = event => { if (!open || !container) return; const iterable = utils.iterateFocusableElements(container); if (event.key === 'ArrowDown') { // TODO: does commenting this out break anything? // event.preventDefault() // prevent scroll event const firstElement = iterable.next().value; /** We push imperative focus to the next tick to prevent React's batching */ setTimeout(() => firstElement === null || firstElement === void 0 ? void 0 : firstElement.focus()); } else if (event.key === 'ArrowUp') { // TODO: does commenting this out break anything? // event.preventDefault() // prevent scroll event const elements = [...iterable]; const lastElement = elements[elements.length - 1]; setTimeout(() => lastElement.focus()); } }; anchor === null || anchor === void 0 ? void 0 : anchor.addEventListener('keydown', handler); return () => anchor === null || anchor === void 0 ? void 0 : anchor.addEventListener('keydown', handler); }, [open, containerRef, anchorRef]); }; exports.useMenuKeyboardNavigation = useMenuKeyboardNavigation;