@primer/react
Version:
An implementation of GitHub's Primer Design System using React
140 lines (134 loc) • 4.89 kB
JavaScript
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 };