@wordpress/components
Version:
UI components for WordPress.
146 lines (145 loc) • 3.88 kB
JavaScript
import { Component, forwardRef } from "@wordpress/element";
import { focus } from "@wordpress/dom";
import { jsx as _jsx } from "react/jsx-runtime";
const noop = () => {
};
const 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;
}
class NavigableContainer extends Component {
constructor(args) {
super(args);
this.onKeyDown = this.onKeyDown.bind(this);
this.bindContainer = this.bindContainer.bind(this);
this.getFocusableContext = this.getFocusableContext.bind(this);
this.getFocusableIndex = this.getFocusableIndex.bind(this);
}
componentDidMount() {
if (!this.container) {
return;
}
this.container.addEventListener("keydown", this.onKeyDown);
}
componentWillUnmount() {
if (!this.container) {
return;
}
this.container.removeEventListener("keydown", this.onKeyDown);
}
bindContainer(ref) {
const {
forwardedRef
} = this.props;
this.container = ref;
if (typeof forwardedRef === "function") {
forwardedRef(ref);
} else if (forwardedRef && "current" in forwardedRef) {
forwardedRef.current = ref;
}
}
getFocusableContext(target) {
if (!this.container) {
return null;
}
const {
onlyBrowserTabstops
} = this.props;
const finder = onlyBrowserTabstops ? focus.tabbable : focus.focusable;
const focusables = finder.find(this.container);
const index = this.getFocusableIndex(focusables, target);
if (index > -1 && target) {
return {
index,
target,
focusables
};
}
return null;
}
getFocusableIndex(focusables, target) {
return focusables.indexOf(target);
}
onKeyDown(event) {
if (this.props.onKeyDown) {
this.props.onKeyDown(event);
}
const {
getFocusableContext
} = this;
const {
cycle = true,
eventToOffset,
onNavigate = noop,
stopNavigationEvents
} = this.props;
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();
}
}
}
render() {
const {
children,
stopNavigationEvents,
eventToOffset,
onNavigate,
onKeyDown,
cycle,
onlyBrowserTabstops,
forwardedRef,
...restProps
} = this.props;
return /* @__PURE__ */ _jsx("div", {
ref: this.bindContainer,
...restProps,
children
});
}
}
const forwardedNavigableContainer = (props, ref) => {
return /* @__PURE__ */ _jsx(NavigableContainer, {
...props,
forwardedRef: ref
});
};
forwardedNavigableContainer.displayName = "NavigableContainer";
var container_default = forwardRef(forwardedNavigableContainer);
export {
container_default as default
};
//# sourceMappingURL=container.js.map