element-plus
Version:
A Component Library for Vue 3
283 lines (280 loc) • 10.5 kB
JavaScript
import { defineComponent, getCurrentInstance, computed, inject, ref, reactive, watch, provide, onMounted, onBeforeUnmount, h, Fragment, withDirectives, vShow } from 'vue';
import { useTimeoutFn } from '@vueuse/core';
import { ElCollapseTransition } from '../../collapse-transition/index.mjs';
import { ElTooltip } from '../../tooltip/index.mjs';
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
import { ElIcon } from '../../icon/index.mjs';
import useMenu from './use-menu.mjs';
import { useMenuCssVar } from './use-menu-css-var.mjs';
import { buildProps } from '../../../utils/vue/props/runtime.mjs';
import { iconPropType } from '../../../utils/vue/icon.mjs';
import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
import { throwError } from '../../../utils/error.mjs';
import { isUndefined } from '../../../utils/types.mjs';
import { isString } from '@vue/shared';
const subMenuProps = buildProps({
index: {
type: String,
required: true
},
showTimeout: Number,
hideTimeout: Number,
popperClass: String,
disabled: Boolean,
teleported: {
type: Boolean,
default: void 0
},
popperOffset: Number,
expandCloseIcon: {
type: iconPropType
},
expandOpenIcon: {
type: iconPropType
},
collapseCloseIcon: {
type: iconPropType
},
collapseOpenIcon: {
type: iconPropType
}
});
const COMPONENT_NAME = "ElSubMenu";
var SubMenu = defineComponent({
name: COMPONENT_NAME,
props: subMenuProps,
setup(props, { slots, expose }) {
const instance = getCurrentInstance();
const { indexPath, parentMenu } = useMenu(instance, computed(() => props.index));
const nsMenu = useNamespace("menu");
const nsSubMenu = useNamespace("sub-menu");
const rootMenu = inject("rootMenu");
if (!rootMenu)
throwError(COMPONENT_NAME, "can not inject root menu");
const subMenu = inject(`subMenu:${parentMenu.value.uid}`);
if (!subMenu)
throwError(COMPONENT_NAME, "can not inject sub menu");
const items = ref({});
const subMenus = ref({});
let timeout;
const mouseInChild = ref(false);
const verticalTitleRef = ref();
const vPopper = ref();
const currentPlacement = computed(() => mode.value === "horizontal" && isFirstLevel.value ? "bottom-start" : "right-start");
const subMenuTitleIcon = computed(() => {
return mode.value === "horizontal" && isFirstLevel.value || mode.value === "vertical" && !rootMenu.props.collapse ? props.expandCloseIcon && props.expandOpenIcon ? opened.value ? props.expandOpenIcon : props.expandCloseIcon : ArrowDown : props.collapseCloseIcon && props.collapseOpenIcon ? opened.value ? props.collapseOpenIcon : props.collapseCloseIcon : ArrowRight;
});
const isFirstLevel = computed(() => subMenu.level === 0);
const appendToBody = computed(() => {
const value = props.teleported;
return isUndefined(value) ? isFirstLevel.value : value;
});
const menuTransitionName = computed(() => rootMenu.props.collapse ? `${nsMenu.namespace.value}-zoom-in-left` : `${nsMenu.namespace.value}-zoom-in-top`);
const fallbackPlacements = computed(() => mode.value === "horizontal" && isFirstLevel.value ? [
"bottom-start",
"bottom-end",
"top-start",
"top-end",
"right-start",
"left-start"
] : [
"right-start",
"right",
"right-end",
"left-start",
"bottom-start",
"bottom-end",
"top-start",
"top-end"
]);
const opened = computed(() => rootMenu.openedMenus.includes(props.index));
const active = computed(() => [...Object.values(items.value), ...Object.values(subMenus.value)].some(({ active: active2 }) => active2));
const mode = computed(() => rootMenu.props.mode);
const persistent = computed(() => rootMenu.props.persistent);
const item = reactive({
index: props.index,
indexPath,
active
});
const ulStyle = useMenuCssVar(rootMenu.props, subMenu.level + 1);
const subMenuPopperOffset = computed(() => {
var _a;
return (_a = props.popperOffset) != null ? _a : rootMenu.props.popperOffset;
});
const subMenuPopperClass = computed(() => {
var _a;
return (_a = props.popperClass) != null ? _a : rootMenu.props.popperClass;
});
const subMenuShowTimeout = computed(() => {
var _a;
return (_a = props.showTimeout) != null ? _a : rootMenu.props.showTimeout;
});
const subMenuHideTimeout = computed(() => {
var _a;
return (_a = props.hideTimeout) != null ? _a : rootMenu.props.hideTimeout;
});
const doDestroy = () => {
var _a, _b, _c;
return (_c = (_b = (_a = vPopper.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.popperInstanceRef) == null ? void 0 : _c.destroy();
};
const handleCollapseToggle = (value) => {
if (!value) {
doDestroy();
}
};
const handleClick = () => {
if (rootMenu.props.menuTrigger === "hover" && rootMenu.props.mode === "horizontal" || rootMenu.props.collapse && rootMenu.props.mode === "vertical" || props.disabled)
return;
rootMenu.handleSubMenuClick({
index: props.index,
indexPath: indexPath.value,
active: active.value
});
};
const handleMouseenter = (event, showTimeout = subMenuShowTimeout.value) => {
var _a;
if (event.type === "focus")
return;
if (rootMenu.props.menuTrigger === "click" && rootMenu.props.mode === "horizontal" || !rootMenu.props.collapse && rootMenu.props.mode === "vertical" || props.disabled) {
subMenu.mouseInChild.value = true;
return;
}
subMenu.mouseInChild.value = true;
timeout == null ? void 0 : timeout();
({ stop: timeout } = useTimeoutFn(() => {
rootMenu.openMenu(props.index, indexPath.value);
}, showTimeout));
if (appendToBody.value) {
(_a = parentMenu.value.vnode.el) == null ? void 0 : _a.dispatchEvent(new MouseEvent("mouseenter"));
}
};
const handleMouseleave = (deepDispatch = false) => {
var _a;
if (rootMenu.props.menuTrigger === "click" && rootMenu.props.mode === "horizontal" || !rootMenu.props.collapse && rootMenu.props.mode === "vertical") {
subMenu.mouseInChild.value = false;
return;
}
timeout == null ? void 0 : timeout();
subMenu.mouseInChild.value = false;
({ stop: timeout } = useTimeoutFn(() => !mouseInChild.value && rootMenu.closeMenu(props.index, indexPath.value), subMenuHideTimeout.value));
if (appendToBody.value && deepDispatch) {
(_a = subMenu.handleMouseleave) == null ? void 0 : _a.call(subMenu, true);
}
};
watch(() => rootMenu.props.collapse, (value) => handleCollapseToggle(Boolean(value)));
{
const addSubMenu = (item2) => {
subMenus.value[item2.index] = item2;
};
const removeSubMenu = (item2) => {
delete subMenus.value[item2.index];
};
provide(`subMenu:${instance.uid}`, {
addSubMenu,
removeSubMenu,
handleMouseleave,
mouseInChild,
level: subMenu.level + 1
});
}
expose({
opened
});
onMounted(() => {
rootMenu.addSubMenu(item);
subMenu.addSubMenu(item);
});
onBeforeUnmount(() => {
subMenu.removeSubMenu(item);
rootMenu.removeSubMenu(item);
});
return () => {
var _a;
const titleTag = [
(_a = slots.title) == null ? void 0 : _a.call(slots),
h(ElIcon, {
class: nsSubMenu.e("icon-arrow"),
style: {
transform: opened.value ? props.expandCloseIcon && props.expandOpenIcon || props.collapseCloseIcon && props.collapseOpenIcon && rootMenu.props.collapse ? "none" : "rotateZ(180deg)" : "none"
}
}, {
default: () => isString(subMenuTitleIcon.value) ? h(instance.appContext.components[subMenuTitleIcon.value]) : h(subMenuTitleIcon.value)
})
];
const child = rootMenu.isMenuPopup ? h(ElTooltip, {
ref: vPopper,
visible: opened.value,
effect: "light",
pure: true,
offset: subMenuPopperOffset.value,
showArrow: false,
persistent: persistent.value,
popperClass: subMenuPopperClass.value,
placement: currentPlacement.value,
teleported: appendToBody.value,
fallbackPlacements: fallbackPlacements.value,
transition: menuTransitionName.value,
gpuAcceleration: false
}, {
content: () => {
var _a2;
return h("div", {
class: [
nsMenu.m(mode.value),
nsMenu.m("popup-container"),
subMenuPopperClass.value
],
onMouseenter: (evt) => handleMouseenter(evt, 100),
onMouseleave: () => handleMouseleave(true),
onFocus: (evt) => handleMouseenter(evt, 100)
}, [
h("ul", {
class: [
nsMenu.b(),
nsMenu.m("popup"),
nsMenu.m(`popup-${currentPlacement.value}`)
],
style: ulStyle.value
}, [(_a2 = slots.default) == null ? void 0 : _a2.call(slots)])
]);
},
default: () => h("div", {
class: nsSubMenu.e("title"),
onClick: handleClick
}, titleTag)
}) : h(Fragment, {}, [
h("div", {
class: nsSubMenu.e("title"),
ref: verticalTitleRef,
onClick: handleClick
}, titleTag),
h(ElCollapseTransition, {}, {
default: () => {
var _a2;
return withDirectives(h("ul", {
role: "menu",
class: [nsMenu.b(), nsMenu.m("inline")],
style: ulStyle.value
}, [(_a2 = slots.default) == null ? void 0 : _a2.call(slots)]), [[vShow, opened.value]]);
}
})
]);
return h("li", {
class: [
nsSubMenu.b(),
nsSubMenu.is("active", active.value),
nsSubMenu.is("opened", opened.value),
nsSubMenu.is("disabled", props.disabled)
],
role: "menuitem",
ariaHaspopup: true,
ariaExpanded: opened.value,
onMouseenter: handleMouseenter,
onMouseleave: () => handleMouseleave(),
onFocus: handleMouseenter
}, [child]);
};
}
});
export { SubMenu as default, subMenuProps };
//# sourceMappingURL=sub-menu.mjs.map