import _extends from "@babel/runtime/helpers/esm/extends"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; var _excluded = ["prefixCls", "style", "className", "tabIndex", "children", "direction", "id", "mode", "inlineCollapsed", "disabled", "disabledOverflow", "subMenuOpenDelay", "subMenuCloseDelay", "forceSubMenuRender", "defaultOpenKeys", "openKeys", "activeKey", "defaultActiveFirst", "selectable", "multiple", "defaultSelectedKeys", "selectedKeys", "onSelect", "onDeselect", "inlineIndent", "motion", "defaultMotions", "triggerSubMenuAction", "builtinPlacements", "itemIcon", "expandIcon", "overflowedIndicator", "overflowedIndicatorPopupClassName", "getPopupContainer", "onClick", "onOpenChange", "onKeyDown", "openAnimation", "openTransitionName", "_internalRenderMenuItem", "_internalRenderSubMenuItem"]; import * as React from 'react'; import classNames from 'classnames'; import shallowEqual from 'shallowequal'; import useMergedState from "rc-util/es/hooks/useMergedState"; import warning from "rc-util/es/warning"; import Overflow from 'rc-overflow'; import MenuItem from './MenuItem'; import { parseChildren } from './utils/nodeUtil'; import MenuContextProvider from './context/MenuContext'; import useMemoCallback from './hooks/useMemoCallback'; import { warnItemProp } from './utils/warnUtil'; import SubMenu from './SubMenu'; import useAccessibility from './hooks/useAccessibility'; import useUUID from './hooks/useUUID'; import { PathRegisterContext, PathUserContext } from './context/PathContext'; import useKeyRecords, { OVERFLOW_KEY } from './hooks/useKeyRecords'; import { IdContext } from './context/IdContext'; import PrivateContext from './context/PrivateContext'; /** * Menu modify after refactor: * ## Add * - disabled * * ## Remove * - openTransitionName * - openAnimation * - onDestroy * - siderCollapsed: Seems antd do not use this prop (Need test in antd) * - collapsedWidth: Seems this logic should be handle by antd Layout.Sider */ // optimize for render var EMPTY_LIST = []; var Menu = function Menu(props) { var _childList$, _classNames; var _props$prefixCls = props.prefixCls, prefixCls = _props$prefixCls === void 0 ? 'rc-menu' : _props$prefixCls, style = props.style, className = props.className, _props$tabIndex = props.tabIndex, tabIndex = _props$tabIndex === void 0 ? 0 : _props$tabIndex, children = props.children, direction = props.direction, id = props.id, _props$mode = props.mode, mode = _props$mode === void 0 ? 'vertical' : _props$mode, inlineCollapsed = props.inlineCollapsed, disabled = props.disabled, disabledOverflow = props.disabledOverflow, _props$subMenuOpenDel = props.subMenuOpenDelay, subMenuOpenDelay = _props$subMenuOpenDel === void 0 ? 0.1 : _props$subMenuOpenDel, _props$subMenuCloseDe = props.subMenuCloseDelay, subMenuCloseDelay = _props$subMenuCloseDe === void 0 ? 0.1 : _props$subMenuCloseDe, forceSubMenuRender = props.forceSubMenuRender, defaultOpenKeys = props.defaultOpenKeys, openKeys = props.openKeys, activeKey = props.activeKey, defaultActiveFirst = props.defaultActiveFirst, _props$selectable = props.selectable, selectable = _props$selectable === void 0 ? true : _props$selectable, _props$multiple = props.multiple, multiple = _props$multiple === void 0 ? false : _props$multiple, defaultSelectedKeys = props.defaultSelectedKeys, selectedKeys = props.selectedKeys, onSelect = props.onSelect, onDeselect = props.onDeselect, _props$inlineIndent = props.inlineIndent, inlineIndent = _props$inlineIndent === void 0 ? 24 : _props$inlineIndent, motion = props.motion, defaultMotions = props.defaultMotions, _props$triggerSubMenu = props.triggerSubMenuAction, triggerSubMenuAction = _props$triggerSubMenu === void 0 ? 'hover' : _props$triggerSubMenu, builtinPlacements = props.builtinPlacements, itemIcon = props.itemIcon, expandIcon = props.expandIcon, _props$overflowedIndi = props.overflowedIndicator, overflowedIndicator = _props$overflowedIndi === void 0 ? '...' : _props$overflowedIndi, overflowedIndicatorPopupClassName = props.overflowedIndicatorPopupClassName, getPopupContainer = props.getPopupContainer, onClick = props.onClick, onOpenChange = props.onOpenChange, onKeyDown = props.onKeyDown, openAnimation = props.openAnimation, openTransitionName = props.openTransitionName, _internalRenderMenuItem = props._internalRenderMenuItem, _internalRenderSubMenuItem = props._internalRenderSubMenuItem, restProps = _objectWithoutProperties(props, _excluded); var childList = parseChildren(children, EMPTY_LIST); var _React$useState = React.useState(false), _React$useState2 = _slicedToArray(_React$useState, 2), mounted = _React$useState2[0], setMounted = _React$useState2[1]; var containerRef = React.useRef(); var uuid = useUUID(id); var isRtl = direction === 'rtl'; // ========================= Warn ========================= if (process.env.NODE_ENV !== 'production') { warning(!openAnimation && !openTransitionName, '`openAnimation` and `openTransitionName` is removed. Please use `motion` or `defaultMotion` instead.'); } // ========================= Mode ========================= var _React$useMemo = React.useMemo(function () { if ((mode === 'inline' || mode === 'vertical') && inlineCollapsed) { return ['vertical', inlineCollapsed]; } return [mode, false]; }, [mode, inlineCollapsed]), _React$useMemo2 = _slicedToArray(_React$useMemo, 2), mergedMode = _React$useMemo2[0], mergedInlineCollapsed = _React$useMemo2[1]; // ====================== Responsive ====================== var _React$useState3 = React.useState(0), _React$useState4 = _slicedToArray(_React$useState3, 2), lastVisibleIndex = _React$useState4[0], setLastVisibleIndex = _React$useState4[1]; var allVisible = lastVisibleIndex >= childList.length - 1 || mergedMode !== 'horizontal' || disabledOverflow; // ========================= Open ========================= var _useMergedState = useMergedState(defaultOpenKeys, { value: openKeys, postState: function postState(keys) { return keys || EMPTY_LIST; } }), _useMergedState2 = _slicedToArray(_useMergedState, 2), mergedOpenKeys = _useMergedState2[0], setMergedOpenKeys = _useMergedState2[1]; var triggerOpenKeys = function triggerOpenKeys(keys) { setMergedOpenKeys(keys); onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(keys); }; // >>>>> Cache & Reset open keys when inlineCollapsed changed var _React$useState5 = React.useState(mergedOpenKeys), _React$useState6 = _slicedToArray(_React$useState5, 2), inlineCacheOpenKeys = _React$useState6[0], setInlineCacheOpenKeys = _React$useState6[1]; var isInlineMode = mergedMode === 'inline'; var mountRef = React.useRef(false); // Cache React.useEffect(function () { if (isInlineMode) { setInlineCacheOpenKeys(mergedOpenKeys); } }, [mergedOpenKeys]); // Restore React.useEffect(function () { if (!mountRef.current) { mountRef.current = true; return; } if (isInlineMode) { setMergedOpenKeys(inlineCacheOpenKeys); } else { // Trigger open event in case its in control triggerOpenKeys(EMPTY_LIST); } }, [isInlineMode]); // ========================= Path ========================= var _useKeyRecords = useKeyRecords(), registerPath = _useKeyRecords.registerPath, unregisterPath = _useKeyRecords.unregisterPath, refreshOverflowKeys = _useKeyRecords.refreshOverflowKeys, isSubPathKey = _useKeyRecords.isSubPathKey, getKeyPath = _useKeyRecords.getKeyPath, getKeys = _useKeyRecords.getKeys, getSubPathKeys = _useKeyRecords.getSubPathKeys; var registerPathContext = React.useMemo(function () { return { registerPath: registerPath, unregisterPath: unregisterPath }; }, [registerPath, unregisterPath]); var pathUserContext = React.useMemo(function () { return { isSubPathKey: isSubPathKey }; }, [isSubPathKey]); React.useEffect(function () { refreshOverflowKeys(allVisible ? EMPTY_LIST : childList.slice(lastVisibleIndex + 1).map(function (child) { return child.key; })); }, [lastVisibleIndex, allVisible]); // ======================== Active ======================== var _useMergedState3 = useMergedState(activeKey || defaultActiveFirst && ((_childList$ = childList[0]) === null || _childList$ === void 0 ? void 0 : _childList$.key), { value: activeKey }), _useMergedState4 = _slicedToArray(_useMergedState3, 2), mergedActiveKey = _useMergedState4[0], setMergedActiveKey = _useMergedState4[1]; var onActive = useMemoCallback(function (key) { setMergedActiveKey(key); }); var onInactive = useMemoCallback(function () { setMergedActiveKey(undefined); }); // ======================== Select ======================== // >>>>> Select keys var _useMergedState5 = useMergedState(defaultSelectedKeys || [], { value: selectedKeys, // Legacy convert key to array postState: function postState(keys) { if (Array.isArray(keys)) { return keys; } if (keys === null || keys === undefined) { return EMPTY_LIST; } return [keys]; } }), _useMergedState6 = _slicedToArray(_useMergedState5, 2), mergedSelectKeys = _useMergedState6[0], setMergedSelectKeys = _useMergedState6[1]; // >>>>> Trigger select var triggerSelection = function triggerSelection(info) { if (selectable) { // Insert or Remove var targetKey = info.key; var exist = mergedSelectKeys.includes(targetKey); var newSelectKeys; if (multiple) { if (exist) { newSelectKeys = mergedSelectKeys.filter(function (key) { return key !== targetKey; }); } else { newSelectKeys = [].concat(_toConsumableArray(mergedSelectKeys), [targetKey]); } } else { newSelectKeys = [targetKey]; } setMergedSelectKeys(newSelectKeys); // Trigger event var selectInfo = _objectSpread(_objectSpread({}, info), {}, { selectedKeys: newSelectKeys }); if (exist) { onDeselect === null || onDeselect === void 0 ? void 0 : onDeselect(selectInfo); } else { onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectInfo); } } // Whatever selectable, always close it if (!multiple && mergedOpenKeys.length && mergedMode !== 'inline') { triggerOpenKeys(EMPTY_LIST); } }; // ========================= Open ========================= /** * Click for item. SubMenu do not have selection status */ var onInternalClick = useMemoCallback(function (info) { onClick === null || onClick === void 0 ? void 0 : onClick(warnItemProp(info)); triggerSelection(info); }); var onInternalOpenChange = useMemoCallback(function (key, open) { var newOpenKeys = mergedOpenKeys.filter(function (k) { return k !== key; }); if (open) { newOpenKeys.push(key); } else if (mergedMode !== 'inline') { // We need find all related popup to close var subPathKeys = getSubPathKeys(key); newOpenKeys = newOpenKeys.filter(function (k) { return !subPathKeys.has(k); }); } if (!shallowEqual(mergedOpenKeys, newOpenKeys)) { triggerOpenKeys(newOpenKeys); } }); var getInternalPopupContainer = useMemoCallback(getPopupContainer); // ==================== Accessibility ===================== var triggerAccessibilityOpen = function triggerAccessibilityOpen(key, open) { var nextOpen = open !== null && open !== void 0 ? open : !mergedOpenKeys.includes(key); onInternalOpenChange(key, nextOpen); }; var onInternalKeyDown = useAccessibility(mergedMode, mergedActiveKey, isRtl, uuid, containerRef, getKeys, getKeyPath, setMergedActiveKey, triggerAccessibilityOpen, onKeyDown); // ======================== Effect ======================== React.useEffect(function () { setMounted(true); }, []); // ======================= Context ======================== var privateContext = React.useMemo(function () { return { _internalRenderMenuItem: _internalRenderMenuItem, _internalRenderSubMenuItem: _internalRenderSubMenuItem }; }, [_internalRenderMenuItem, _internalRenderSubMenuItem]); // ======================== Render ======================== // >>>>> Children var wrappedChildList = mergedMode !== 'horizontal' || disabledOverflow ? childList : // Need wrap for overflow dropdown that do not response for open childList.map(function (child, index) { return ( /*#__PURE__*/ // Always wrap provider to avoid sub node re-mount React.createElement(MenuContextProvider, { key: child.key, overflowDisabled: index > lastVisibleIndex }, child) ); }); // >>>>> Container var container = /*#__PURE__*/React.createElement(Overflow, _extends({ id: id, ref: containerRef, prefixCls: "".concat(prefixCls, "-overflow"), component: "ul", itemComponent: MenuItem, className: classNames(prefixCls, "".concat(prefixCls, "-root"), "".concat(prefixCls, "-").concat(mergedMode), className, (_classNames = {}, _defineProperty(_classNames, "".concat(prefixCls, "-inline-collapsed"), mergedInlineCollapsed), _defineProperty(_classNames, "".concat(prefixCls, "-rtl"), isRtl), _classNames)), dir: direction, style: style, role: "menu", tabIndex: tabIndex, data: wrappedChildList, renderRawItem: function renderRawItem(node) { return node; }, renderRawRest: function renderRawRest(omitItems) { // We use origin list since wrapped list use context to prevent open var len = omitItems.length; var originOmitItems = len ? childList.slice(-len) : null; return /*#__PURE__*/React.createElement(SubMenu, { eventKey: OVERFLOW_KEY, title: overflowedIndicator, disabled: allVisible, internalPopupClose: len === 0, popupClassName: overflowedIndicatorPopupClassName }, originOmitItems); }, maxCount: mergedMode !== 'horizontal' || disabledOverflow ? Overflow.INVALIDATE : Overflow.RESPONSIVE, ssr: "full", "data-menu-list": true, onVisibleChange: function onVisibleChange(newLastIndex) { setLastVisibleIndex(newLastIndex); }, onKeyDown: onInternalKeyDown }, restProps)); // >>>>> Render return /*#__PURE__*/React.createElement(PrivateContext.Provider, { value: privateContext }, /*#__PURE__*/React.createElement(IdContext.Provider, { value: uuid }, /*#__PURE__*/React.createElement(MenuContextProvider, { prefixCls: prefixCls, mode: mergedMode, openKeys: mergedOpenKeys, rtl: isRtl // Disabled , disabled: disabled // Motion , motion: mounted ? motion : null, defaultMotions: mounted ? defaultMotions : null // Active , activeKey: mergedActiveKey, onActive: onActive, onInactive: onInactive // Selection , selectedKeys: mergedSelectKeys // Level , inlineIndent: inlineIndent // Popup , subMenuOpenDelay: subMenuOpenDelay, subMenuCloseDelay: subMenuCloseDelay, forceSubMenuRender: forceSubMenuRender, builtinPlacements: builtinPlacements, triggerSubMenuAction: triggerSubMenuAction, getPopupContainer: getInternalPopupContainer // Icon , itemIcon: itemIcon, expandIcon: expandIcon // Events , onItemClick: onInternalClick, onOpenChange: onInternalOpenChange }, /*#__PURE__*/React.createElement(PathUserContext.Provider, { value: pathUserContext }, container), /*#__PURE__*/React.createElement("div", { style: { display: 'none' }, "aria-hidden": true }, /*#__PURE__*/React.createElement(PathRegisterContext.Provider, { value: registerPathContext }, childList))))); }; export default Menu;