@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
203 lines (202 loc) • 7.51 kB
JavaScript
;
"use client";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = MenuRoot;
var _push = _interopRequireDefault(require("core-js-pure/stable/instance/push.js"));
var _react = _interopRequireWildcard(require("react"));
var _clsx = _interopRequireDefault(require("clsx"));
var _Popover = _interopRequireDefault(require("../popover/Popover.js"));
var _MenuContext2 = require("./MenuContext.js");
var _withComponentMarkers = _interopRequireDefault(require("../../shared/helpers/withComponentMarkers.js"));
var _whatInput = _interopRequireDefault(require("../../shared/helpers/whatInput.js"));
var _MenuButton = _interopRequireDefault(require("./MenuButton.js"));
var _MenuAction = _interopRequireDefault(require("./MenuAction.js"));
var _useIsomorphicLayoutEffect = _interopRequireDefault(require("../../shared/helpers/useIsomorphicLayoutEffect.js"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function MenuRoot(props) {
var _MenuContext;
const {
id,
className,
children,
placement = 'bottom',
arrowPosition = 'center',
open,
onOpenChange,
skipPortal = false,
noAnimation = false,
autoAlignMode = 'initial'
} = props;
let triggerChild = null;
const contentChildren = [];
_react.default.Children.forEach(children, child => {
if (!triggerChild && _react.default.isValidElement(child) && (child.type === _MenuButton.default || child.type === _MenuAction.default)) {
triggerChild = child;
} else {
(0, _push.default)(contentChildren).call(contentChildren, child);
}
});
const parentContext = (0, _MenuContext2.useMenuContext)();
const level = parentContext ? parentContext.level + 1 : 0;
const activeIndexRef = (0, _react.useRef)(-1);
const [activeIndex, setActiveIndexState] = (0, _react.useState)(-1);
const itemRefsRef = (0, _react.useRef)([]);
const nextIndexRef = (0, _react.useRef)(0);
const menuRef = (0, _react.useRef)(null);
const [isOpenInternal, setIsOpenInternal] = (0, _react.useState)(false);
const isControlled = typeof open === 'boolean';
const isOpen = isControlled ? open : isOpenInternal;
const popoverCloseRef = (0, _react.useRef)(null);
const setOpenState = (0, _react.useCallback)(next => {
if (!isControlled) {
setIsOpenInternal(next);
}
onOpenChange === null || onOpenChange === void 0 || onOpenChange(next);
}, [isControlled, onOpenChange]);
const closeAll = (0, _react.useCallback)(() => {
if (popoverCloseRef.current) {
popoverCloseRef.current();
} else {
setOpenState(false);
}
parentContext === null || parentContext === void 0 || parentContext.closeAll();
}, [setOpenState, parentContext]);
const closeSelf = (0, _react.useCallback)(() => {
if (popoverCloseRef.current) {
popoverCloseRef.current();
} else {
setOpenState(false);
}
}, [setOpenState]);
const setActiveIndex = (0, _react.useCallback)(index => {
activeIndexRef.current = index;
setActiveIndexState(index);
}, []);
const registerItem = (0, _react.useCallback)(ref => {
const index = nextIndexRef.current;
nextIndexRef.current += 1;
itemRefsRef.current[index] = ref;
return index;
}, []);
const unregisterItem = (0, _react.useCallback)(index => {
itemRefsRef.current[index] = undefined;
}, []);
const contextValue = (0, _react.useMemo)(() => ({
level,
closeAll,
closeSelf,
activeIndex,
setActiveIndex,
registerItem,
unregisterItem,
itemRefs: itemRefsRef,
menuRef,
isOpen
}), [level, closeAll, closeSelf, activeIndex, setActiveIndex, registerItem, unregisterItem, isOpen]);
(0, _react.useEffect)(() => {
if (!isOpen) {
nextIndexRef.current = 0;
itemRefsRef.current = [];
setActiveIndex(-1);
}
}, [isOpen, setActiveIndex]);
(0, _useIsomorphicLayoutEffect.default)(() => {
if (isOpen) {
_whatInput.default.specificKeys([9, 37, 38, 39, 40, 33, 34, 35, 36]);
}
return () => {
_whatInput.default.specificKeys([9]);
};
}, [isOpen]);
const focusOnOpenElement = (0, _react.useCallback)(() => {
var _menuRef$current;
return (_menuRef$current = menuRef.current) !== null && _menuRef$current !== void 0 ? _menuRef$current : null;
}, []);
const handleFocusComplete = (0, _react.useCallback)(() => {
if (level === 0) {
return;
}
const firstRef = itemRefsRef.current[0];
if (firstRef !== null && firstRef !== void 0 && firstRef.current) {
firstRef.current.focus();
setActiveIndex(0);
}
}, [level, setActiveIndex]);
const handleOpenChange = (0, _react.useCallback)(next => {
setOpenState(next);
}, [setOpenState]);
const handleTriggerKeyDown = (0, _react.useCallback)(event => {
if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'ArrowRight') {
event.preventDefault();
if (!isOpen) {
handleOpenChange(true);
}
requestAnimationFrame(() => {
const firstRef = itemRefsRef.current[0];
if (firstRef !== null && firstRef !== void 0 && firstRef.current) {
firstRef.current.focus();
setActiveIndex(0);
}
});
}
}, [isOpen, handleOpenChange, setActiveIndex]);
const resolvedTrigger = triggerChild ? renderProps => {
const domProps = {
...renderProps
};
const {
active,
open: openFn,
close: closeFn,
toggle: toggleFn
} = renderProps;
return (0, _jsxRuntime.jsx)(_MenuContext2.MenuTriggerContext, {
value: {
active,
triggerProps: domProps,
open: openFn,
close: closeFn,
toggle: toggleFn
},
children: triggerChild
});
} : undefined;
return (0, _jsxRuntime.jsx)(_Popover.default, {
id: id,
className: (0, _clsx.default)('dnb-menu', className),
trigger: resolvedTrigger,
triggerAttributes: {
'aria-haspopup': 'menu',
onKeyDown: handleTriggerKeyDown
},
placement: placement,
open: isOpen,
onOpenChange: handleOpenChange,
skipPortal: level > 0 ? true : skipPortal,
autoAlignMode: autoAlignMode,
arrowPosition: arrowPosition,
noAnimation: noAnimation,
hideCloseButton: true,
noInnerSpace: true,
focusOnOpenElement: focusOnOpenElement,
onFocusComplete: handleFocusComplete,
contentClassName: "dnb-menu__popover-content",
children: ({
close
}) => {
popoverCloseRef.current = close;
return _MenuContext || (_MenuContext = (0, _jsxRuntime.jsx)(_MenuContext2.MenuContext, {
value: contextValue,
children: contentChildren
}));
}
});
}
(0, _withComponentMarkers.default)(MenuRoot, {
_supportsSpacingProps: true
});
//# sourceMappingURL=MenuRoot.js.map