ant-design-vue
Version:
An enterprise-class UI design language and Vue-based implementation
580 lines (495 loc) • 21.7 kB
JavaScript
var _defineComponent;
import { isVNode as _isVNode, withDirectives as _withDirectives, resolveDirective as _resolveDirective, vShow as _vShow, createVNode as _createVNode } from "vue";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
import { computed, defineComponent, getCurrentInstance, inject, onBeforeUnmount, onMounted, provide, reactive } from 'vue';
import omit from 'omit.js';
import PropTypes from '../_util/vue-types';
import Trigger from '../vc-trigger';
import KeyCode from '../_util/KeyCode';
import SubPopupMenu from './SubPopupMenu';
import placements from './placements';
import BaseMixin from '../_util/BaseMixin';
import { getComponent, splitAttrs, findDOMNode, getSlot } from '../_util/props-util';
import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout';
import { noop, getMenuIdFromSubMenuEventKey, loopMenuItemRecursively } from './util';
import { getTransitionProps, Transition } from '../_util/transition';
import InjectExtraProps from './InjectExtraProps';
function _isSlot(s) {
return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !_isVNode(s);
}
var guid = 0;
var popupPlacementMap = {
horizontal: 'bottomLeft',
vertical: 'rightTop',
'vertical-left': 'rightTop',
'vertical-right': 'leftTop'
};
var updateDefaultActiveFirst = function updateDefaultActiveFirst(store, eventKey, defaultActiveFirst) {
var menuId = getMenuIdFromSubMenuEventKey(eventKey);
store.defaultActiveFirst[menuId] = defaultActiveFirst;
};
var indexGuid = 0;
var SubMenu = defineComponent((_defineComponent = {
name: 'SubMenu',
mixins: [BaseMixin],
inheritAttrs: false,
isSubMenu: true,
props: {
title: PropTypes.any,
openKeys: PropTypes.array.def([]),
openChange: PropTypes.func.def(noop),
rootPrefixCls: PropTypes.string,
eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
multiple: PropTypes.looseBool,
isRootMenu: PropTypes.looseBool.def(false),
index: PropTypes.number,
triggerSubMenuAction: PropTypes.string,
popupClassName: PropTypes.string,
getPopupContainer: PropTypes.func,
forceSubMenuRender: PropTypes.looseBool.def(false),
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
disabled: PropTypes.looseBool,
subMenuOpenDelay: PropTypes.number.def(0.1),
subMenuCloseDelay: PropTypes.number.def(0.1),
level: PropTypes.number.def(1),
inlineIndent: PropTypes.number.def(24),
openTransitionName: PropTypes.string,
popupOffset: PropTypes.array,
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
manualRef: PropTypes.func.def(noop),
builtinPlacements: PropTypes.object.def(function () {
return {};
}),
itemIcon: PropTypes.any,
expandIcon: PropTypes.any,
subMenuKey: PropTypes.string,
theme: PropTypes.string,
parentUniKeys: PropTypes.array.def(function () {
return [];
}),
parentUniKey: PropTypes.string,
isOverflowedSubMenu: PropTypes.looseBool.def(false)
}
}, _defineProperty(_defineComponent, "isSubMenu", true), _defineProperty(_defineComponent, "setup", function setup(props) {
var uniKey = props.isOverflowedSubMenu ? 'MENUITEM_OVERFLOWED_UNI_KEY' : "sub_menu_".concat(++indexGuid);
var store = inject('menuStore', function () {
return {};
});
onMounted(function () {
store.addChildrenInfo(uniKey, computed(function () {
return {
parentUniKeys: props.parentUniKeys,
parentUniKey: props.parentUniKey,
eventKey: props.eventKey,
disabled: props.disabled
};
}));
});
onBeforeUnmount(function () {
store.removeChildrenInfo(uniKey);
});
var isChildrenSelected = computed(function () {
return store.selectedParentUniKeys.indexOf(uniKey) !== -1;
});
var ins = getCurrentInstance();
var getEl = function getEl() {
return ins.vnode.el;
};
provide('parentMenu', reactive({
isRootMenu: computed(function () {
return props.isRootMenu;
}),
getPopupContainer: props.getPopupContainer,
getEl: getEl
}));
return {
parentMenu: inject('parentMenu', undefined),
store: store,
isChildrenSelected: isChildrenSelected,
childrenUniKeys: [].concat(_toConsumableArray(props.parentUniKeys), [uniKey]),
uniKey: uniKey,
isOpen: computed(function () {
return store.openKeys.indexOf(props.eventKey) > -1;
}),
active: computed(function () {
return store.activeKey[props.subMenuKey] === props.eventKey;
})
};
}), _defineProperty(_defineComponent, "data", function data() {
var props = this.$props;
var store = this.store;
var eventKey = props.eventKey;
var defaultActiveFirst = store.defaultActiveFirst;
var value = false;
if (defaultActiveFirst) {
value = defaultActiveFirst[eventKey];
}
updateDefaultActiveFirst(store, eventKey, value);
this.internalMenuId = undefined;
this.haveRendered = undefined;
this.haveOpened = undefined;
this.subMenuTitle = undefined;
return {};
}), _defineProperty(_defineComponent, "mounted", function mounted() {
var _this = this;
this.$nextTick(function () {
_this.handleUpdated();
});
}), _defineProperty(_defineComponent, "updated", function updated() {
var _this2 = this;
this.$nextTick(function () {
_this2.handleUpdated();
});
}), _defineProperty(_defineComponent, "beforeUnmount", function beforeUnmount() {
var eventKey = this.eventKey;
this.__emit('destroy', eventKey);
/* istanbul ignore if */
if (this.minWidthTimeout) {
cancelAnimationTimeout(this.minWidthTimeout);
this.minWidthTimeout = null;
}
/* istanbul ignore if */
if (this.mouseenterTimeout) {
cancelAnimationTimeout(this.mouseenterTimeout);
this.mouseenterTimeout = null;
}
}), _defineProperty(_defineComponent, "methods", {
isChildrenSelected2: function isChildrenSelected2() {
if (this.haveOpened) return this.isChildrenSelected;
var ret = {
find: false
};
loopMenuItemRecursively(getSlot(this), this.store.selectedKeys, ret);
return ret.find;
},
handleUpdated: function handleUpdated() {
var _this3 = this;
var _this$$props = this.$props,
mode = _this$$props.mode,
manualRef = _this$$props.manualRef; // invoke customized ref to expose component to mixin
if (manualRef) {
manualRef(this);
}
if (mode !== 'horizontal' || !this.parentMenu.isRootMenu || !this.isOpen) {
return;
}
this.minWidthTimeout = requestAnimationTimeout(function () {
return _this3.adjustWidth();
}, 0);
},
onKeyDown: function onKeyDown(e) {
var keyCode = e.keyCode;
var menu = this.menuInstance;
var isOpen = this.isOpen;
if (keyCode === KeyCode.ENTER) {
this.onTitleClick(e);
updateDefaultActiveFirst(this.store, this.$props.eventKey, true);
return true;
}
if (keyCode === KeyCode.RIGHT) {
if (isOpen) {
menu.onKeyDown(e);
} else {
this.triggerOpenChange(true); // need to update current menu's defaultActiveFirst value
updateDefaultActiveFirst(this.store, this.$props.eventKey, true);
}
return true;
}
if (keyCode === KeyCode.LEFT) {
var handled;
if (isOpen) {
handled = menu.onKeyDown(e);
} else {
return undefined;
}
if (!handled) {
this.triggerOpenChange(false);
handled = true;
}
return handled;
}
if (isOpen && (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN)) {
return menu.onKeyDown(e);
}
return undefined;
},
onPopupVisibleChange: function onPopupVisibleChange(visible) {
this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave');
},
onMouseEnter: function onMouseEnter(e) {
var key = this.$props.eventKey;
updateDefaultActiveFirst(this.store, key, false);
this.__emit('mouseenter', {
key: key,
domEvent: e
});
},
onMouseLeave: function onMouseLeave(e) {
var eventKey = this.$props.eventKey;
this.__emit('mouseleave', {
key: eventKey,
domEvent: e
});
},
onTitleMouseEnter: function onTitleMouseEnter(domEvent) {
var key = this.$props.eventKey;
this.__emit('itemHover', {
key: key,
hover: true
});
this.__emit('titleMouseenter', {
key: key,
domEvent: domEvent
});
},
onTitleMouseLeave: function onTitleMouseLeave(e) {
var eventKey = this.$props.eventKey;
this.__emit('itemHover', {
key: eventKey,
hover: false
});
this.__emit('titleMouseleave', {
key: eventKey,
domEvent: e
});
},
onTitleClick: function onTitleClick(e) {
var _this$$props2 = this.$props,
triggerSubMenuAction = _this$$props2.triggerSubMenuAction,
eventKey = _this$$props2.eventKey;
this.__emit('titleClick', {
key: eventKey,
domEvent: e
});
if (triggerSubMenuAction === 'hover') {
return;
}
this.triggerOpenChange(!this.isOpen, 'click');
updateDefaultActiveFirst(this.store, eventKey, false);
},
onSubMenuClick: function onSubMenuClick(info) {
this.__emit('click', this.addKeyPath(info));
},
getPrefixCls: function getPrefixCls() {
return "".concat(this.$props.rootPrefixCls, "-submenu");
},
getActiveClassName: function getActiveClassName() {
return "".concat(this.getPrefixCls(), "-active");
},
getDisabledClassName: function getDisabledClassName() {
return "".concat(this.getPrefixCls(), "-disabled");
},
getSelectedClassName: function getSelectedClassName() {
return "".concat(this.getPrefixCls(), "-selected");
},
getOpenClassName: function getOpenClassName() {
return "".concat(this.$props.rootPrefixCls, "-submenu-open");
},
saveMenuInstance: function saveMenuInstance(c) {
// children menu instance
this.menuInstance = c;
},
addKeyPath: function addKeyPath(info) {
return _extends(_extends({}, info), {
keyPath: (info.keyPath || []).concat(this.$props.eventKey)
});
},
triggerOpenChange: function triggerOpenChange(open, type) {
var _this4 = this;
var key = this.$props.eventKey;
var openChange = function openChange() {
_this4.__emit('openChange', {
key: key,
item: _this4.$props,
trigger: type,
open: open
});
};
if (type === 'mouseenter') {
// make sure mouseenter happen after other menu item's mouseleave
this.mouseenterTimeout = requestAnimationTimeout(function () {
openChange();
}, 0);
} else {
openChange();
}
},
adjustWidth: function adjustWidth() {
/* istanbul ignore if */
if (!this.subMenuTitle || !this.menuInstance) {
return;
}
var popupMenu = findDOMNode(this.menuInstance);
if (popupMenu.offsetWidth >= this.subMenuTitle.offsetWidth) {
return;
}
/* istanbul ignore next */
popupMenu.style.minWidth = "".concat(this.subMenuTitle.offsetWidth, "px");
},
saveSubMenuTitle: function saveSubMenuTitle(subMenuTitle) {
this.subMenuTitle = subMenuTitle;
},
renderChildren: function renderChildren() {
var _slot;
var props = _extends(_extends({}, this.$props), this.$attrs);
var subPopupMenuProps = {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
visible: this.isOpen,
level: props.level + 1,
inlineIndent: props.inlineIndent,
focusable: false,
eventKey: "".concat(props.eventKey, "-menu-"),
openKeys: props.openKeys,
openTransitionName: props.openTransitionName,
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction,
builtinPlacements: props.builtinPlacements,
multiple: props.multiple,
prefixCls: props.rootPrefixCls,
manualRef: this.saveMenuInstance,
itemIcon: getComponent(this, 'itemIcon'),
expandIcon: getComponent(this, 'expandIcon'),
onClick: this.onSubMenuClick,
onSelect: props.onSelect || noop,
onDeselect: props.onDeselect || noop,
onOpenChange: props.onOpenChange || noop,
id: this.internalMenuId,
parentUniKeys: this.childrenUniKeys,
parentUniKey: this.uniKey
};
var haveRendered = this.haveRendered;
this.haveRendered = true;
this.haveOpened = this.haveOpened || subPopupMenuProps.visible || subPopupMenuProps.forceSubMenuRender; // never rendered not planning to, don't render
if (!this.haveOpened) {
return _createVNode("div", null, null);
} // don't show transition on first rendering (no animation for opened menu)
// show appear transition if it's not visible (not sure why)
// show appear transition if it's not inline mode
var transitionAppear = haveRendered || !subPopupMenuProps.visible || !subPopupMenuProps.mode === 'inline';
subPopupMenuProps.class = " ".concat(subPopupMenuProps.prefixCls, "-sub");
var transitionProps = {
appear: transitionAppear,
css: false
};
if (subPopupMenuProps.openTransitionName) {
transitionProps = getTransitionProps(subPopupMenuProps.openTransitionName, {
appear: transitionAppear
});
} else if (_typeof(subPopupMenuProps.openAnimation) === 'object') {
transitionProps = _extends(_extends({}, transitionProps), subPopupMenuProps.openAnimation || {});
if (!transitionAppear) {
transitionProps.appear = false;
}
} else if (typeof subPopupMenuProps.openAnimation === 'string') {
transitionProps = getTransitionProps(subPopupMenuProps.openAnimation, {
appear: transitionAppear
});
}
return _createVNode(Transition, transitionProps, _isSlot(_slot = _withDirectives(_createVNode(SubPopupMenu, subPopupMenuProps, _objectSpread({}, this.$slots)), [[_vShow, this.isOpen]])) ? _slot : {
default: function _default() {
return [_slot];
}
});
}
}), _defineProperty(_defineComponent, "render", function render() {
var _className, _createVNode2;
var props = _extends(_extends({}, this.$props), this.$attrs);
var _splitAttrs = splitAttrs(props),
onEvents = _splitAttrs.onEvents;
var isOpen = this.isOpen;
var prefixCls = this.getPrefixCls();
var isInlineMode = props.mode === 'inline';
if (!this.internalMenuId) {
if (props.eventKey) {
this.internalMenuId = "".concat(props.eventKey, "$Menu");
} else {
this.internalMenuId = "$__$".concat(++guid, "$Menu");
}
}
var children = this.renderChildren();
var className = (_className = {}, _defineProperty(_className, prefixCls, true), _defineProperty(_className, "".concat(prefixCls, "-").concat(props.mode), true), _defineProperty(_className, props.class, !!props.class), _defineProperty(_className, this.getOpenClassName(), isOpen), _defineProperty(_className, this.getActiveClassName(), this.active || isOpen && !isInlineMode), _defineProperty(_className, this.getDisabledClassName(), props.disabled), _defineProperty(_className, this.getSelectedClassName(), this.isChildrenSelected || this.isChildrenSelected2()), _className);
var mouseEvents = {};
var titleClickEvents = {};
var titleMouseEvents = {};
if (!props.disabled) {
mouseEvents = {
onMouseleave: this.onMouseLeave,
onMouseenter: this.onMouseEnter
}; // only works in title, not outer li
titleClickEvents = {
onClick: this.onTitleClick
};
titleMouseEvents = {
onMouseenter: this.onTitleMouseEnter,
onMouseleave: this.onTitleMouseLeave
};
}
var style = {};
if (isInlineMode) {
style.paddingLeft = "".concat(props.inlineIndent * props.level, "px");
}
var ariaOwns = {}; // only set aria-owns when menu is open
// otherwise it would be an invalid aria-owns value
// since corresponding node cannot be found
if (isOpen) {
ariaOwns = {
'aria-owns': this.internalMenuId
};
}
var titleProps = _extends(_extends(_extends(_extends(_extends({
'aria-expanded': isOpen
}, ariaOwns), {
'aria-haspopup': 'true',
title: typeof props.title === 'string' ? props.title : undefined
}), titleMouseEvents), titleClickEvents), {
style: style,
class: "".concat(prefixCls, "-title"),
ref: this.saveSubMenuTitle
}); // expand custom icon should NOT be displayed in menu with horizontal mode.
var icon = null;
if (props.mode !== 'horizontal') {
icon = getComponent(this, 'expandIcon', props);
}
var title = _createVNode("div", titleProps, [getComponent(this, 'title'), icon || _createVNode("i", {
"class": "".concat(prefixCls, "-arrow")
}, null)]);
var getPopupContainer = this.parentMenu.isRootMenu ? this.parentMenu.getPopupContainer : function (triggerNode) {
return triggerNode.parentNode;
};
var popupPlacement = popupPlacementMap[props.mode];
var popupAlign = props.popupOffset ? {
offset: props.popupOffset
} : {};
var popupClassName = props.mode === 'inline' ? '' : props.popupClassName || '';
popupClassName = "".concat(prefixCls, "-popup ").concat(popupClassName);
var liProps = _extends(_extends(_extends({}, omit(onEvents, ['onClick'])), mouseEvents), {
class: className,
style: props.style
});
return _createVNode("li", _objectSpread(_objectSpread({}, liProps), {}, {
"role": "menuitem"
}), [isInlineMode && title, isInlineMode && children, !isInlineMode && _createVNode(Trigger, (_createVNode2 = {
"prefixCls": prefixCls,
"popupClassName": popupClassName,
"getPopupContainer": getPopupContainer,
"builtinPlacements": placements
}, _defineProperty(_createVNode2, "builtinPlacements", _extends({}, placements, props.builtinPlacements)), _defineProperty(_createVNode2, "popupPlacement", popupPlacement), _defineProperty(_createVNode2, "popupVisible", isOpen), _defineProperty(_createVNode2, "popupAlign", popupAlign), _defineProperty(_createVNode2, "action", props.disabled ? [] : [props.triggerSubMenuAction]), _defineProperty(_createVNode2, "mouseEnterDelay", props.subMenuOpenDelay), _defineProperty(_createVNode2, "mouseLeaveDelay", props.subMenuCloseDelay), _defineProperty(_createVNode2, "onPopupVisibleChange", this.onPopupVisibleChange), _defineProperty(_createVNode2, "forceRender", props.forceSubMenuRender), _defineProperty(_createVNode2, "popup", children), _createVNode2), _isSlot(title) ? title : {
default: function _default() {
return [title];
}
})]);
}), _defineComponent));
export default InjectExtraProps(SubMenu);