naive-ui
Version:
A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast
532 lines • 20.6 kB
JavaScript
import { createId } from 'seemly';
import { createTreeMate } from 'treemate';
import { useCompitable, useMergedState } from 'vooks';
import { computed, defineComponent, h, inject, mergeProps, provide, ref, toRef, watchEffect } from 'vue';
import { VOverflow, VResizeObserver } from 'vueuc';
import { useConfig, useTheme, useThemeClass } from "../../_mixins/index.mjs";
import { call } from "../../_utils/index.mjs";
import { layoutSiderInjectionKey } from "../../layout/src/interface.mjs";
import { menuLight } from "../styles/index.mjs";
import { menuInjectionKey } from "./context.mjs";
import style from "./styles/index.cssr.mjs";
import { NSubmenu } from "./Submenu.mjs";
import { useCheckDeprecated } from "./useCheckDeprecated.mjs";
import { isIgnoredNode, itemRenderer } from "./utils.mjs";
export const menuProps = Object.assign(Object.assign({}, useTheme.props), {
options: {
type: Array,
default: () => []
},
collapsed: {
type: Boolean,
default: undefined
},
collapsedWidth: {
type: Number,
default: 48
},
iconSize: {
type: Number,
default: 20
},
collapsedIconSize: {
type: Number,
default: 24
},
rootIndent: Number,
indent: {
type: Number,
default: 32
},
labelField: {
type: String,
default: 'label'
},
keyField: {
type: String,
default: 'key'
},
childrenField: {
type: String,
default: 'children'
},
disabledField: {
type: String,
default: 'disabled'
},
defaultExpandAll: Boolean,
defaultExpandedKeys: Array,
expandedKeys: Array,
value: [String, Number],
defaultValue: {
type: [String, Number],
default: null
},
mode: {
type: String,
default: 'vertical'
},
watchProps: {
type: Array,
default: undefined
},
disabled: Boolean,
show: {
type: Boolean,
default: true
},
inverted: Boolean,
'onUpdate:expandedKeys': [Function, Array],
onUpdateExpandedKeys: [Function, Array],
onUpdateValue: [Function, Array],
'onUpdate:value': [Function, Array],
expandIcon: Function,
renderIcon: Function,
renderLabel: Function,
renderExtra: Function,
dropdownProps: Object,
accordion: Boolean,
nodeProps: Function,
dropdownPlacement: {
type: String,
default: 'bottom'
},
responsive: Boolean,
// deprecated
items: Array,
onOpenNamesChange: [Function, Array],
onSelect: [Function, Array],
onExpandedNamesChange: [Function, Array],
expandedNames: Array,
defaultExpandedNames: Array
});
export default defineComponent({
name: 'Menu',
inheritAttrs: false,
props: menuProps,
setup(props) {
if (process.env.NODE_ENV !== 'production') {
useCheckDeprecated(props);
}
const {
mergedClsPrefixRef,
inlineThemeDisabled
} = useConfig(props);
const themeRef = useTheme('Menu', '-menu', style, menuLight, props, mergedClsPrefixRef);
const layoutSider = inject(layoutSiderInjectionKey, null);
const mergedCollapsedRef = computed(() => {
var _a;
const {
collapsed
} = props;
if (collapsed !== undefined) return collapsed;
if (layoutSider) {
const {
collapseModeRef,
collapsedRef
} = layoutSider;
if (collapseModeRef.value === 'width') {
return (_a = collapsedRef.value) !== null && _a !== void 0 ? _a : false;
}
}
return false;
});
const treeMateRef = computed(() => {
const {
keyField,
childrenField,
disabledField
} = props;
return createTreeMate(props.items || props.options, {
getIgnored(node) {
return isIgnoredNode(node);
},
getChildren(node) {
return node[childrenField];
},
getDisabled(node) {
return node[disabledField];
},
getKey(node) {
var _a;
return (_a = node[keyField]) !== null && _a !== void 0 ? _a : node.name;
}
});
});
const treeKeysLevelOneRef = computed(() => new Set(treeMateRef.value.treeNodes.map(e => e.key)));
const {
watchProps
} = props;
const uncontrolledValueRef = ref(null);
if (watchProps === null || watchProps === void 0 ? void 0 : watchProps.includes('defaultValue')) {
watchEffect(() => {
uncontrolledValueRef.value = props.defaultValue;
});
} else {
uncontrolledValueRef.value = props.defaultValue;
}
const controlledValueRef = toRef(props, 'value');
const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef);
const uncontrolledExpandedKeysRef = ref([]);
const initUncontrolledExpandedKeys = () => {
uncontrolledExpandedKeysRef.value = props.defaultExpandAll ? treeMateRef.value.getNonLeafKeys() : props.defaultExpandedNames || props.defaultExpandedKeys || treeMateRef.value.getPath(mergedValueRef.value, {
includeSelf: false
}).keyPath;
};
if (watchProps === null || watchProps === void 0 ? void 0 : watchProps.includes('defaultExpandedKeys')) {
watchEffect(initUncontrolledExpandedKeys);
} else {
initUncontrolledExpandedKeys();
}
const controlledExpandedKeysRef = useCompitable(props, ['expandedNames', 'expandedKeys']);
const mergedExpandedKeysRef = useMergedState(controlledExpandedKeysRef, uncontrolledExpandedKeysRef);
const tmNodesRef = computed(() => treeMateRef.value.treeNodes);
const activePathRef = computed(() => {
return treeMateRef.value.getPath(mergedValueRef.value).keyPath;
});
provide(menuInjectionKey, {
props,
mergedCollapsedRef,
mergedThemeRef: themeRef,
mergedValueRef,
mergedExpandedKeysRef,
activePathRef,
mergedClsPrefixRef,
isHorizontalRef: computed(() => props.mode === 'horizontal'),
invertedRef: toRef(props, 'inverted'),
doSelect,
toggleExpand
});
function doSelect(value, item) {
const {
'onUpdate:value': _onUpdateValue,
onUpdateValue,
onSelect
} = props;
if (onUpdateValue) {
call(onUpdateValue, value, item);
}
if (_onUpdateValue) {
call(_onUpdateValue, value, item);
}
if (onSelect) {
call(onSelect, value, item);
}
uncontrolledValueRef.value = value;
}
function doUpdateExpandedKeys(value) {
const {
'onUpdate:expandedKeys': _onUpdateExpandedKeys,
onUpdateExpandedKeys,
onExpandedNamesChange,
onOpenNamesChange
} = props;
if (_onUpdateExpandedKeys) {
call(_onUpdateExpandedKeys, value);
}
if (onUpdateExpandedKeys) {
call(onUpdateExpandedKeys, value);
}
// deprecated
if (onExpandedNamesChange) {
call(onExpandedNamesChange, value);
}
if (onOpenNamesChange) {
call(onOpenNamesChange, value);
}
uncontrolledExpandedKeysRef.value = value;
}
function toggleExpand(key) {
const currentExpandedKeys = Array.from(mergedExpandedKeysRef.value);
const index = currentExpandedKeys.findIndex(expanededKey => expanededKey === key);
if (~index) {
currentExpandedKeys.splice(index, 1);
} else {
if (props.accordion) {
if (treeKeysLevelOneRef.value.has(key)) {
const closeKeyIndex = currentExpandedKeys.findIndex(e => treeKeysLevelOneRef.value.has(e));
if (closeKeyIndex > -1) {
currentExpandedKeys.splice(closeKeyIndex, 1);
}
}
}
currentExpandedKeys.push(key);
}
doUpdateExpandedKeys(currentExpandedKeys);
}
const showOption = key => {
const selectedKeyPath = treeMateRef.value.getPath(key !== null && key !== void 0 ? key : mergedValueRef.value, {
includeSelf: false
}).keyPath;
if (!selectedKeyPath.length) return;
const currentExpandedKeys = Array.from(mergedExpandedKeysRef.value);
const nextExpandedKeys = new Set([...currentExpandedKeys, ...selectedKeyPath]);
if (props.accordion) {
treeKeysLevelOneRef.value.forEach(firstLevelKey => {
if (nextExpandedKeys.has(firstLevelKey) && !selectedKeyPath.includes(firstLevelKey)) {
nextExpandedKeys.delete(firstLevelKey);
}
});
}
doUpdateExpandedKeys(Array.from(nextExpandedKeys));
};
const cssVarsRef = computed(() => {
const {
inverted
} = props;
const {
common: {
cubicBezierEaseInOut
},
self
} = themeRef.value;
const {
borderRadius,
borderColorHorizontal,
fontSize,
itemHeight,
dividerColor
} = self;
const vars = {
'--n-divider-color': dividerColor,
'--n-bezier': cubicBezierEaseInOut,
'--n-font-size': fontSize,
'--n-border-color-horizontal': borderColorHorizontal,
'--n-border-radius': borderRadius,
'--n-item-height': itemHeight
};
if (inverted) {
vars['--n-group-text-color'] = self.groupTextColorInverted;
vars['--n-color'] = self.colorInverted;
vars['--n-item-text-color'] = self.itemTextColorInverted;
vars['--n-item-text-color-hover'] = self.itemTextColorHoverInverted;
vars['--n-item-text-color-active'] = self.itemTextColorActiveInverted;
vars['--n-item-text-color-child-active'] = self.itemTextColorChildActiveInverted;
vars['--n-item-text-color-child-active-hover'] = self.itemTextColorChildActiveInverted;
vars['--n-item-text-color-active-hover'] = self.itemTextColorActiveHoverInverted;
vars['--n-item-icon-color'] = self.itemIconColorInverted;
vars['--n-item-icon-color-hover'] = self.itemIconColorHoverInverted;
vars['--n-item-icon-color-active'] = self.itemIconColorActiveInverted;
vars['--n-item-icon-color-active-hover'] = self.itemIconColorActiveHoverInverted;
vars['--n-item-icon-color-child-active'] = self.itemIconColorChildActiveInverted;
vars['--n-item-icon-color-child-active-hover'] = self.itemIconColorChildActiveHoverInverted;
vars['--n-item-icon-color-collapsed'] = self.itemIconColorCollapsedInverted;
vars['--n-item-text-color-horizontal'] = self.itemTextColorHorizontalInverted;
vars['--n-item-text-color-hover-horizontal'] = self.itemTextColorHoverHorizontalInverted;
vars['--n-item-text-color-active-horizontal'] = self.itemTextColorActiveHorizontalInverted;
vars['--n-item-text-color-child-active-horizontal'] = self.itemTextColorChildActiveHorizontalInverted;
vars['--n-item-text-color-child-active-hover-horizontal'] = self.itemTextColorChildActiveHoverHorizontalInverted;
vars['--n-item-text-color-active-hover-horizontal'] = self.itemTextColorActiveHoverHorizontalInverted;
vars['--n-item-icon-color-horizontal'] = self.itemIconColorHorizontalInverted;
vars['--n-item-icon-color-hover-horizontal'] = self.itemIconColorHoverHorizontalInverted;
vars['--n-item-icon-color-active-horizontal'] = self.itemIconColorActiveHorizontalInverted;
vars['--n-item-icon-color-active-hover-horizontal'] = self.itemIconColorActiveHoverHorizontalInverted;
vars['--n-item-icon-color-child-active-horizontal'] = self.itemIconColorChildActiveHorizontalInverted;
vars['--n-item-icon-color-child-active-hover-horizontal'] = self.itemIconColorChildActiveHoverHorizontalInverted;
vars['--n-arrow-color'] = self.arrowColorInverted;
vars['--n-arrow-color-hover'] = self.arrowColorHoverInverted;
vars['--n-arrow-color-active'] = self.arrowColorActiveInverted;
vars['--n-arrow-color-active-hover'] = self.arrowColorActiveHoverInverted;
vars['--n-arrow-color-child-active'] = self.arrowColorChildActiveInverted;
vars['--n-arrow-color-child-active-hover'] = self.arrowColorChildActiveHoverInverted;
vars['--n-item-color-hover'] = self.itemColorHoverInverted;
vars['--n-item-color-active'] = self.itemColorActiveInverted;
vars['--n-item-color-active-hover'] = self.itemColorActiveHoverInverted;
vars['--n-item-color-active-collapsed'] = self.itemColorActiveCollapsedInverted;
} else {
vars['--n-group-text-color'] = self.groupTextColor;
vars['--n-color'] = self.color;
vars['--n-item-text-color'] = self.itemTextColor;
vars['--n-item-text-color-hover'] = self.itemTextColorHover;
vars['--n-item-text-color-active'] = self.itemTextColorActive;
vars['--n-item-text-color-child-active'] = self.itemTextColorChildActive;
vars['--n-item-text-color-child-active-hover'] = self.itemTextColorChildActiveHover;
vars['--n-item-text-color-active-hover'] = self.itemTextColorActiveHover;
vars['--n-item-icon-color'] = self.itemIconColor;
vars['--n-item-icon-color-hover'] = self.itemIconColorHover;
vars['--n-item-icon-color-active'] = self.itemIconColorActive;
vars['--n-item-icon-color-active-hover'] = self.itemIconColorActiveHover;
vars['--n-item-icon-color-child-active'] = self.itemIconColorChildActive;
vars['--n-item-icon-color-child-active-hover'] = self.itemIconColorChildActiveHover;
vars['--n-item-icon-color-collapsed'] = self.itemIconColorCollapsed;
vars['--n-item-text-color-horizontal'] = self.itemTextColorHorizontal;
vars['--n-item-text-color-hover-horizontal'] = self.itemTextColorHoverHorizontal;
vars['--n-item-text-color-active-horizontal'] = self.itemTextColorActiveHorizontal;
vars['--n-item-text-color-child-active-horizontal'] = self.itemTextColorChildActiveHorizontal;
vars['--n-item-text-color-child-active-hover-horizontal'] = self.itemTextColorChildActiveHoverHorizontal;
vars['--n-item-text-color-active-hover-horizontal'] = self.itemTextColorActiveHoverHorizontal;
vars['--n-item-icon-color-horizontal'] = self.itemIconColorHorizontal;
vars['--n-item-icon-color-hover-horizontal'] = self.itemIconColorHoverHorizontal;
vars['--n-item-icon-color-active-horizontal'] = self.itemIconColorActiveHorizontal;
vars['--n-item-icon-color-active-hover-horizontal'] = self.itemIconColorActiveHoverHorizontal;
vars['--n-item-icon-color-child-active-horizontal'] = self.itemIconColorChildActiveHorizontal;
vars['--n-item-icon-color-child-active-hover-horizontal'] = self.itemIconColorChildActiveHoverHorizontal;
vars['--n-arrow-color'] = self.arrowColor;
vars['--n-arrow-color-hover'] = self.arrowColorHover;
vars['--n-arrow-color-active'] = self.arrowColorActive;
vars['--n-arrow-color-active-hover'] = self.arrowColorActiveHover;
vars['--n-arrow-color-child-active'] = self.arrowColorChildActive;
vars['--n-arrow-color-child-active-hover'] = self.arrowColorChildActiveHover;
vars['--n-item-color-hover'] = self.itemColorHover;
vars['--n-item-color-active'] = self.itemColorActive;
vars['--n-item-color-active-hover'] = self.itemColorActiveHover;
vars['--n-item-color-active-collapsed'] = self.itemColorActiveCollapsed;
}
return vars;
});
const themeClassHandle = inlineThemeDisabled ? useThemeClass('menu', computed(() => props.inverted ? 'a' : 'b'), cssVarsRef, props) : undefined;
const ellipsisNodeId = createId();
const overflowRef = ref(null);
const counterRef = ref(null);
let isFirstResize = true;
const onResize = () => {
var _a;
if (isFirstResize) {
isFirstResize = false;
} else {
(_a = overflowRef.value) === null || _a === void 0 ? void 0 : _a.sync({
showAllItemsBeforeCalculate: true
});
}
};
function getCounter() {
return document.getElementById(ellipsisNodeId);
}
const ellipsisFromIndexRef = ref(-1);
function onUpdateCount(count) {
ellipsisFromIndexRef.value = props.options.length - count;
}
function onUpdateOverflow(overflow) {
if (!overflow) {
ellipsisFromIndexRef.value = -1;
}
}
const ellipsisOptionRef = computed(() => {
const ellipsisFromIndex = ellipsisFromIndexRef.value;
const option = {
children: ellipsisFromIndex === -1 ? [] : props.options.slice(ellipsisFromIndex)
};
return option;
});
const ellipsisTreeMateRef = computed(() => {
const {
childrenField,
disabledField,
keyField
} = props;
return createTreeMate([ellipsisOptionRef.value], {
getIgnored(node) {
return isIgnoredNode(node);
},
getChildren(node) {
return node[childrenField];
},
getDisabled(node) {
return node[disabledField];
},
getKey(node) {
var _a;
return (_a = node[keyField]) !== null && _a !== void 0 ? _a : node.name;
}
});
});
const emptyTmNodeRef = computed(() => {
return createTreeMate([{}]).treeNodes[0];
});
function renderCounter() {
var _a;
if (ellipsisFromIndexRef.value === -1) {
// Only a placeholder
return h(NSubmenu, {
root: true,
level: 0,
key: "__ellpisisGroupPlaceholder__",
internalKey: "__ellpisisGroupPlaceholder__",
title: "\u00B7\u00B7\u00B7",
tmNode: emptyTmNodeRef.value,
domId: ellipsisNodeId,
isEllipsisPlaceholder: true
});
}
const tmNode = ellipsisTreeMateRef.value.treeNodes[0];
const activePath = activePathRef.value;
const childActive = !!((_a = tmNode.children) === null || _a === void 0 ? void 0 : _a.some(tmNode => {
return activePath.includes(tmNode.key);
}));
return h(NSubmenu, {
level: 0,
root: true,
key: "__ellpisisGroup__",
internalKey: "__ellpisisGroup__",
title: "\u00B7\u00B7\u00B7",
virtualChildActive: childActive,
tmNode: tmNode,
domId: ellipsisNodeId,
rawNodes: tmNode.rawNode.children || [],
tmNodes: tmNode.children || [],
isEllipsisPlaceholder: true
});
}
return {
mergedClsPrefix: mergedClsPrefixRef,
controlledExpandedKeys: controlledExpandedKeysRef,
uncontrolledExpanededKeys: uncontrolledExpandedKeysRef,
mergedExpandedKeys: mergedExpandedKeysRef,
uncontrolledValue: uncontrolledValueRef,
mergedValue: mergedValueRef,
activePath: activePathRef,
tmNodes: tmNodesRef,
mergedTheme: themeRef,
mergedCollapsed: mergedCollapsedRef,
cssVars: inlineThemeDisabled ? undefined : cssVarsRef,
themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass,
overflowRef,
counterRef,
updateCounter: () => {},
onResize,
onUpdateOverflow,
onUpdateCount,
renderCounter,
getCounter,
onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender,
showOption,
deriveResponsiveState: onResize
};
},
render() {
const {
mergedClsPrefix,
mode,
themeClass,
onRender
} = this;
onRender === null || onRender === void 0 ? void 0 : onRender();
const renderMenuItemNodes = () => this.tmNodes.map(tmNode => itemRenderer(tmNode, this.$props));
const horizontal = mode === 'horizontal';
const finalResponsive = horizontal && this.responsive;
const renderMainNode = () => h('div', mergeProps(this.$attrs, {
role: mode === 'horizontal' ? 'menubar' : 'menu',
class: [`${mergedClsPrefix}-menu`, themeClass, `${mergedClsPrefix}-menu--${mode}`, finalResponsive && `${mergedClsPrefix}-menu--responsive`, this.mergedCollapsed && `${mergedClsPrefix}-menu--collapsed`],
style: this.cssVars
}), finalResponsive ? h(VOverflow, {
ref: "overflowRef",
onUpdateOverflow: this.onUpdateOverflow,
getCounter: this.getCounter,
onUpdateCount: this.onUpdateCount,
updateCounter: this.updateCounter,
style: {
width: '100%',
display: 'flex',
overflow: 'hidden'
}
}, {
default: renderMenuItemNodes,
counter: this.renderCounter
}) : renderMenuItemNodes());
return finalResponsive ? h(VResizeObserver, {
onResize: this.onResize
}, {
default: renderMainNode
}) : renderMainNode();
}
});