import { defineComponent, getCurrentInstance, ref, computed, nextTick, watch, watchEffect, provide, reactive, onMounted, h, withDirectives } from 'vue'; import { useResizeObserver } from '@vueuse/core'; import { isNil } from 'lodash-unified'; import { ElIcon } from '../../icon/index.mjs'; import { More } from '@element-plus/icons-vue'; import '../../../utils/index.mjs'; import '../../../hooks/index.mjs'; import '../../../directives/index.mjs'; import Menu$1 from './utils/menu-bar.mjs'; import ElMenuCollapseTransition from './menu-collapse-transition.mjs'; import SubMenu from './sub-menu.mjs'; import { useMenuCssVar } from './use-menu-css-var.mjs'; import { buildProps, definePropType } from '../../../utils/vue/props/runtime.mjs'; import { mutable } from '../../../utils/typescript.mjs'; import { iconPropType } from '../../../utils/vue/icon.mjs'; import { isString, isObject } from '@vue/shared'; import { useNamespace } from '../../../hooks/use-namespace/index.mjs'; import { flattedChildren } from '../../../utils/vue/vnode.mjs'; import ClickOutside from '../../../directives/click-outside/index.mjs'; const menuProps = buildProps({ mode: { type: String, values: ["horizontal", "vertical"], default: "vertical" }, defaultActive: { type: String, default: "" }, defaultOpeneds: { type: definePropType(Array), default: () => mutable([]) }, uniqueOpened: Boolean, router: Boolean, menuTrigger: { type: String, values: ["hover", "click"], default: "hover" }, collapse: Boolean, backgroundColor: String, textColor: String, activeTextColor: String, closeOnClickOutside: Boolean, collapseTransition: { type: Boolean, default: true }, ellipsis: { type: Boolean, default: true }, popperOffset: { type: Number, default: 6 }, ellipsisIcon: { type: iconPropType, default: () => More }, popperEffect: { type: definePropType(String), default: "dark" }, popperClass: String, showTimeout: { type: Number, default: 300 }, hideTimeout: { type: Number, default: 300 } }); const checkIndexPath = (indexPath) => Array.isArray(indexPath) && indexPath.every((path) => isString(path)); const menuEmits = { close: (index, indexPath) => isString(index) && checkIndexPath(indexPath), open: (index, indexPath) => isString(index) && checkIndexPath(indexPath), select: (index, indexPath, item, routerResult) => isString(index) && checkIndexPath(indexPath) && isObject(item) && (routerResult === void 0 || routerResult instanceof Promise) }; var Menu = defineComponent({ name: "ElMenu", props: menuProps, emits: menuEmits, setup(props, { emit, slots, expose }) { const instance = getCurrentInstance(); const router = instance.appContext.config.globalProperties.$router; const menu = ref(); const nsMenu = useNamespace("menu"); const nsSubMenu = useNamespace("sub-menu"); const sliceIndex = ref(-1); const openedMenus = ref(props.defaultOpeneds && !props.collapse ? props.defaultOpeneds.slice(0) : []); const activeIndex = ref(props.defaultActive); const items = ref({}); const subMenus = ref({}); const isMenuPopup = computed(() => { return props.mode === "horizontal" || props.mode === "vertical" && props.collapse; }); const initMenu = () => { const activeItem = activeIndex.value && items.value[activeIndex.value]; if (!activeItem || props.mode === "horizontal" || props.collapse) return; const indexPath = activeItem.indexPath; indexPath.forEach((index) => { const subMenu = subMenus.value[index]; subMenu && openMenu(index, subMenu.indexPath); }); }; const openMenu = (index, indexPath) => { if (openedMenus.value.includes(index)) return; if (props.uniqueOpened) { openedMenus.value = openedMenus.value.filter((index2) => indexPath.includes(index2)); } openedMenus.value.push(index); emit("open", index, indexPath); }; const close = (index) => { const i = openedMenus.value.indexOf(index); if (i !== -1) { openedMenus.value.splice(i, 1); } }; const closeMenu = (index, indexPath) => { close(index); emit("close", index, indexPath); }; const handleSubMenuClick = ({ index, indexPath }) => { const isOpened = openedMenus.value.includes(index); if (isOpened) { closeMenu(index, indexPath); } else { openMenu(index, indexPath); } }; const handleMenuItemClick = (menuItem) => { if (props.mode === "horizontal" || props.collapse) { openedMenus.value = []; } const { index, indexPath } = menuItem; if (isNil(index) || isNil(indexPath)) return; if (props.router && router) { const route = menuItem.route || index; const routerResult = router.push(route).then((res) => { if (!res) activeIndex.value = index; return res; }); emit("select", index, indexPath, { index, indexPath, route }, routerResult); } else { activeIndex.value = index; emit("select", index, indexPath, { index, indexPath }); } }; const updateActiveIndex = (val) => { const itemsInData = items.value; const item = itemsInData[val] || activeIndex.value && itemsInData[activeIndex.value] || itemsInData[props.defaultActive]; if (item) { activeIndex.value = item.index; } else { activeIndex.value = val; } }; const calcMenuItemWidth = (menuItem) => { const computedStyle = getComputedStyle(menuItem); const marginLeft = Number.parseInt(computedStyle.marginLeft, 10); const marginRight = Number.parseInt(computedStyle.marginRight, 10); return menuItem.offsetWidth + marginLeft + marginRight || 0; }; const calcSliceIndex = () => { var _a, _b; if (!menu.value) return -1; const items2 = Array.from((_b = (_a = menu.value) == null ? void 0 : _a.childNodes) != null ? _b : []).filter((item) => item.nodeName !== "#comment" && (item.nodeName !== "#text" || item.nodeValue)); const moreItemWidth = 64; const computedMenuStyle = getComputedStyle(menu.value); const paddingLeft = Number.parseInt(computedMenuStyle.paddingLeft, 10); const paddingRight = Number.parseInt(computedMenuStyle.paddingRight, 10); const menuWidth = menu.value.clientWidth - paddingLeft - paddingRight; let calcWidth = 0; let sliceIndex2 = 0; items2.forEach((item, index) => { calcWidth += calcMenuItemWidth(item); if (calcWidth <= menuWidth - moreItemWidth) { sliceIndex2 = index + 1; } }); return sliceIndex2 === items2.length ? -1 : sliceIndex2; }; const getIndexPath = (index) => subMenus.value[index].indexPath; const debounce = (fn, wait = 33.34) => { let timmer; return () => { timmer && clearTimeout(timmer); timmer = setTimeout(() => { fn(); }, wait); }; }; let isFirstTimeRender = true; const handleResize = () => { if (sliceIndex.value === calcSliceIndex()) return; const callback = () => { sliceIndex.value = -1; nextTick(() => { sliceIndex.value = calcSliceIndex(); }); }; isFirstTimeRender ? callback() : debounce(callback)(); isFirstTimeRender = false; }; watch(() => props.defaultActive, (currentActive) => { if (!items.value[currentActive]) { activeIndex.value = ""; } updateActiveIndex(currentActive); }); watch(() => props.collapse, (value) => { if (value) openedMenus.value = []; }); watch(items.value, initMenu); let resizeStopper; watchEffect(() => { if (props.mode === "horizontal" && props.ellipsis) resizeStopper = useResizeObserver(menu, handleResize).stop; else resizeStopper == null ? void 0 : resizeStopper(); }); const mouseInChild = ref(false); { const addSubMenu = (item) => { subMenus.value[item.index] = item; }; const removeSubMenu = (item) => { delete subMenus.value[item.index]; }; const addMenuItem = (item) => { items.value[item.index] = item; }; const removeMenuItem = (item) => { delete items.value[item.index]; }; provide("rootMenu", reactive({ props, openedMenus, items, subMenus, activeIndex, isMenuPopup, addMenuItem, removeMenuItem, addSubMenu, removeSubMenu, openMenu, closeMenu, handleMenuItemClick, handleSubMenuClick })); provide(`subMenu:${instance.uid}`, { addSubMenu, removeSubMenu, mouseInChild, level: 0 }); } onMounted(() => { if (props.mode === "horizontal") { new Menu$1(instance.vnode.el, nsMenu.namespace.value); } }); { const open = (index) => { const { indexPath } = subMenus.value[index]; indexPath.forEach((i) => openMenu(i, indexPath)); }; expose({ open, close, handleResize }); } const ulStyle = useMenuCssVar(props, 0); return () => { var _a, _b; let slot = (_b = (_a = slots.default) == null ? void 0 : _a.call(slots)) != null ? _b : []; const vShowMore = []; if (props.mode === "horizontal" && menu.value) { const originalSlot = flattedChildren(slot); const slotDefault = sliceIndex.value === -1 ? originalSlot : originalSlot.slice(0, sliceIndex.value); const slotMore = sliceIndex.value === -1 ? [] : originalSlot.slice(sliceIndex.value); if ((slotMore == null ? void 0 : slotMore.length) && props.ellipsis) { slot = slotDefault; vShowMore.push(h(SubMenu, { index: "sub-menu-more", class: nsSubMenu.e("hide-arrow"), popperOffset: props.popperOffset }, { title: () => h(ElIcon, { class: nsSubMenu.e("icon-more") }, { default: () => h(props.ellipsisIcon) }), default: () => slotMore })); } } const directives = props.closeOnClickOutside ? [ [ ClickOutside, () => { if (!openedMenus.value.length) return; if (!mouseInChild.value) { openedMenus.value.forEach((openedMenu) => emit("close", openedMenu, getIndexPath(openedMenu))); openedMenus.value = []; } } ] ] : []; const vMenu = withDirectives(h("ul", { key: String(props.collapse), role: "menubar", ref: menu, style: ulStyle.value, class: { [nsMenu.b()]: true, [nsMenu.m(props.mode)]: true, [nsMenu.m("collapse")]: props.collapse } }, [...slot, ...vShowMore]), directives); if (props.collapseTransition && props.mode === "vertical") { return h(ElMenuCollapseTransition, () => vMenu); } return vMenu; }; } }); export { Menu as default, menuEmits, menuProps }; //# sourceMappingURL=menu.mjs.map