ant-design-vue
Version:
An enterprise-class UI design language and Vue-based implementation
518 lines (473 loc) • 17.3 kB
JavaScript
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _extends from "@babel/runtime/helpers/esm/extends";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import { Fragment as _Fragment, createVNode as _createVNode, resolveDirective as _resolveDirective } from "vue";
import { computed, defineComponent, ref, inject, watchEffect, watch, onMounted, unref } from 'vue';
import shallowEqual from '../../_util/shallowequal';
import useProvideMenu, { MenuContextProvider, useProvideFirstLevel } from './hooks/useMenuContext';
import useConfigInject from '../../_util/hooks/useConfigInject';
import devWarning from '../../vc-util/devWarning';
import uniq from 'lodash-es/uniq';
import { SiderCollapsedKey } from '../../layout/injectionKey';
import { flattenChildren } from '../../_util/props-util';
import Overflow from '../../vc-overflow';
import MenuItem from './MenuItem';
import SubMenu from './SubMenu';
import EllipsisOutlined from "@ant-design/icons-vue/es/icons/EllipsisOutlined";
import { cloneElement } from '../../_util/vnode';
import { OVERFLOW_KEY, PathContext } from './hooks/useKeyPath';
import collapseMotion from '../../_util/collapseMotion';
export var menuProps = function menuProps() {
return {
id: String,
prefixCls: String,
disabled: Boolean,
inlineCollapsed: Boolean,
disabledOverflow: Boolean,
forceSubMenuRender: Boolean,
openKeys: Array,
selectedKeys: Array,
activeKey: String,
selectable: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: false
},
motion: Object,
theme: {
type: String,
default: 'light'
},
mode: {
type: String,
default: 'vertical'
},
inlineIndent: {
type: Number,
default: 24
},
subMenuOpenDelay: {
type: Number,
default: 0.1
},
subMenuCloseDelay: {
type: Number,
default: 0.1
},
builtinPlacements: {
type: Object
},
triggerSubMenuAction: {
type: String,
default: 'hover'
},
getPopupContainer: Function,
expandIcon: Function,
onOpenChange: Function,
onSelect: Function,
onDeselect: Function,
onClick: [Function, Array],
onFocus: Function,
onBlur: Function,
onMousedown: Function,
'onUpdate:openKeys': Function,
'onUpdate:selectedKeys': Function,
'onUpdate:activeKey': Function
};
};
var EMPTY_LIST = [];
export default defineComponent({
name: 'AMenu',
inheritAttrs: false,
props: menuProps(),
slots: ['expandIcon', 'overflowedIndicator'],
setup: function setup(props, _ref) {
var slots = _ref.slots,
emit = _ref.emit,
attrs = _ref.attrs;
var _useConfigInject = useConfigInject('menu', props),
prefixCls = _useConfigInject.prefixCls,
direction = _useConfigInject.direction,
getPrefixCls = _useConfigInject.getPrefixCls;
var store = ref({});
var siderCollapsed = inject(SiderCollapsedKey, ref(undefined));
var inlineCollapsed = computed(function () {
if (siderCollapsed.value !== undefined) {
return siderCollapsed.value;
}
return props.inlineCollapsed;
});
var isMounted = ref(false);
onMounted(function () {
isMounted.value = true;
});
watchEffect(function () {
devWarning(!(props.inlineCollapsed === true && props.mode !== 'inline'), 'Menu', '`inlineCollapsed` should only be used when `mode` is inline.');
devWarning(!(siderCollapsed.value !== undefined && props.inlineCollapsed === true), 'Menu', '`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead.');
});
var activeKeys = ref([]);
var mergedSelectedKeys = ref([]);
var keyMapStore = ref({});
watch(store, function () {
var newKeyMapStore = {};
for (var _i = 0, _Object$values = Object.values(store.value); _i < _Object$values.length; _i++) {
var menuInfo = _Object$values[_i];
newKeyMapStore[menuInfo.key] = menuInfo;
}
keyMapStore.value = newKeyMapStore;
}, {
flush: 'post'
});
watchEffect(function () {
if (props.activeKey !== undefined) {
var keys = [];
var menuInfo = props.activeKey ? keyMapStore.value[props.activeKey] : undefined;
if (menuInfo && props.activeKey !== undefined) {
keys = uniq([].concat(unref(menuInfo.parentKeys), props.activeKey));
} else {
keys = [];
}
if (!shallowEqual(activeKeys.value, keys)) {
activeKeys.value = keys;
}
}
});
watch(function () {
return props.selectedKeys;
}, function (selectedKeys) {
if (selectedKeys) {
mergedSelectedKeys.value = selectedKeys.slice();
}
}, {
immediate: true,
deep: true
});
var selectedSubMenuKeys = ref([]);
watch([keyMapStore, mergedSelectedKeys], function () {
var subMenuParentKeys = [];
mergedSelectedKeys.value.forEach(function (key) {
var menuInfo = keyMapStore.value[key];
if (menuInfo) {
subMenuParentKeys = subMenuParentKeys.concat(unref(menuInfo.parentKeys));
}
});
subMenuParentKeys = uniq(subMenuParentKeys);
if (!shallowEqual(selectedSubMenuKeys.value, subMenuParentKeys)) {
selectedSubMenuKeys.value = subMenuParentKeys;
}
}, {
immediate: true
}); // >>>>> Trigger select
var triggerSelection = function triggerSelection(info) {
if (!props.selectable) {
return;
} // Insert or Remove
var targetKey = info.key;
var exist = mergedSelectedKeys.value.includes(targetKey);
var newSelectedKeys;
if (props.multiple) {
if (exist) {
newSelectedKeys = mergedSelectedKeys.value.filter(function (key) {
return key !== targetKey;
});
} else {
newSelectedKeys = [].concat(_toConsumableArray(mergedSelectedKeys.value), [targetKey]);
}
} else {
newSelectedKeys = [targetKey];
} // Trigger event
var selectInfo = _extends(_extends({}, info), {
selectedKeys: newSelectedKeys
});
if (!shallowEqual(newSelectedKeys, mergedSelectedKeys.value)) {
if (props.selectedKeys === undefined) {
mergedSelectedKeys.value = newSelectedKeys;
}
emit('update:selectedKeys', newSelectedKeys);
if (exist && props.multiple) {
emit('deselect', selectInfo);
} else {
emit('select', selectInfo);
}
}
if (mergedMode.value !== 'inline' && !props.multiple && mergedOpenKeys.value.length) {
triggerOpenKeys(EMPTY_LIST);
}
};
var mergedOpenKeys = ref([]);
watch(function () {
return props.openKeys;
}, function () {
var openKeys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : mergedOpenKeys.value;
if (!shallowEqual(mergedOpenKeys.value, openKeys)) {
mergedOpenKeys.value = openKeys.slice();
}
}, {
immediate: true,
deep: true
});
var timeout;
var changeActiveKeys = function changeActiveKeys(keys) {
clearTimeout(timeout);
timeout = setTimeout(function () {
if (props.activeKey === undefined) {
activeKeys.value = keys;
}
emit('update:activeKey', keys[keys.length - 1]);
});
};
var disabled = computed(function () {
return !!props.disabled;
});
var isRtl = computed(function () {
return direction.value === 'rtl';
});
var mergedMode = ref('vertical');
var mergedInlineCollapsed = ref(false);
watchEffect(function () {
if ((props.mode === 'inline' || props.mode === 'vertical') && inlineCollapsed.value) {
mergedMode.value = 'vertical';
mergedInlineCollapsed.value = inlineCollapsed.value;
} else {
mergedMode.value = props.mode;
mergedInlineCollapsed.value = false;
}
});
var isInlineMode = computed(function () {
return mergedMode.value === 'inline';
});
var triggerOpenKeys = function triggerOpenKeys(keys) {
mergedOpenKeys.value = keys;
emit('update:openKeys', keys);
emit('openChange', keys);
}; // >>>>> Cache & Reset open keys when inlineCollapsed changed
var inlineCacheOpenKeys = ref(mergedOpenKeys.value);
var mountRef = ref(false); // Cache
watch(mergedOpenKeys, function () {
if (isInlineMode.value) {
inlineCacheOpenKeys.value = mergedOpenKeys.value;
}
}, {
immediate: true
}); // Restore
watch(isInlineMode, function () {
if (!mountRef.value) {
mountRef.value = true;
return;
}
if (isInlineMode.value) {
mergedOpenKeys.value = inlineCacheOpenKeys.value;
} else {
// Trigger open event in case its in control
triggerOpenKeys(EMPTY_LIST);
}
}, {
immediate: true
});
var className = computed(function () {
var _ref2;
return _ref2 = {}, _defineProperty(_ref2, "".concat(prefixCls.value), true), _defineProperty(_ref2, "".concat(prefixCls.value, "-root"), true), _defineProperty(_ref2, "".concat(prefixCls.value, "-").concat(mergedMode.value), true), _defineProperty(_ref2, "".concat(prefixCls.value, "-inline-collapsed"), mergedInlineCollapsed.value), _defineProperty(_ref2, "".concat(prefixCls.value, "-rtl"), isRtl.value), _defineProperty(_ref2, "".concat(prefixCls.value, "-").concat(props.theme), true), _ref2;
});
var rootPrefixCls = computed(function () {
return getPrefixCls();
});
var defaultMotions = computed(function () {
return {
horizontal: {
name: "".concat(rootPrefixCls.value, "-slide-up")
},
inline: collapseMotion,
other: {
name: "".concat(rootPrefixCls.value, "-zoom-big")
}
};
});
useProvideFirstLevel(true);
var getChildrenKeys = function getChildrenKeys() {
var eventKeys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var keys = [];
var storeValue = store.value;
eventKeys.forEach(function (eventKey) {
var _storeValue$eventKey = storeValue[eventKey],
key = _storeValue$eventKey.key,
childrenEventKeys = _storeValue$eventKey.childrenEventKeys;
keys.push.apply(keys, [key].concat(_toConsumableArray(getChildrenKeys(childrenEventKeys))));
});
return keys;
}; // ========================= Open =========================
/**
* Click for item. SubMenu do not have selection status
*/
var onInternalClick = function onInternalClick(info) {
emit('click', info);
triggerSelection(info);
};
var onInternalOpenChange = function onInternalOpenChange(key, open) {
var childrenEventKeys = keyMapStore.value[key].childrenEventKeys;
var newOpenKeys = mergedOpenKeys.value.filter(function (k) {
return k !== key;
});
if (open) {
newOpenKeys.push(key);
} else if (mergedMode.value !== 'inline') {
// We need find all related popup to close
var subPathKeys = getChildrenKeys(childrenEventKeys);
newOpenKeys = uniq(newOpenKeys.filter(function (k) {
return !subPathKeys.includes(k);
}));
}
if (!shallowEqual(mergedOpenKeys, newOpenKeys)) {
triggerOpenKeys(newOpenKeys);
}
};
var registerMenuInfo = function registerMenuInfo(key, info) {
store.value = _extends(_extends({}, store.value), _defineProperty({}, key, info));
};
var unRegisterMenuInfo = function unRegisterMenuInfo(key) {
delete store.value[key];
store.value = _extends({}, store.value);
};
var lastVisibleIndex = ref(0);
var expandIcon = computed(function () {
return props.expandIcon || slots.expandIcon ? function (opt) {
var icon = props.expandIcon || slots.expandIcon;
icon = typeof icon === 'function' ? icon(opt) : icon;
return cloneElement(icon, {
class: "".concat(prefixCls.value, "-submenu-expand-icon")
}, false);
} : null;
});
useProvideMenu({
store: store,
prefixCls: prefixCls,
activeKeys: activeKeys,
openKeys: mergedOpenKeys,
selectedKeys: mergedSelectedKeys,
changeActiveKeys: changeActiveKeys,
disabled: disabled,
rtl: isRtl,
mode: mergedMode,
inlineIndent: computed(function () {
return props.inlineIndent;
}),
subMenuCloseDelay: computed(function () {
return props.subMenuCloseDelay;
}),
subMenuOpenDelay: computed(function () {
return props.subMenuOpenDelay;
}),
builtinPlacements: computed(function () {
return props.builtinPlacements;
}),
triggerSubMenuAction: computed(function () {
return props.triggerSubMenuAction;
}),
getPopupContainer: computed(function () {
return props.getPopupContainer;
}),
inlineCollapsed: mergedInlineCollapsed,
antdMenuTheme: computed(function () {
return props.theme;
}),
siderCollapsed: siderCollapsed,
defaultMotions: computed(function () {
return isMounted.value ? defaultMotions.value : null;
}),
motion: computed(function () {
return isMounted.value ? props.motion : null;
}),
overflowDisabled: ref(undefined),
onOpenChange: onInternalOpenChange,
onItemClick: onInternalClick,
registerMenuInfo: registerMenuInfo,
unRegisterMenuInfo: unRegisterMenuInfo,
selectedSubMenuKeys: selectedSubMenuKeys,
isRootMenu: ref(true),
expandIcon: expandIcon,
forceSubMenuRender: computed(function () {
return props.forceSubMenuRender;
})
});
return function () {
var _a, _b;
var childList = flattenChildren((_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots));
var allVisible = lastVisibleIndex.value >= childList.length - 1 || mergedMode.value !== 'horizontal' || props.disabledOverflow; // >>>>> Children
var wrappedChildList = mergedMode.value !== 'horizontal' || props.disabledOverflow ? childList : // Need wrap for overflow dropdown that do not response for open
childList.map(function (child, index) {
return (// Always wrap provider to avoid sub node re-mount
_createVNode(MenuContextProvider, {
"key": child.key,
"overflowDisabled": index > lastVisibleIndex.value
}, {
default: function _default() {
return child;
}
})
);
});
var overflowedIndicator = ((_b = slots.overflowedIndicator) === null || _b === void 0 ? void 0 : _b.call(slots)) || _createVNode(EllipsisOutlined, null, null);
return _createVNode(_Fragment, null, [_createVNode(Overflow, _objectSpread(_objectSpread({}, attrs), {}, {
"onMousedown": props.onMousedown,
"prefixCls": "".concat(prefixCls.value, "-overflow"),
"component": "ul",
"itemComponent": MenuItem,
"class": [className.value, attrs.class],
"role": "menu",
"id": props.id,
"data": wrappedChildList,
"renderRawItem": function renderRawItem(node) {
return node;
},
"renderRawRest": function renderRawRest(omitItems) {
// We use origin list since wrapped list use context to prevent open
var len = omitItems.length;
var originOmitItems = len ? childList.slice(-len) : null;
return _createVNode(_Fragment, null, [_createVNode(SubMenu, {
"eventKey": OVERFLOW_KEY,
"key": OVERFLOW_KEY,
"title": overflowedIndicator,
"disabled": allVisible,
"internalPopupClose": len === 0
}, {
default: function _default() {
return originOmitItems;
}
}), _createVNode(PathContext, null, {
default: function _default() {
return [_createVNode(SubMenu, {
"eventKey": OVERFLOW_KEY,
"key": OVERFLOW_KEY,
"title": overflowedIndicator,
"disabled": allVisible,
"internalPopupClose": len === 0
}, {
default: function _default() {
return originOmitItems;
}
})];
}
})]);
},
"maxCount": mergedMode.value !== 'horizontal' || props.disabledOverflow ? Overflow.INVALIDATE : Overflow.RESPONSIVE,
"ssr": "full",
"data-menu-list": true,
"onVisibleChange": function onVisibleChange(newLastIndex) {
lastVisibleIndex.value = newLastIndex;
}
}), null), _createVNode("div", {
"style": {
display: 'none'
},
"aria-hidden": true
}, [_createVNode(PathContext, null, {
default: function _default() {
return [wrappedChildList];
}
})])]);
};
}
});