| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- import { isArray, isObject, isString, isUndefined as isUndefined$1 } from "../../../utils/types.mjs";
- import { buildProps, definePropType } from "../../../utils/vue/props/runtime.mjs";
- import { iconPropType } from "../../../utils/vue/icon.mjs";
- import { flattedChildren } from "../../../utils/vue/vnode.mjs";
- import { mutable } from "../../../utils/typescript.mjs";
- import { useNamespace } from "../../../hooks/use-namespace/index.mjs";
- import { ElIcon } from "../../icon/index.mjs";
- import ClickOutside from "../../../directives/click-outside/index.mjs";
- import Menu from "./utils/menu-bar.mjs";
- import menu_collapse_transition_default from "./menu-collapse-transition.mjs";
- import { useMenuCssVar } from "./use-menu-css-var.mjs";
- import { MENU_INJECTION_KEY, SUB_MENU_INJECTION_KEY } from "./tokens.mjs";
- import sub_menu_default from "./sub-menu.mjs";
- import { unrefElement, useResizeObserver } from "@vueuse/core";
- import { isNil } from "lodash-unified";
- import { More } from "@element-plus/icons-vue";
- import { computed, defineComponent, getCurrentInstance, h, nextTick, onMounted, provide, reactive, ref, watch, watchEffect, withDirectives } from "vue";
- //#region ../../packages/components/menu/src/menu.ts
- const menuProps = buildProps({
- /**
- * @description menu display mode
- */
- mode: {
- type: String,
- values: ["horizontal", "vertical"],
- default: "vertical"
- },
- /**
- * @description index of active menu on page load
- */
- defaultActive: {
- type: String,
- default: ""
- },
- /**
- * @description array that contains indexes of currently active sub-menus
- */
- defaultOpeneds: {
- type: definePropType(Array),
- default: () => mutable([])
- },
- /**
- * @description whether only one sub-menu can be active
- */
- uniqueOpened: Boolean,
- /**
- * @description whether `vue-router` mode is activated. If true, index will be used as 'path' to activate the route action. Use with `default-active` to set the active item on load.
- */
- router: Boolean,
- /**
- * @description how sub-menus are triggered, only works when `mode` is 'horizontal'
- */
- menuTrigger: {
- type: String,
- values: ["hover", "click"],
- default: "hover"
- },
- /**
- * @description whether the menu is collapsed (available only in vertical mode)
- */
- collapse: Boolean,
- /**
- * @description background color of Menu (hex format) (deprecated, use `--bg-color` instead)
- * @deprecated use `--bg-color` instead
- */
- backgroundColor: String,
- /**
- * @description text color of Menu (hex format) (deprecated, use `--text-color` instead)
- * @deprecated use `--text-color` instead
- */
- textColor: String,
- /**
- * @description text color of currently active menu item (hex format) (deprecated, use `--active-color` instead)
- * @deprecated use `--active-color` instead
- */
- activeTextColor: String,
- /**
- * @description optional, whether menu is collapsed when clicking outside
- */
- closeOnClickOutside: Boolean,
- /**
- * @description whether to enable the collapse transition
- */
- collapseTransition: {
- type: Boolean,
- default: true
- },
- /**
- * @description whether the menu is ellipsis (available only in horizontal mode)
- */
- ellipsis: {
- type: Boolean,
- default: true
- },
- /**
- * @description offset of the popper (effective for all submenus)
- */
- popperOffset: {
- type: Number,
- default: 6
- },
- /**
- * @description custom ellipsis icon (available only in horizontal mode and ellipsis is true)
- */
- ellipsisIcon: {
- type: iconPropType,
- default: () => More
- },
- /**
- * @description Tooltip theme, built-in theme: `dark` / `light` when menu is collapsed
- */
- popperEffect: {
- type: definePropType(String),
- default: "dark"
- },
- /**
- * @description custom class name for all popup menus
- */
- popperClass: String,
- /**
- * @description custom style for all popup menus
- */
- popperStyle: { type: definePropType([String, Object]) },
- /**
- * @description control timeout for all menus before showing
- */
- showTimeout: {
- type: Number,
- default: 300
- },
- /**
- * @description control timeout for all menus before hiding
- */
- hideTimeout: {
- type: Number,
- default: 300
- },
- /**
- * @description when menu inactive and `persistent` is `false` , dropdown menu will be destroyed
- */
- persistent: {
- type: Boolean,
- default: true
- }
- });
- const checkIndexPath = (indexPath) => 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) && (isUndefined$1(routerResult) || routerResult instanceof Promise)
- };
- const DEFAULT_MORE_ITEM_WIDTH = 64;
- var menu_default = 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 subMenu = ref();
- const nsMenu = useNamespace("menu");
- const nsSubMenu = useNamespace("sub-menu");
- let moreItemWidth = DEFAULT_MORE_ITEM_WIDTH;
- 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(() => 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;
- activeItem.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((index) => indexPath.includes(index));
- 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 }) => {
- openedMenus.value.includes(index) ? closeMenu(index, indexPath) : 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;
- activeIndex.value = (itemsInData[val] || activeIndex.value && itemsInData[activeIndex.value] || itemsInData[props.defaultActive])?.index ?? 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 = () => {
- if (!menu.value) return -1;
- const items = Array.from(menu.value.childNodes).filter((item) => item.nodeName !== "#comment" && (item.nodeName !== "#text" || item.nodeValue));
- 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 sliceIndex = 0;
- items.forEach((item, index) => {
- calcWidth += calcMenuItemWidth(item);
- if (calcWidth <= menuWidth - moreItemWidth) sliceIndex = index + 1;
- });
- return sliceIndex === items.length ? -1 : sliceIndex;
- };
- const getIndexPath = (index) => subMenus.value[index].indexPath;
- const debounce = (fn, wait = 33.34) => {
- let timer;
- return () => {
- timer && clearTimeout(timer);
- timer = setTimeout(() => {
- fn();
- }, wait);
- };
- };
- let isFirstTimeRender = true;
- const handleResize = () => {
- const el = unrefElement(subMenu);
- if (el) moreItemWidth = calcMenuItemWidth(el) || DEFAULT_MORE_ITEM_WIDTH;
- 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?.();
- });
- 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(MENU_INJECTION_KEY, reactive({
- props,
- openedMenus,
- items,
- subMenus,
- activeIndex,
- isMenuPopup,
- addMenuItem,
- removeMenuItem,
- addSubMenu,
- removeSubMenu,
- openMenu,
- closeMenu,
- handleMenuItemClick,
- handleSubMenuClick
- }));
- provide(`${SUB_MENU_INJECTION_KEY}${instance.uid}`, {
- addSubMenu,
- removeSubMenu,
- mouseInChild,
- level: 0
- });
- }
- onMounted(() => {
- if (props.mode === "horizontal") new Menu(instance.vnode.el, nsMenu.namespace.value);
- });
- {
- const open = (index) => {
- const { indexPath } = subMenus.value[index];
- indexPath.forEach((i) => openMenu(i, indexPath));
- };
- expose({
- open,
- close,
- updateActiveIndex,
- handleResize
- });
- }
- const ulStyle = useMenuCssVar(props, 0);
- return () => {
- let slot = slots.default?.() ?? [];
- const vShowMore = [];
- if (props.mode === "horizontal" && menu.value) {
- const originalSlot = flattedChildren(slot).filter((vnode) => {
- return vnode?.shapeFlag !== 8;
- });
- const slotDefault = sliceIndex.value === -1 ? originalSlot : originalSlot.slice(0, sliceIndex.value);
- const slotMore = sliceIndex.value === -1 ? [] : originalSlot.slice(sliceIndex.value);
- if (slotMore?.length && props.ellipsis) {
- slot = slotDefault;
- vShowMore.push(h(sub_menu_default, {
- ref: 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(menu_collapse_transition_default, () => vMenu);
- return vMenu;
- };
- }
- });
- //#endregion
- export { menu_default as default, menuEmits, menuProps };
- //# sourceMappingURL=menu.mjs.map
|