menu.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var vue = require('vue');
  4. var core = require('@vueuse/core');
  5. var lodashUnified = require('lodash-unified');
  6. var index$1 = require('../../icon/index.js');
  7. var iconsVue = require('@element-plus/icons-vue');
  8. var menuBar = require('./utils/menu-bar.js');
  9. var menuCollapseTransition = require('./menu-collapse-transition.js');
  10. var subMenu = require('./sub-menu.js');
  11. var useMenuCssVar = require('./use-menu-css-var.js');
  12. var index$2 = require('../../../directives/click-outside/index.js');
  13. var runtime = require('../../../utils/vue/props/runtime.js');
  14. var typescript = require('../../../utils/typescript.js');
  15. var icon = require('../../../utils/vue/icon.js');
  16. var index = require('../../../hooks/use-namespace/index.js');
  17. var vnode = require('../../../utils/vue/vnode.js');
  18. var shared = require('@vue/shared');
  19. var types = require('../../../utils/types.js');
  20. const menuProps = runtime.buildProps({
  21. mode: {
  22. type: String,
  23. values: ["horizontal", "vertical"],
  24. default: "vertical"
  25. },
  26. defaultActive: {
  27. type: String,
  28. default: ""
  29. },
  30. defaultOpeneds: {
  31. type: runtime.definePropType(Array),
  32. default: () => typescript.mutable([])
  33. },
  34. uniqueOpened: Boolean,
  35. router: Boolean,
  36. menuTrigger: {
  37. type: String,
  38. values: ["hover", "click"],
  39. default: "hover"
  40. },
  41. collapse: Boolean,
  42. backgroundColor: String,
  43. textColor: String,
  44. activeTextColor: String,
  45. closeOnClickOutside: Boolean,
  46. collapseTransition: {
  47. type: Boolean,
  48. default: true
  49. },
  50. ellipsis: {
  51. type: Boolean,
  52. default: true
  53. },
  54. popperOffset: {
  55. type: Number,
  56. default: 6
  57. },
  58. ellipsisIcon: {
  59. type: icon.iconPropType,
  60. default: () => iconsVue.More
  61. },
  62. popperEffect: {
  63. type: runtime.definePropType(String),
  64. default: "dark"
  65. },
  66. popperClass: String,
  67. showTimeout: {
  68. type: Number,
  69. default: 300
  70. },
  71. hideTimeout: {
  72. type: Number,
  73. default: 300
  74. },
  75. persistent: {
  76. type: Boolean,
  77. default: true
  78. }
  79. });
  80. const checkIndexPath = (indexPath) => shared.isArray(indexPath) && indexPath.every((path) => shared.isString(path));
  81. const menuEmits = {
  82. close: (index, indexPath) => shared.isString(index) && checkIndexPath(indexPath),
  83. open: (index, indexPath) => shared.isString(index) && checkIndexPath(indexPath),
  84. select: (index, indexPath, item, routerResult) => shared.isString(index) && checkIndexPath(indexPath) && shared.isObject(item) && (types.isUndefined(routerResult) || routerResult instanceof Promise)
  85. };
  86. var Menu = vue.defineComponent({
  87. name: "ElMenu",
  88. props: menuProps,
  89. emits: menuEmits,
  90. setup(props, { emit, slots, expose }) {
  91. const instance = vue.getCurrentInstance();
  92. const router = instance.appContext.config.globalProperties.$router;
  93. const menu = vue.ref();
  94. const nsMenu = index.useNamespace("menu");
  95. const nsSubMenu = index.useNamespace("sub-menu");
  96. const sliceIndex = vue.ref(-1);
  97. const openedMenus = vue.ref(props.defaultOpeneds && !props.collapse ? props.defaultOpeneds.slice(0) : []);
  98. const activeIndex = vue.ref(props.defaultActive);
  99. const items = vue.ref({});
  100. const subMenus = vue.ref({});
  101. const isMenuPopup = vue.computed(() => props.mode === "horizontal" || props.mode === "vertical" && props.collapse);
  102. const initMenu = () => {
  103. const activeItem = activeIndex.value && items.value[activeIndex.value];
  104. if (!activeItem || props.mode === "horizontal" || props.collapse)
  105. return;
  106. const indexPath = activeItem.indexPath;
  107. indexPath.forEach((index) => {
  108. const subMenu = subMenus.value[index];
  109. subMenu && openMenu(index, subMenu.indexPath);
  110. });
  111. };
  112. const openMenu = (index, indexPath) => {
  113. if (openedMenus.value.includes(index))
  114. return;
  115. if (props.uniqueOpened) {
  116. openedMenus.value = openedMenus.value.filter((index2) => indexPath.includes(index2));
  117. }
  118. openedMenus.value.push(index);
  119. emit("open", index, indexPath);
  120. };
  121. const close = (index) => {
  122. const i = openedMenus.value.indexOf(index);
  123. if (i !== -1) {
  124. openedMenus.value.splice(i, 1);
  125. }
  126. };
  127. const closeMenu = (index, indexPath) => {
  128. close(index);
  129. emit("close", index, indexPath);
  130. };
  131. const handleSubMenuClick = ({
  132. index,
  133. indexPath
  134. }) => {
  135. const isOpened = openedMenus.value.includes(index);
  136. isOpened ? closeMenu(index, indexPath) : openMenu(index, indexPath);
  137. };
  138. const handleMenuItemClick = (menuItem) => {
  139. if (props.mode === "horizontal" || props.collapse) {
  140. openedMenus.value = [];
  141. }
  142. const { index, indexPath } = menuItem;
  143. if (lodashUnified.isNil(index) || lodashUnified.isNil(indexPath))
  144. return;
  145. if (props.router && router) {
  146. const route = menuItem.route || index;
  147. const routerResult = router.push(route).then((res) => {
  148. if (!res)
  149. activeIndex.value = index;
  150. return res;
  151. });
  152. emit("select", index, indexPath, { index, indexPath, route }, routerResult);
  153. } else {
  154. activeIndex.value = index;
  155. emit("select", index, indexPath, { index, indexPath });
  156. }
  157. };
  158. const updateActiveIndex = (val) => {
  159. var _a;
  160. const itemsInData = items.value;
  161. const item = itemsInData[val] || activeIndex.value && itemsInData[activeIndex.value] || itemsInData[props.defaultActive];
  162. activeIndex.value = (_a = item == null ? void 0 : item.index) != null ? _a : val;
  163. };
  164. const calcMenuItemWidth = (menuItem) => {
  165. const computedStyle = getComputedStyle(menuItem);
  166. const marginLeft = Number.parseInt(computedStyle.marginLeft, 10);
  167. const marginRight = Number.parseInt(computedStyle.marginRight, 10);
  168. return menuItem.offsetWidth + marginLeft + marginRight || 0;
  169. };
  170. const calcSliceIndex = () => {
  171. var _a, _b;
  172. if (!menu.value)
  173. return -1;
  174. const items2 = Array.from((_b = (_a = menu.value) == null ? void 0 : _a.childNodes) != null ? _b : []).filter((item) => item.nodeName !== "#text" || item.nodeValue);
  175. const moreItemWidth = 64;
  176. const computedMenuStyle = getComputedStyle(menu.value);
  177. const paddingLeft = Number.parseInt(computedMenuStyle.paddingLeft, 10);
  178. const paddingRight = Number.parseInt(computedMenuStyle.paddingRight, 10);
  179. const menuWidth = menu.value.clientWidth - paddingLeft - paddingRight;
  180. let calcWidth = 0;
  181. let sliceIndex2 = 0;
  182. items2.forEach((item, index) => {
  183. if (item.nodeName === "#comment")
  184. return;
  185. calcWidth += calcMenuItemWidth(item);
  186. if (calcWidth <= menuWidth - moreItemWidth) {
  187. sliceIndex2 = index + 1;
  188. }
  189. });
  190. return sliceIndex2 === items2.length ? -1 : sliceIndex2;
  191. };
  192. const getIndexPath = (index) => subMenus.value[index].indexPath;
  193. const debounce = (fn, wait = 33.34) => {
  194. let timmer;
  195. return () => {
  196. timmer && clearTimeout(timmer);
  197. timmer = setTimeout(() => {
  198. fn();
  199. }, wait);
  200. };
  201. };
  202. let isFirstTimeRender = true;
  203. const handleResize = () => {
  204. if (sliceIndex.value === calcSliceIndex())
  205. return;
  206. const callback = () => {
  207. sliceIndex.value = -1;
  208. vue.nextTick(() => {
  209. sliceIndex.value = calcSliceIndex();
  210. });
  211. };
  212. isFirstTimeRender ? callback() : debounce(callback)();
  213. isFirstTimeRender = false;
  214. };
  215. vue.watch(() => props.defaultActive, (currentActive) => {
  216. if (!items.value[currentActive]) {
  217. activeIndex.value = "";
  218. }
  219. updateActiveIndex(currentActive);
  220. });
  221. vue.watch(() => props.collapse, (value) => {
  222. if (value)
  223. openedMenus.value = [];
  224. });
  225. vue.watch(items.value, initMenu);
  226. let resizeStopper;
  227. vue.watchEffect(() => {
  228. if (props.mode === "horizontal" && props.ellipsis)
  229. resizeStopper = core.useResizeObserver(menu, handleResize).stop;
  230. else
  231. resizeStopper == null ? void 0 : resizeStopper();
  232. });
  233. const mouseInChild = vue.ref(false);
  234. {
  235. const addSubMenu = (item) => {
  236. subMenus.value[item.index] = item;
  237. };
  238. const removeSubMenu = (item) => {
  239. delete subMenus.value[item.index];
  240. };
  241. const addMenuItem = (item) => {
  242. items.value[item.index] = item;
  243. };
  244. const removeMenuItem = (item) => {
  245. delete items.value[item.index];
  246. };
  247. vue.provide("rootMenu", vue.reactive({
  248. props,
  249. openedMenus,
  250. items,
  251. subMenus,
  252. activeIndex,
  253. isMenuPopup,
  254. addMenuItem,
  255. removeMenuItem,
  256. addSubMenu,
  257. removeSubMenu,
  258. openMenu,
  259. closeMenu,
  260. handleMenuItemClick,
  261. handleSubMenuClick
  262. }));
  263. vue.provide(`subMenu:${instance.uid}`, {
  264. addSubMenu,
  265. removeSubMenu,
  266. mouseInChild,
  267. level: 0
  268. });
  269. }
  270. vue.onMounted(() => {
  271. if (props.mode === "horizontal") {
  272. new menuBar["default"](instance.vnode.el, nsMenu.namespace.value);
  273. }
  274. });
  275. {
  276. const open = (index) => {
  277. const { indexPath } = subMenus.value[index];
  278. indexPath.forEach((i) => openMenu(i, indexPath));
  279. };
  280. expose({
  281. open,
  282. close,
  283. updateActiveIndex,
  284. handleResize
  285. });
  286. }
  287. const ulStyle = useMenuCssVar.useMenuCssVar(props, 0);
  288. return () => {
  289. var _a, _b;
  290. let slot = (_b = (_a = slots.default) == null ? void 0 : _a.call(slots)) != null ? _b : [];
  291. const vShowMore = [];
  292. if (props.mode === "horizontal" && menu.value) {
  293. const originalSlot = vnode.flattedChildren(slot);
  294. const slotDefault = sliceIndex.value === -1 ? originalSlot : originalSlot.slice(0, sliceIndex.value);
  295. const slotMore = sliceIndex.value === -1 ? [] : originalSlot.slice(sliceIndex.value);
  296. if ((slotMore == null ? void 0 : slotMore.length) && props.ellipsis) {
  297. slot = slotDefault;
  298. vShowMore.push(vue.h(subMenu["default"], {
  299. index: "sub-menu-more",
  300. class: nsSubMenu.e("hide-arrow"),
  301. popperOffset: props.popperOffset
  302. }, {
  303. title: () => vue.h(index$1.ElIcon, {
  304. class: nsSubMenu.e("icon-more")
  305. }, {
  306. default: () => vue.h(props.ellipsisIcon)
  307. }),
  308. default: () => slotMore
  309. }));
  310. }
  311. }
  312. const directives = props.closeOnClickOutside ? [
  313. [
  314. index$2["default"],
  315. () => {
  316. if (!openedMenus.value.length)
  317. return;
  318. if (!mouseInChild.value) {
  319. openedMenus.value.forEach((openedMenu) => emit("close", openedMenu, getIndexPath(openedMenu)));
  320. openedMenus.value = [];
  321. }
  322. }
  323. ]
  324. ] : [];
  325. const vMenu = vue.withDirectives(vue.h("ul", {
  326. key: String(props.collapse),
  327. role: "menubar",
  328. ref: menu,
  329. style: ulStyle.value,
  330. class: {
  331. [nsMenu.b()]: true,
  332. [nsMenu.m(props.mode)]: true,
  333. [nsMenu.m("collapse")]: props.collapse
  334. }
  335. }, [...slot, ...vShowMore]), directives);
  336. if (props.collapseTransition && props.mode === "vertical") {
  337. return vue.h(menuCollapseTransition["default"], () => vMenu);
  338. }
  339. return vMenu;
  340. };
  341. }
  342. });
  343. exports["default"] = Menu;
  344. exports.menuEmits = menuEmits;
  345. exports.menuProps = menuProps;
  346. //# sourceMappingURL=menu.js.map