5282056f206a1fbd775f65b616bd70786b4b07932549c27b1e9f8e599e1426dbd292db267d7da55a10b78ac226d30e5532151a7098e533f12bff5f9d48ef64 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import { defineComponent, getCurrentInstance, computed, inject, ref, reactive, watch, provide, onMounted, onBeforeUnmount, h, Fragment, withDirectives, vShow, nextTick } from 'vue';
  2. import { useTimeoutFn } from '@vueuse/core';
  3. import { ElCollapseTransition } from '../../collapse-transition/index.mjs';
  4. import { ElTooltip } from '../../tooltip/index.mjs';
  5. import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
  6. import { ElIcon } from '../../icon/index.mjs';
  7. import useMenu from './use-menu.mjs';
  8. import { useMenuCssVar } from './use-menu-css-var.mjs';
  9. import { MENU_INJECTION_KEY, SUB_MENU_INJECTION_KEY } from './tokens.mjs';
  10. import { buildProps } from '../../../utils/vue/props/runtime.mjs';
  11. import { iconPropType } from '../../../utils/vue/icon.mjs';
  12. import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
  13. import { throwError } from '../../../utils/error.mjs';
  14. import { isUndefined } from '../../../utils/types.mjs';
  15. import { isString } from '@vue/shared';
  16. import { focusElement } from '../../../utils/dom/aria.mjs';
  17. const subMenuProps = buildProps({
  18. index: {
  19. type: String,
  20. required: true
  21. },
  22. showTimeout: Number,
  23. hideTimeout: Number,
  24. popperClass: String,
  25. disabled: Boolean,
  26. teleported: {
  27. type: Boolean,
  28. default: void 0
  29. },
  30. popperOffset: Number,
  31. expandCloseIcon: {
  32. type: iconPropType
  33. },
  34. expandOpenIcon: {
  35. type: iconPropType
  36. },
  37. collapseCloseIcon: {
  38. type: iconPropType
  39. },
  40. collapseOpenIcon: {
  41. type: iconPropType
  42. }
  43. });
  44. const COMPONENT_NAME = "ElSubMenu";
  45. var SubMenu = defineComponent({
  46. name: COMPONENT_NAME,
  47. props: subMenuProps,
  48. setup(props, { slots, expose }) {
  49. const instance = getCurrentInstance();
  50. const { indexPath, parentMenu } = useMenu(instance, computed(() => props.index));
  51. const nsMenu = useNamespace("menu");
  52. const nsSubMenu = useNamespace("sub-menu");
  53. const rootMenu = inject(MENU_INJECTION_KEY);
  54. if (!rootMenu)
  55. throwError(COMPONENT_NAME, "can not inject root menu");
  56. const subMenu = inject(`${SUB_MENU_INJECTION_KEY}${parentMenu.value.uid}`);
  57. if (!subMenu)
  58. throwError(COMPONENT_NAME, "can not inject sub menu");
  59. const items = ref({});
  60. const subMenus = ref({});
  61. let timeout;
  62. const mouseInChild = ref(false);
  63. const verticalTitleRef = ref();
  64. const vPopper = ref();
  65. const isFirstLevel = computed(() => subMenu.level === 0);
  66. const currentPlacement = computed(() => mode.value === "horizontal" && isFirstLevel.value ? "bottom-start" : "right-start");
  67. const subMenuTitleIcon = computed(() => {
  68. const isExpandedMode = mode.value === "horizontal" && isFirstLevel.value || mode.value === "vertical" && !rootMenu.props.collapse;
  69. if (isExpandedMode) {
  70. if (props.expandCloseIcon && props.expandOpenIcon) {
  71. return opened.value ? props.expandOpenIcon : props.expandCloseIcon;
  72. }
  73. return ArrowDown;
  74. } else {
  75. if (props.collapseCloseIcon && props.collapseOpenIcon) {
  76. return opened.value ? props.collapseOpenIcon : props.collapseCloseIcon;
  77. }
  78. return ArrowRight;
  79. }
  80. });
  81. const appendToBody = computed(() => {
  82. const value = props.teleported;
  83. return isUndefined(value) ? isFirstLevel.value : value;
  84. });
  85. const menuTransitionName = computed(() => rootMenu.props.collapse ? `${nsMenu.namespace.value}-zoom-in-left` : `${nsMenu.namespace.value}-zoom-in-top`);
  86. const fallbackPlacements = computed(() => mode.value === "horizontal" && isFirstLevel.value ? [
  87. "bottom-start",
  88. "bottom-end",
  89. "top-start",
  90. "top-end",
  91. "right-start",
  92. "left-start"
  93. ] : [
  94. "right-start",
  95. "right",
  96. "right-end",
  97. "left-start",
  98. "bottom-start",
  99. "bottom-end",
  100. "top-start",
  101. "top-end"
  102. ]);
  103. const opened = computed(() => rootMenu.openedMenus.includes(props.index));
  104. const active = computed(() => [...Object.values(items.value), ...Object.values(subMenus.value)].some(({ active: active2 }) => active2));
  105. const mode = computed(() => rootMenu.props.mode);
  106. const persistent = computed(() => rootMenu.props.persistent);
  107. const item = reactive({
  108. index: props.index,
  109. indexPath,
  110. active
  111. });
  112. const ulStyle = useMenuCssVar(rootMenu.props, subMenu.level + 1);
  113. const subMenuPopperOffset = computed(() => {
  114. var _a;
  115. return (_a = props.popperOffset) != null ? _a : rootMenu.props.popperOffset;
  116. });
  117. const subMenuPopperClass = computed(() => {
  118. var _a;
  119. return (_a = props.popperClass) != null ? _a : rootMenu.props.popperClass;
  120. });
  121. const subMenuShowTimeout = computed(() => {
  122. var _a;
  123. return (_a = props.showTimeout) != null ? _a : rootMenu.props.showTimeout;
  124. });
  125. const subMenuHideTimeout = computed(() => {
  126. var _a;
  127. return (_a = props.hideTimeout) != null ? _a : rootMenu.props.hideTimeout;
  128. });
  129. const doDestroy = () => {
  130. var _a, _b, _c;
  131. return (_c = (_b = (_a = vPopper.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.popperInstanceRef) == null ? void 0 : _c.destroy();
  132. };
  133. const handleCollapseToggle = (value) => {
  134. if (!value) {
  135. doDestroy();
  136. }
  137. };
  138. const handleClick = () => {
  139. if (rootMenu.props.menuTrigger === "hover" && rootMenu.props.mode === "horizontal" || rootMenu.props.collapse && rootMenu.props.mode === "vertical" || props.disabled)
  140. return;
  141. rootMenu.handleSubMenuClick({
  142. index: props.index,
  143. indexPath: indexPath.value,
  144. active: active.value
  145. });
  146. };
  147. const handleMouseenter = (event, showTimeout = subMenuShowTimeout.value) => {
  148. var _a;
  149. if (event.type === "focus")
  150. return;
  151. if (rootMenu.props.menuTrigger === "click" && rootMenu.props.mode === "horizontal" || !rootMenu.props.collapse && rootMenu.props.mode === "vertical" || props.disabled) {
  152. subMenu.mouseInChild.value = true;
  153. return;
  154. }
  155. subMenu.mouseInChild.value = true;
  156. timeout == null ? void 0 : timeout();
  157. ({ stop: timeout } = useTimeoutFn(() => {
  158. rootMenu.openMenu(props.index, indexPath.value);
  159. }, showTimeout));
  160. if (appendToBody.value) {
  161. (_a = parentMenu.value.vnode.el) == null ? void 0 : _a.dispatchEvent(new MouseEvent("mouseenter"));
  162. }
  163. if (event.type === "mouseenter" && event.target) {
  164. nextTick(() => {
  165. focusElement(event.target, { preventScroll: true });
  166. });
  167. }
  168. };
  169. const handleMouseleave = (deepDispatch = false) => {
  170. var _a;
  171. if (rootMenu.props.menuTrigger === "click" && rootMenu.props.mode === "horizontal" || !rootMenu.props.collapse && rootMenu.props.mode === "vertical") {
  172. subMenu.mouseInChild.value = false;
  173. return;
  174. }
  175. timeout == null ? void 0 : timeout();
  176. subMenu.mouseInChild.value = false;
  177. ({ stop: timeout } = useTimeoutFn(() => !mouseInChild.value && rootMenu.closeMenu(props.index, indexPath.value), subMenuHideTimeout.value));
  178. if (appendToBody.value && deepDispatch) {
  179. (_a = subMenu.handleMouseleave) == null ? void 0 : _a.call(subMenu, true);
  180. }
  181. };
  182. watch(() => rootMenu.props.collapse, (value) => handleCollapseToggle(Boolean(value)));
  183. {
  184. const addSubMenu = (item2) => {
  185. subMenus.value[item2.index] = item2;
  186. };
  187. const removeSubMenu = (item2) => {
  188. delete subMenus.value[item2.index];
  189. };
  190. provide(`${SUB_MENU_INJECTION_KEY}${instance.uid}`, {
  191. addSubMenu,
  192. removeSubMenu,
  193. handleMouseleave,
  194. mouseInChild,
  195. level: subMenu.level + 1
  196. });
  197. }
  198. expose({
  199. opened
  200. });
  201. onMounted(() => {
  202. rootMenu.addSubMenu(item);
  203. subMenu.addSubMenu(item);
  204. });
  205. onBeforeUnmount(() => {
  206. subMenu.removeSubMenu(item);
  207. rootMenu.removeSubMenu(item);
  208. });
  209. return () => {
  210. var _a;
  211. const titleTag = [
  212. (_a = slots.title) == null ? void 0 : _a.call(slots),
  213. h(ElIcon, {
  214. class: nsSubMenu.e("icon-arrow"),
  215. style: {
  216. transform: opened.value ? props.expandCloseIcon && props.expandOpenIcon || props.collapseCloseIcon && props.collapseOpenIcon && rootMenu.props.collapse ? "none" : "rotateZ(180deg)" : "none"
  217. }
  218. }, {
  219. default: () => isString(subMenuTitleIcon.value) ? h(instance.appContext.components[subMenuTitleIcon.value]) : h(subMenuTitleIcon.value)
  220. })
  221. ];
  222. const child = rootMenu.isMenuPopup ? h(ElTooltip, {
  223. ref: vPopper,
  224. visible: opened.value,
  225. effect: "light",
  226. pure: true,
  227. offset: subMenuPopperOffset.value,
  228. showArrow: false,
  229. persistent: persistent.value,
  230. popperClass: subMenuPopperClass.value,
  231. placement: currentPlacement.value,
  232. teleported: appendToBody.value,
  233. fallbackPlacements: fallbackPlacements.value,
  234. transition: menuTransitionName.value,
  235. gpuAcceleration: false
  236. }, {
  237. content: () => {
  238. var _a2;
  239. return h("div", {
  240. class: [
  241. nsMenu.m(mode.value),
  242. nsMenu.m("popup-container"),
  243. subMenuPopperClass.value
  244. ],
  245. onMouseenter: (evt) => handleMouseenter(evt, 100),
  246. onMouseleave: () => handleMouseleave(true),
  247. onFocus: (evt) => handleMouseenter(evt, 100)
  248. }, [
  249. h("ul", {
  250. class: [
  251. nsMenu.b(),
  252. nsMenu.m("popup"),
  253. nsMenu.m(`popup-${currentPlacement.value}`)
  254. ],
  255. style: ulStyle.value
  256. }, [(_a2 = slots.default) == null ? void 0 : _a2.call(slots)])
  257. ]);
  258. },
  259. default: () => h("div", {
  260. class: nsSubMenu.e("title"),
  261. onClick: handleClick
  262. }, titleTag)
  263. }) : h(Fragment, {}, [
  264. h("div", {
  265. class: nsSubMenu.e("title"),
  266. ref: verticalTitleRef,
  267. onClick: handleClick
  268. }, titleTag),
  269. h(ElCollapseTransition, {}, {
  270. default: () => {
  271. var _a2;
  272. return withDirectives(h("ul", {
  273. role: "menu",
  274. class: [nsMenu.b(), nsMenu.m("inline")],
  275. style: ulStyle.value
  276. }, [(_a2 = slots.default) == null ? void 0 : _a2.call(slots)]), [[vShow, opened.value]]);
  277. }
  278. })
  279. ]);
  280. return h("li", {
  281. class: [
  282. nsSubMenu.b(),
  283. nsSubMenu.is("active", active.value),
  284. nsSubMenu.is("opened", opened.value),
  285. nsSubMenu.is("disabled", props.disabled)
  286. ],
  287. role: "menuitem",
  288. ariaHaspopup: true,
  289. ariaExpanded: opened.value,
  290. onMouseenter: handleMouseenter,
  291. onMouseleave: () => handleMouseleave(),
  292. onFocus: handleMouseenter
  293. }, [child]);
  294. };
  295. }
  296. });
  297. export { SubMenu as default, subMenuProps };
  298. //# sourceMappingURL=sub-menu.mjs.map