UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

140 lines (134 loc) 4.89 kB
import { c } from 'react-compiler-runtime'; import React from 'react'; import { iterateFocusableElements } from '@primer/behaviors/utils'; import { useMenuInitialFocus } from './useMenuInitialFocus.js'; import { useMnemonics } from './useMnemonics.js'; /** * Keyboard navigation is a mix of 4 hooks * 1. useMenuInitialFocus * 2. useTypeaheadFocus * 3. useCloseMenuOnTab * 4. useMoveFocusToMenuItem */ const useMenuKeyboardNavigation = (open, onClose, containerRef, anchorRef, t0) => { const isSubmenu = t0 === undefined ? false : t0; useMenuInitialFocus(open, containerRef, anchorRef); useMnemonics(open, containerRef); useCloseMenuOnTab(open, onClose, containerRef, anchorRef); useMoveFocusToMenuItem(open, containerRef, anchorRef); useCloseSubmenuOnArrow(open, isSubmenu, onClose, containerRef); }; /** * 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) => { const $ = c(6); let t0; let t1; if ($[0] !== anchorRef || $[1] !== containerRef || $[2] !== onClose || $[3] !== open) { t0 = () => { const container = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current; const anchor = anchorRef === null || anchorRef === void 0 ? void 0 : 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); }; }; t1 = [open, onClose, containerRef, anchorRef]; $[0] = anchorRef; $[1] = containerRef; $[2] = onClose; $[3] = open; $[4] = t0; $[5] = t1; } else { t0 = $[4]; t1 = $[5]; } React.useEffect(t0, t1); }; /** * Close submenu when left arrow key is pressed */ const useCloseSubmenuOnArrow = (open, isSubmenu, onClose, containerRef) => { const $ = c(6); let t0; let t1; if ($[0] !== containerRef || $[1] !== isSubmenu || $[2] !== onClose || $[3] !== open) { t0 = () => { const container = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current; const handler = event => { if (open && isSubmenu && event.key === "ArrowLeft") { onClose === null || onClose === void 0 ? void 0 : onClose("arrow-left"); } }; container === null || container === void 0 ? void 0 : container.addEventListener("keydown", handler); return () => { container === null || container === void 0 ? void 0 : container.removeEventListener("keydown", handler); }; }; t1 = [open, onClose, containerRef, isSubmenu]; $[0] = containerRef; $[1] = isSubmenu; $[2] = onClose; $[3] = open; $[4] = t0; $[5] = t1; } else { t0 = $[4]; t1 = $[5]; } React.useEffect(t0, t1); }; /** * When Arrow Keys are pressed and the focus is on the anchor, * focus should move to a menu item */ const useMoveFocusToMenuItem = (open, containerRef, anchorRef) => { const $ = c(5); let t0; let t1; if ($[0] !== anchorRef || $[1] !== containerRef || $[2] !== open) { t0 = () => { const container = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current; const anchor = anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current; const handler = event => { if (!open || !container) { return; } const iterable = iterateFocusableElements(container); if (event.key === "ArrowDown") { const firstElement = iterable.next().value; setTimeout(() => firstElement === null || firstElement === void 0 ? void 0 : firstElement.focus()); } else { if (event.key === "ArrowUp") { 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); }; t1 = [open, containerRef, anchorRef]; $[0] = anchorRef; $[1] = containerRef; $[2] = open; $[3] = t0; $[4] = t1; } else { t0 = $[3]; t1 = $[4]; } React.useEffect(t0, t1); }; export { useMenuKeyboardNavigation };