UNPKG

naive-ui

Version:

A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast

295 lines 9.81 kB
import { Transition, computed, defineComponent, h, inject, mergeProps, provide, ref } from 'vue'; import { VBinder, VFollower, VTarget } from 'vueuc'; import { useMemo } from 'vooks'; import { happensIn } from 'seemly'; import { ChevronRightIcon } from "../../_internal/icons/index.mjs"; import { render, useDeferredTrue } from "../../_utils/index.mjs"; import { NIcon } from "../../icon/index.mjs"; import { popoverBodyInjectionKey } from "../../popover/src/interface.mjs"; import NDropdownMenu from "./DropdownMenu.mjs"; import { dropdownInjectionKey, dropdownMenuInjectionKey, dropdownOptionInjectionKey } from "./context.mjs"; import { isSubmenuNode } from "./utils.mjs"; export default defineComponent({ name: 'DropdownOption', props: { clsPrefix: { type: String, required: true }, tmNode: { type: Object, required: true }, parentKey: { type: [String, Number], default: null }, placement: { type: String, default: 'right-start' }, props: Object, scrollable: Boolean }, setup(props) { const NDropdown = inject(dropdownInjectionKey); const { hoverKeyRef, keyboardKeyRef, lastToggledSubmenuKeyRef, pendingKeyPathRef, activeKeyPathRef, animatedRef, mergedShowRef, renderLabelRef, renderIconRef, labelFieldRef, childrenFieldRef, renderOptionRef, nodePropsRef, menuPropsRef } = NDropdown; const NDropdownOption = inject(dropdownOptionInjectionKey, null); const NDropdownMenu = inject(dropdownMenuInjectionKey); const NPopoverBody = inject(popoverBodyInjectionKey); const rawNodeRef = computed(() => props.tmNode.rawNode); const hasSubmenuRef = computed(() => { const { value: childrenField } = childrenFieldRef; return isSubmenuNode(props.tmNode.rawNode, childrenField); }); const mergedDisabledRef = computed(() => { const { disabled } = props.tmNode; return disabled; }); const showSubmenuRef = computed(() => { if (!hasSubmenuRef.value) return false; const { key, disabled } = props.tmNode; if (disabled) return false; const { value: hoverKey } = hoverKeyRef; const { value: keyboardKey } = keyboardKeyRef; const { value: lastToggledSubmenuKey } = lastToggledSubmenuKeyRef; const { value: pendingKeyPath } = pendingKeyPathRef; if (hoverKey !== null) return pendingKeyPath.includes(key); if (keyboardKey !== null) { return pendingKeyPath.includes(key) && pendingKeyPath[pendingKeyPath.length - 1] !== key; } if (lastToggledSubmenuKey !== null) return pendingKeyPath.includes(key); return false; }); const shouldDelayRef = computed(() => { return keyboardKeyRef.value === null && !animatedRef.value; }); const deferredShowSubmenuRef = useDeferredTrue(showSubmenuRef, 300, shouldDelayRef); const parentEnteringSubmenuRef = computed(() => { return !!(NDropdownOption === null || NDropdownOption === void 0 ? void 0 : NDropdownOption.enteringSubmenuRef.value); }); const enteringSubmenuRef = ref(false); provide(dropdownOptionInjectionKey, { enteringSubmenuRef }); // methods function handleSubmenuBeforeEnter() { enteringSubmenuRef.value = true; } function handleSubmenuAfterEnter() { enteringSubmenuRef.value = false; } function handleMouseEnter() { const { parentKey, tmNode } = props; if (tmNode.disabled) return; if (!mergedShowRef.value) return; lastToggledSubmenuKeyRef.value = parentKey; keyboardKeyRef.value = null; hoverKeyRef.value = tmNode.key; } function handleMouseMove() { const { tmNode } = props; if (tmNode.disabled) return; if (!mergedShowRef.value) return; if (hoverKeyRef.value === tmNode.key) return; handleMouseEnter(); } function handleMouseLeave(e) { if (props.tmNode.disabled) return; if (!mergedShowRef.value) return; const { relatedTarget } = e; if (relatedTarget && !happensIn({ target: relatedTarget }, 'dropdownOption') && !happensIn({ target: relatedTarget }, 'scrollbarRail')) { hoverKeyRef.value = null; } } function handleClick() { const { value: hasSubmenu } = hasSubmenuRef; const { tmNode } = props; if (!mergedShowRef.value) return; if (!hasSubmenu && !tmNode.disabled) { NDropdown.doSelect(tmNode.key, tmNode.rawNode); NDropdown.doUpdateShow(false); } } return { labelField: labelFieldRef, renderLabel: renderLabelRef, renderIcon: renderIconRef, siblingHasIcon: NDropdownMenu.showIconRef, siblingHasSubmenu: NDropdownMenu.hasSubmenuRef, menuProps: menuPropsRef, popoverBody: NPopoverBody, animated: animatedRef, mergedShowSubmenu: computed(() => { return deferredShowSubmenuRef.value && !parentEnteringSubmenuRef.value; }), rawNode: rawNodeRef, hasSubmenu: hasSubmenuRef, pending: useMemo(() => { const { value: pendingKeyPath } = pendingKeyPathRef; const { key } = props.tmNode; return pendingKeyPath.includes(key); }), childActive: useMemo(() => { const { value: activeKeyPath } = activeKeyPathRef; const { key } = props.tmNode; const index = activeKeyPath.findIndex(k => key === k); if (index === -1) return false; return index < activeKeyPath.length - 1; }), active: useMemo(() => { const { value: activeKeyPath } = activeKeyPathRef; const { key } = props.tmNode; const index = activeKeyPath.findIndex(k => key === k); if (index === -1) return false; return index === activeKeyPath.length - 1; }), mergedDisabled: mergedDisabledRef, renderOption: renderOptionRef, nodeProps: nodePropsRef, handleClick, handleMouseMove, handleMouseEnter, handleMouseLeave, handleSubmenuBeforeEnter, handleSubmenuAfterEnter }; }, render() { var _a, _b; const { animated, rawNode, mergedShowSubmenu, clsPrefix, siblingHasIcon, siblingHasSubmenu, renderLabel, renderIcon, renderOption, nodeProps, props, scrollable } = this; let submenuVNode = null; if (mergedShowSubmenu) { const submenuNodeProps = (_a = this.menuProps) === null || _a === void 0 ? void 0 : _a.call(this, rawNode, rawNode.children); submenuVNode = h(NDropdownMenu, Object.assign({}, submenuNodeProps, { clsPrefix: clsPrefix, scrollable: this.scrollable, tmNodes: this.tmNode.children, parentKey: this.tmNode.key })); } const builtinProps = { class: [`${clsPrefix}-dropdown-option-body`, this.pending && `${clsPrefix}-dropdown-option-body--pending`, this.active && `${clsPrefix}-dropdown-option-body--active`, this.childActive && `${clsPrefix}-dropdown-option-body--child-active`, this.mergedDisabled && `${clsPrefix}-dropdown-option-body--disabled`], onMousemove: this.handleMouseMove, onMouseenter: this.handleMouseEnter, onMouseleave: this.handleMouseLeave, onClick: this.handleClick }; const optionNodeProps = nodeProps === null || nodeProps === void 0 ? void 0 : nodeProps(rawNode); const node = h("div", Object.assign({ class: [`${clsPrefix}-dropdown-option`, optionNodeProps === null || optionNodeProps === void 0 ? void 0 : optionNodeProps.class], "data-dropdown-option": true }, optionNodeProps), h('div', mergeProps(builtinProps, props), [h("div", { class: [`${clsPrefix}-dropdown-option-body__prefix`, siblingHasIcon && `${clsPrefix}-dropdown-option-body__prefix--show-icon`] }, [renderIcon ? renderIcon(rawNode) : render(rawNode.icon)]), h("div", { "data-dropdown-option": true, class: `${clsPrefix}-dropdown-option-body__label` }, renderLabel ? renderLabel(rawNode) : render((_b = rawNode[this.labelField]) !== null && _b !== void 0 ? _b : rawNode.title)), h("div", { "data-dropdown-option": true, class: [`${clsPrefix}-dropdown-option-body__suffix`, siblingHasSubmenu && `${clsPrefix}-dropdown-option-body__suffix--has-submenu`] }, this.hasSubmenu ? h(NIcon, null, { default: () => h(ChevronRightIcon, null) }) : null)]), this.hasSubmenu ? h(VBinder, null, { default: () => [h(VTarget, null, { default: () => h("div", { class: `${clsPrefix}-dropdown-offset-container` }, h(VFollower, { show: this.mergedShowSubmenu, placement: this.placement, to: scrollable ? this.popoverBody || undefined : undefined, teleportDisabled: !scrollable }, { default: () => { return h("div", { class: `${clsPrefix}-dropdown-menu-wrapper` }, animated ? h(Transition, { onBeforeEnter: this.handleSubmenuBeforeEnter, onAfterEnter: this.handleSubmenuAfterEnter, name: "fade-in-scale-up-transition", appear: true }, { default: () => submenuVNode }) : submenuVNode); } })) })] }) : null); if (renderOption) { return renderOption({ node, option: rawNode }); } return node; } });