UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

196 lines (195 loc) 6.14 kB
"use client"; import _pushInstanceProperty from "core-js-pure/stable/instance/push.js"; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import clsx from 'clsx'; import Popover from "../popover/Popover.js"; import { MenuContext, MenuTriggerContext, useMenuContext } from "./MenuContext.js"; import withComponentMarkers from "../../shared/helpers/withComponentMarkers.js"; import whatInput from "../../shared/helpers/whatInput.js"; import MenuButton from "./MenuButton.js"; import MenuAction from "./MenuAction.js"; import useIsomorphicLayoutEffect from "../../shared/helpers/useIsomorphicLayoutEffect.js"; import { jsx as _jsx } from "react/jsx-runtime"; export default 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.Children.forEach(children, child => { if (!triggerChild && React.isValidElement(child) && (child.type === MenuButton || child.type === MenuAction)) { triggerChild = child; } else { _pushInstanceProperty(contentChildren).call(contentChildren, child); } }); const parentContext = useMenuContext(); const level = parentContext ? parentContext.level + 1 : 0; const activeIndexRef = useRef(-1); const [activeIndex, setActiveIndexState] = useState(-1); const itemRefsRef = useRef([]); const nextIndexRef = useRef(0); const menuRef = useRef(null); const [isOpenInternal, setIsOpenInternal] = useState(false); const isControlled = typeof open === 'boolean'; const isOpen = isControlled ? open : isOpenInternal; const popoverCloseRef = useRef(null); const setOpenState = useCallback(next => { if (!isControlled) { setIsOpenInternal(next); } onOpenChange === null || onOpenChange === void 0 || onOpenChange(next); }, [isControlled, onOpenChange]); const closeAll = useCallback(() => { if (popoverCloseRef.current) { popoverCloseRef.current(); } else { setOpenState(false); } parentContext === null || parentContext === void 0 || parentContext.closeAll(); }, [setOpenState, parentContext]); const closeSelf = useCallback(() => { if (popoverCloseRef.current) { popoverCloseRef.current(); } else { setOpenState(false); } }, [setOpenState]); const setActiveIndex = useCallback(index => { activeIndexRef.current = index; setActiveIndexState(index); }, []); const registerItem = useCallback(ref => { const index = nextIndexRef.current; nextIndexRef.current += 1; itemRefsRef.current[index] = ref; return index; }, []); const unregisterItem = useCallback(index => { itemRefsRef.current[index] = undefined; }, []); const contextValue = useMemo(() => ({ level, closeAll, closeSelf, activeIndex, setActiveIndex, registerItem, unregisterItem, itemRefs: itemRefsRef, menuRef, isOpen }), [level, closeAll, closeSelf, activeIndex, setActiveIndex, registerItem, unregisterItem, isOpen]); useEffect(() => { if (!isOpen) { nextIndexRef.current = 0; itemRefsRef.current = []; setActiveIndex(-1); } }, [isOpen, setActiveIndex]); useIsomorphicLayoutEffect(() => { if (isOpen) { whatInput.specificKeys([9, 37, 38, 39, 40, 33, 34, 35, 36]); } return () => { whatInput.specificKeys([9]); }; }, [isOpen]); const focusOnOpenElement = useCallback(() => { var _menuRef$current; return (_menuRef$current = menuRef.current) !== null && _menuRef$current !== void 0 ? _menuRef$current : null; }, []); const handleFocusComplete = 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 = useCallback(next => { setOpenState(next); }, [setOpenState]); const handleTriggerKeyDown = 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 _jsx(MenuTriggerContext, { value: { active, triggerProps: domProps, open: openFn, close: closeFn, toggle: toggleFn }, children: triggerChild }); } : undefined; return _jsx(Popover, { id: id, className: clsx('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 = _jsx(MenuContext, { value: contextValue, children: contentChildren })); } }); } withComponentMarkers(MenuRoot, { _supportsSpacingProps: true }); //# sourceMappingURL=MenuRoot.js.map