UNPKG

mui-nav-menu

Version:

Navigation menu component based on material ui

211 lines (210 loc) 8.92 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { alpha, ClickAwayListener, Collapse, List, Paper, Popper, useEventCallback, } from "@mui/material"; import classNames from "classnames"; import { useCallback, useMemo, useRef, useState, } from "react"; import { unstable_batchedUpdates } from "react-dom"; import { makeStyles } from "tss-react/mui"; import Arrow from "./Arrow"; import { useMenuContext } from "./contexts/MenuContext"; import PathContext, { useConnectedPath } from "./contexts/PathContext"; import ListMenuItem from "./ListMenuItem"; import { ConditionalWrapper } from "./utils"; const useStyle = makeStyles()((theme) => ({ popper: { "& .MuiListItemButton-root": { minWidth: 200, }, "&.NestedLevel-isRoot": { "& .MuiPaper-root": { marginLeft: 0, }, '&[data-popper-placement*="right"]': { "& .PopperArrow": { left: -5, }, "& .MuiPaper-root": { marginLeft: 10, }, }, '&[data-popper-placement*="left"]': { "& .PopperArrow": { right: -5, }, "& .MuiPaper-root": { marginRight: 10, }, }, '&[data-popper-placement*="top"]': { "& .PopperArrow": { bottom: -5, }, "& .MuiPaper-root": { marginBottom: 10, }, }, '&[data-popper-placement*="bottom"]': { "& .PopperArrow": { top: -5, }, "& .MuiPaper-root": { marginTop: 10, }, }, }, }, paper: { transformOrigin: "top left", position: "relative", zIndex: 1, marginLeft: 10, "& .PopperContent": { background: theme.palette.background.paper, overflowY: "auto", maxHeight: 500, padding: 5, borderRadius: 10, }, }, isSubMenuActive: { background: alpha("#000000", 0.05), }, })); const SubMenu = (props) => { const { openIds = [], selectedIds, checkSelectedFn, mode, collapsed, inlineIndent, onInternalOpenChange, onSelectedChange, popperClassName, TransitionComponent, onItemClick, focusedNodeId, elevation, arrow, defaultCollapseIcon, defaultExpandIcon, defaultPopupRootIcon, defaultPopupSubIcon, placement: placementProp, prefixCls, trigger, enterDelay, leaveDelay, } = useMenuContext(); const { classes } = useStyle(); const { id, children, ...rest } = props; const connectedIdPath = useConnectedPath(id); // const { transitioning, transitionHandlers } = useTransitionState(); const isInlineMode = mode == "inline"; const [arrowRef, setArrowRef] = useState(null); const subIsOpen = useMemo(() => { return openIds.includes(id); }, [openIds]); const delayTimerRef = useRef(); const isSubMenuActive = subIsOpen && mode != "inline"; const isSelected = useMemo(() => { if (checkSelectedFn) { return checkSelectedFn(props); } return selectedIds ? selectedIds.includes(id) : false; }, [selectedIds, checkSelectedFn]); const [menuItemElement, setMenuItemElement] = useState(null); const nestedLevel = useMemo(() => { return connectedIdPath.length; }, [connectedIdPath]); const isRoot = nestedLevel == 1; const setPopupVisible = useCallback((visible) => { clearDelayTimer(); onInternalOpenChange && onInternalOpenChange(id, visible, connectedIdPath); }, [connectedIdPath, onInternalOpenChange, openIds]); const delaySetPopupVisible = useCallback((value, delayTime) => { clearDelayTimer(); if (delayTime) { delayTimerRef.current = setTimeout(() => { setPopupVisible(value); clearDelayTimer(); }, delayTime); } else { setPopupVisible(value); } }, [openIds]); const toggleInlineSubVisible = useCallback(() => { onInternalOpenChange && onInternalOpenChange(id, !subIsOpen, connectedIdPath); }, [openIds, id]); const handleMouseEnter = (evt) => { if (isRoot && trigger == "click") { return; } delaySetPopupVisible(true, enterDelay); }; const handleMouseLeave = (evt) => { if (trigger == "click") { return; } if (children) { delaySetPopupVisible(false, leaveDelay); } }; const clearDelayTimer = () => { if (delayTimerRef.current) { clearTimeout(delayTimerRef.current); delayTimerRef.current = null; } }; const handleItemClick = useEventCallback((event) => { if (event.type !== "click") { return; } if (!children) { unstable_batchedUpdates(() => { onSelectedChange(connectedIdPath, true); }); } else { if (mode == "inline") { toggleInlineSubVisible(); } else if (trigger == "click" && isRoot) { setPopupVisible(!subIsOpen); } } onItemClick && onItemClick(id, connectedIdPath, event); }); const handleClickAway = () => { setPopupVisible(false); }; const placement = useMemo(() => { if (placementProp && isRoot) { return placementProp; } if (mode == "horizontal" && isRoot) { return "bottom-start"; } return "right-start"; }, [mode, isRoot, placementProp]); const modifiers = useMemo(() => { return [ { name: "flip", enabled: true, }, { name: "preventOverflow", enabled: true, options: { boundariesElement: "viewport", }, }, { name: "arrow", enabled: isRoot && arrow, options: { element: arrowRef, }, }, ]; }, [arrowRef, isRoot, arrow]); const setMenuItemElementRef = (ele) => { if (mode !== "inline") { setTimeout(() => { setMenuItemElement(ele); }, 200); } }; return (_jsxs("div", { className: classNames(prefixCls + "SubListItem-root", prefixCls + "ListItem-root"), ...(!isInlineMode && { style: { pointerEvents: rest.disabled ? "none" : undefined, }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, }), children: [_jsx(ListMenuItem, { id: id, hasChildren: !!children, ref: setMenuItemElementRef, onClick: handleItemClick, inlineIndent: inlineIndent, subIsOpen: subIsOpen, isSelected: isSelected, collapsed: collapsed, mode: mode, isFocused: focusedNodeId == id, connectedIdPath: connectedIdPath, expandIcon: defaultExpandIcon, collapseIcon: defaultCollapseIcon, popupRootIcon: defaultPopupRootIcon, popupSubIcon: defaultPopupSubIcon, className: classNames({ isSubMenuActive: isSubMenuActive, [classes.isSubMenuActive]: isSubMenuActive, }), ...rest }), children && isInlineMode && (_jsx(Collapse, { in: subIsOpen, timeout: "auto", unmountOnExit: true, children: _jsx(List, { className: prefixCls + "SubList-root", component: "div", disablePadding: true, children: _jsx(PathContext.Provider, { value: connectedIdPath, children: children }) }) })), children && !isInlineMode && (_jsx(Popper, { className: classNames(classes.popper, popperClassName, "NestedLevel-" + nestedLevel, { "NestedLevel-isRoot": nestedLevel == 1, }), transition: true, anchorEl: menuItemElement, placement: placement, open: subIsOpen && !!menuItemElement, modifiers: modifiers, children: ({ TransitionProps }) => (_jsx(ConditionalWrapper, { condition: isRoot && trigger == "click", wrapper: (child) => (_jsx(ClickAwayListener, { onClickAway: handleClickAway, children: _jsx("div", { children: child }) })), children: _jsx(TransitionComponent, { ...TransitionProps, children: _jsxs(Paper, { elevation: elevation, className: classes.paper, children: [_jsx("div", { className: "PopperContent", children: _jsx(List, { component: "div", disablePadding: true, children: _jsx(PathContext.Provider, { value: connectedIdPath, children: children }) }) }), isRoot && arrow && (_jsx(Arrow, { elevation: elevation, ref: setArrowRef }))] }) }) })) }))] })); }; export default SubMenu;