@primer/react
Version:
An implementation of GitHub's Primer Design System using React
143 lines (142 loc) • 4.6 kB
JavaScript
import { useMenuInitialFocus } from "./useMenuInitialFocus.js";
import { useMnemonics } from "./useMnemonics.js";
import { c } from "react-compiler-runtime";
import React from "react";
import { iterateFocusableElements } from "@primer/behaviors/utils";
//#region src/hooks/useMenuKeyboardNavigation.ts
/**
* 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 === void 0 ? 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 || onClose("tab");
};
container === null || container === void 0 || container.addEventListener("keydown", handler);
anchor === null || anchor === void 0 || anchor.addEventListener("keydown", handler);
return () => {
container === null || container === void 0 || container.removeEventListener("keydown", handler);
anchor === null || anchor === 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 || onClose("arrow-left");
};
container === null || container === void 0 || container.addEventListener("keydown", handler);
return () => {
container === null || container === 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 || 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);
};
//#endregion
export { useMenuKeyboardNavigation };