UNPKG

@wordpress/components

Version:
109 lines (108 loc) 3.34 kB
// packages/components/src/navigable-container/container.tsx import { forwardRef, useRef, useEffect, useCallback } from "@wordpress/element"; import { useMergeRefs } from "@wordpress/compose"; import { focus } from "@wordpress/dom"; import { jsx as _jsx } from "react/jsx-runtime"; var noop = () => { }; var MENU_ITEM_ROLES = ["menuitem", "menuitemradio", "menuitemcheckbox"]; function cycleValue(value, total, offset) { const nextValue = value + offset; if (nextValue < 0) { return total + nextValue; } else if (nextValue >= total) { return nextValue - total; } return nextValue; } function UnforwardedNavigableContainer({ children, stopNavigationEvents, eventToOffset, onNavigate = noop, onKeyDown, cycle = true, onlyBrowserTabstops, ...restProps }, ref) { const containerRef = useRef(null); const getFocusableIndex = useCallback((focusables, target) => { return focusables.indexOf(target); }, []); const getFocusableContext = useCallback((target) => { if (!containerRef.current) { return null; } const finder = onlyBrowserTabstops ? focus.tabbable : focus.focusable; const focusables = finder.find(containerRef.current); const index = getFocusableIndex(focusables, target); if (index > -1 && target) { return { index, target, focusables }; } return null; }, [onlyBrowserTabstops, getFocusableIndex]); useEffect(() => { const container = containerRef.current; if (!container) { return; } function handleKeyDown(event) { if (onKeyDown) { onKeyDown(event); } const offset = eventToOffset(event); if (offset !== void 0 && stopNavigationEvents) { event.stopImmediatePropagation(); const targetRole = event.target?.getAttribute("role"); const targetHasMenuItemRole = !!targetRole && MENU_ITEM_ROLES.includes(targetRole); if (targetHasMenuItemRole) { event.preventDefault(); } } if (!offset) { return; } const activeElement = event.target?.ownerDocument?.activeElement; if (!activeElement) { return; } const context = getFocusableContext(activeElement); if (!context) { return; } const { index, focusables } = context; const nextIndex = cycle ? cycleValue(index, focusables.length, offset) : index + offset; if (nextIndex >= 0 && nextIndex < focusables.length) { focusables[nextIndex].focus(); onNavigate(nextIndex, focusables[nextIndex]); if (event.code === "Tab") { event.preventDefault(); } } } container.addEventListener("keydown", handleKeyDown); return () => { container.removeEventListener("keydown", handleKeyDown); }; }, [onKeyDown, eventToOffset, stopNavigationEvents, cycle, onNavigate, getFocusableContext]); const mergedRef = useMergeRefs([containerRef, ref]); return /* @__PURE__ */ _jsx("div", { ref: mergedRef, ...restProps, children }); } var NavigableContainer = forwardRef(UnforwardedNavigableContainer); NavigableContainer.displayName = "NavigableContainer"; var container_default = NavigableContainer; export { container_default as default }; //# sourceMappingURL=container.mjs.map