@wordpress/components
Version:
UI components for WordPress.
109 lines (108 loc) • 3.34 kB
JavaScript
// 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