feat: update menu
parent
75e1661c16
commit
4311c15755
|
@ -29,3 +29,9 @@ v-model -> v-model:visible
|
||||||
## Tooltip
|
## Tooltip
|
||||||
|
|
||||||
v-model -> v-model:visible
|
v-model -> v-model:visible
|
||||||
|
|
||||||
|
## Modal
|
||||||
|
|
||||||
|
v-model -> v-model:visible
|
||||||
|
|
||||||
|
okButtonProps、cancelButtonProps 扁平化处理
|
||||||
|
|
|
@ -54,12 +54,12 @@ function animate(node, show, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const animation = {
|
const animation = {
|
||||||
enter(node, done) {
|
onEnter(node, done) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
animate(node, true, done);
|
animate(node, true, done);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
leave(node, done) {
|
onLeave(node, done) {
|
||||||
return animate(node, false, done);
|
return animate(node, false, done);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,6 +118,7 @@ const getOptionProps = instance => {
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
const getComponent = (instance, prop, options = instance, execute = true) => {
|
const getComponent = (instance, prop, options = instance, execute = true) => {
|
||||||
|
if (instance.$) {
|
||||||
const temp = instance[prop];
|
const temp = instance[prop];
|
||||||
if (temp !== undefined) {
|
if (temp !== undefined) {
|
||||||
return typeof temp === 'function' && execute ? temp(options) : temp;
|
return typeof temp === 'function' && execute ? temp(options) : temp;
|
||||||
|
@ -126,6 +127,17 @@ const getComponent = (instance, prop, options = instance, execute = true) => {
|
||||||
com = execute && com ? com(options) : com;
|
com = execute && com ? com(options) : com;
|
||||||
return Array.isArray(com) && com.length === 1 ? com[0] : com;
|
return Array.isArray(com) && com.length === 1 ? com[0] : com;
|
||||||
}
|
}
|
||||||
|
} else if (isVNode(instance)) {
|
||||||
|
const temp = instance.props && instance.props[prop];
|
||||||
|
if (temp !== undefined) {
|
||||||
|
return typeof temp === 'function' && execute ? temp(options) : temp;
|
||||||
|
} else if (instance.children && instance.children[name]) {
|
||||||
|
let com = instance.children[prop];
|
||||||
|
com = execute && com ? com(options) : com;
|
||||||
|
return Array.isArray(com) && com.length === 1 ? com[0] : com;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
const getComponentFromProp = (instance, prop, options = instance, execute = true) => {
|
const getComponentFromProp = (instance, prop, options = instance, execute = true) => {
|
||||||
if (instance.$createElement) {
|
if (instance.$createElement) {
|
||||||
|
@ -169,13 +181,13 @@ const getComponentFromProp = (instance, prop, options = instance, execute = true
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllProps = ele => {
|
const getAllProps = ele => {
|
||||||
let data = ele.data || {};
|
let props = getOptionProps(ele);
|
||||||
let componentOptions = ele.componentOptions || {};
|
if (ele.$) {
|
||||||
if (ele.$vnode) {
|
props = { ...props, ...this.$attrs };
|
||||||
data = ele.$vnode.data || {};
|
} else {
|
||||||
componentOptions = ele.$vnode.componentOptions || {};
|
props = { ...props, ...ele.props };
|
||||||
}
|
}
|
||||||
return { ...data.props, ...data.attrs, ...componentOptions.propsData };
|
return props;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 使用 getOptionProps 替换 ,待测试
|
// 使用 getOptionProps 替换 ,待测试
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { createVNode } from 'vue';
|
||||||
import PropTypes from './vue-types';
|
import PropTypes from './vue-types';
|
||||||
import { getOptionProps, getListeners } from './props-util';
|
import { getOptionProps } from './props-util';
|
||||||
|
|
||||||
function getDisplayName(WrappedComponent) {
|
function getDisplayName(WrappedComponent) {
|
||||||
return WrappedComponent.name || 'Component';
|
return WrappedComponent.name || 'Component';
|
||||||
|
@ -15,6 +16,7 @@ export default function wrapWithConnect(WrappedComponent) {
|
||||||
WrappedComponent.props.children = PropTypes.array.def([]);
|
WrappedComponent.props.children = PropTypes.array.def([]);
|
||||||
const ProxyWrappedComponent = {
|
const ProxyWrappedComponent = {
|
||||||
props,
|
props,
|
||||||
|
inheritAttrs: false,
|
||||||
model: WrappedComponent.model,
|
model: WrappedComponent.model,
|
||||||
name: `Proxy_${getDisplayName(WrappedComponent)}`,
|
name: `Proxy_${getDisplayName(WrappedComponent)}`,
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -23,30 +25,21 @@ export default function wrapWithConnect(WrappedComponent) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { $slots = {}, $scopedSlots } = this;
|
const { $slots = {} } = this;
|
||||||
const props = getOptionProps(this);
|
const props = getOptionProps(this);
|
||||||
const wrapProps = {
|
const wrapProps = {
|
||||||
props: {
|
|
||||||
...props,
|
...props,
|
||||||
__propsSymbol__: Symbol(),
|
__propsSymbol__: Symbol(),
|
||||||
componentWillReceiveProps: { ...props },
|
componentWillReceiveProps: { ...props },
|
||||||
children: $slots.default || props.children || [],
|
children: props.children || $slots?.default() || [],
|
||||||
},
|
slots: $slots,
|
||||||
on: getListeners(this),
|
ref: 'wrappedInstance',
|
||||||
};
|
};
|
||||||
if (Object.keys($scopedSlots).length) {
|
return createVNode(WrappedComponent, wrapProps);
|
||||||
wrapProps.scopedSlots = $scopedSlots;
|
// return (
|
||||||
}
|
// <WrappedComponent {...wrapProps} ref="wrappedInstance">
|
||||||
const slotsKey = Object.keys($slots);
|
// </WrappedComponent>
|
||||||
return (
|
// );
|
||||||
<WrappedComponent {...wrapProps} ref="wrappedInstance">
|
|
||||||
{slotsKey.length
|
|
||||||
? slotsKey.map(name => {
|
|
||||||
return <template slot={name}>{$slots[name]}</template>;
|
|
||||||
})
|
|
||||||
: null}
|
|
||||||
</WrappedComponent>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Object.keys(methods).map(m => {
|
Object.keys(methods).map(m => {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
import { provide } from 'vue';
|
||||||
import { storeShape } from './PropTypes';
|
import { storeShape } from './PropTypes';
|
||||||
|
import { getSlot } from '../props-util';
|
||||||
export default {
|
export default {
|
||||||
name: 'StoreProvider',
|
name: 'StoreProvider',
|
||||||
props: {
|
props: {
|
||||||
store: storeShape.isRequired,
|
store: storeShape.isRequired,
|
||||||
},
|
},
|
||||||
provide() {
|
created() {
|
||||||
return {
|
provide('storeContext', this.$props);
|
||||||
storeContext: this.$props,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return this.$slots.default[0];
|
return getSlot(this);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import shallowEqual from 'shallowequal';
|
import shallowEqual from 'shallowequal';
|
||||||
|
import { inject, createVNode } from 'vue';
|
||||||
import omit from 'omit.js';
|
import omit from 'omit.js';
|
||||||
import { getOptionProps, getListeners } from '../props-util';
|
import { getOptionProps } from '../props-util';
|
||||||
import PropTypes from '../vue-types';
|
import PropTypes from '../vue-types';
|
||||||
import proxyComponent from '../proxyComponent';
|
import proxyComponent from '../proxyComponent';
|
||||||
|
|
||||||
|
@ -22,9 +23,12 @@ export default function connect(mapStateToProps) {
|
||||||
});
|
});
|
||||||
const Connect = {
|
const Connect = {
|
||||||
name: `Connect_${getDisplayName(WrappedComponent)}`,
|
name: `Connect_${getDisplayName(WrappedComponent)}`,
|
||||||
|
inheritAttrs: false,
|
||||||
props,
|
props,
|
||||||
inject: {
|
setup() {
|
||||||
storeContext: { default: () => ({}) },
|
return {
|
||||||
|
storeContext: inject('storeContext', {}),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
this.store = this.storeContext.store;
|
this.store = this.storeContext.store;
|
||||||
|
@ -80,25 +84,19 @@ export default function connect(mapStateToProps) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { $slots = {}, $scopedSlots, subscribed, store } = this;
|
const { $slots = {}, subscribed, store, $attrs } = this;
|
||||||
const props = getOptionProps(this);
|
const props = getOptionProps(this);
|
||||||
this.preProps = { ...omit(props, ['__propsSymbol__']) };
|
this.preProps = { ...omit(props, ['__propsSymbol__']) };
|
||||||
const wrapProps = {
|
const wrapProps = {
|
||||||
props: {
|
|
||||||
...props,
|
...props,
|
||||||
...subscribed,
|
...subscribed,
|
||||||
|
...$attrs,
|
||||||
store,
|
store,
|
||||||
},
|
slots: $slots,
|
||||||
on: getListeners(this),
|
ref: 'wrappedInstance',
|
||||||
scopedSlots: $scopedSlots,
|
|
||||||
};
|
};
|
||||||
return (
|
return createVNode(WrappedComponent, wrapProps);
|
||||||
<WrappedComponent {...wrapProps} ref="wrappedInstance">
|
// return <WrappedComponent {...wrapProps} ref="wrappedInstance"></WrappedComponent>;
|
||||||
{Object.keys($slots).map(name => {
|
|
||||||
return <template slot={name}>{$slots[name]}</template>;
|
|
||||||
})}
|
|
||||||
</WrappedComponent>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return proxyComponent(Connect);
|
return proxyComponent(Connect);
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
|
import { inject } from 'vue';
|
||||||
import { Item, itemProps } from '../vc-menu';
|
import { Item, itemProps } from '../vc-menu';
|
||||||
import { getOptionProps, getListeners } from '../_util/props-util';
|
import { getOptionProps, getSlot } from '../_util/props-util';
|
||||||
import Tooltip from '../tooltip';
|
import Tooltip from '../tooltip';
|
||||||
function noop() {}
|
function noop() {}
|
||||||
export default {
|
export default {
|
||||||
name: 'MenuItem',
|
name: 'MenuItem',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: itemProps,
|
props: itemProps,
|
||||||
inject: {
|
|
||||||
getInlineCollapsed: { default: () => noop },
|
|
||||||
layoutSiderContext: { default: () => ({}) },
|
|
||||||
},
|
|
||||||
isMenuItem: true,
|
isMenuItem: true,
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
getInlineCollapsed: inject('getInlineCollapsed', noop),
|
||||||
|
layoutSiderContext: inject('layoutSiderContext', {}),
|
||||||
|
};
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
this.$refs.menuItem.onKeyDown(e);
|
this.$refs.menuItem.onKeyDown(e);
|
||||||
|
@ -33,24 +37,20 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemProps = {
|
const itemProps = {
|
||||||
props: {
|
|
||||||
...props,
|
...props,
|
||||||
title,
|
title,
|
||||||
},
|
...attrs,
|
||||||
attrs,
|
|
||||||
on: getListeners(this),
|
|
||||||
};
|
};
|
||||||
const toolTipProps = {
|
const toolTipProps = {
|
||||||
props: {
|
|
||||||
...tooltipProps,
|
...tooltipProps,
|
||||||
placement: 'right',
|
placement: 'right',
|
||||||
overlayClassName: `${rootPrefixCls}-inline-collapsed-tooltip`,
|
overlayClassName: `${rootPrefixCls}-inline-collapsed-tooltip`,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
// return <div>ddd</div>;
|
||||||
return (
|
return (
|
||||||
<Tooltip {...toolTipProps}>
|
<Tooltip {...toolTipProps}>
|
||||||
<Item {...itemProps} ref="menuItem">
|
<Item {...itemProps} ref="menuItem">
|
||||||
{$slots.default}
|
{getSlot(this)}
|
||||||
</Item>
|
</Item>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
|
import { inject } from 'vue';
|
||||||
import { SubMenu as VcSubMenu } from '../vc-menu';
|
import { SubMenu as VcSubMenu } from '../vc-menu';
|
||||||
import { getListeners } from '../_util/props-util';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import Omit from 'omit.js';
|
||||||
|
import { getSlot } from '../_util/props-util';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ASubMenu',
|
name: 'ASubMenu',
|
||||||
isSubMenu: true,
|
isSubMenu: true,
|
||||||
|
inheritAttrs: false,
|
||||||
props: { ...VcSubMenu.props },
|
props: { ...VcSubMenu.props },
|
||||||
inject: {
|
setup() {
|
||||||
menuPropsContext: { default: () => ({}) },
|
return {
|
||||||
|
menuPropsContext: inject('menuPropsContext', {}),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
|
@ -16,27 +21,16 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { $slots, $scopedSlots } = this;
|
const { $slots, $attrs } = this;
|
||||||
const { rootPrefixCls, popupClassName } = this.$props;
|
const { rootPrefixCls, popupClassName } = this.$props;
|
||||||
const { theme: antdMenuTheme } = this.menuPropsContext;
|
const { theme: antdMenuTheme } = this.menuPropsContext;
|
||||||
const props = {
|
const props = {
|
||||||
props: {
|
|
||||||
...this.$props,
|
...this.$props,
|
||||||
popupClassName: classNames(`${rootPrefixCls}-${antdMenuTheme}`, popupClassName),
|
popupClassName: classNames(`${rootPrefixCls}-${antdMenuTheme}`, popupClassName),
|
||||||
},
|
|
||||||
ref: 'subMenu',
|
ref: 'subMenu',
|
||||||
on: getListeners(this),
|
...$attrs,
|
||||||
scopedSlots: $scopedSlots,
|
...Omit($slots, ['default']),
|
||||||
};
|
};
|
||||||
const slotsKey = Object.keys($slots);
|
return <VcSubMenu {...props}>{getSlot(this)}</VcSubMenu>;
|
||||||
return (
|
|
||||||
<VcSubMenu {...props}>
|
|
||||||
{slotsKey.length
|
|
||||||
? slotsKey.map(name => {
|
|
||||||
return <template slot={name}>{$slots[name]}</template>;
|
|
||||||
})
|
|
||||||
: null}
|
|
||||||
</VcSubMenu>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { inject, provide } from 'vue';
|
||||||
import omit from 'omit.js';
|
import omit from 'omit.js';
|
||||||
import VcMenu, { Divider, ItemGroup } from '../vc-menu';
|
import VcMenu, { Divider, ItemGroup } from '../vc-menu';
|
||||||
import SubMenu from './SubMenu';
|
import SubMenu from './SubMenu';
|
||||||
|
@ -5,11 +6,10 @@ import PropTypes from '../_util/vue-types';
|
||||||
import animation from '../_util/openAnimation';
|
import animation from '../_util/openAnimation';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
import Item from './MenuItem';
|
import Item from './MenuItem';
|
||||||
import { hasProp, getListeners, getOptionProps } from '../_util/props-util';
|
import { hasProp, getOptionProps, getSlot } from '../_util/props-util';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
import commonPropsType from '../vc-menu/commonPropsType';
|
import commonPropsType from '../vc-menu/commonPropsType';
|
||||||
import { ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumerProps } from '../config-provider';
|
||||||
import Base from '../base';
|
|
||||||
// import raf from '../_util/raf';
|
// import raf from '../_util/raf';
|
||||||
|
|
||||||
export const MenuMode = PropTypes.oneOf([
|
export const MenuMode = PropTypes.oneOf([
|
||||||
|
@ -41,26 +41,27 @@ export const menuProps = {
|
||||||
|
|
||||||
const Menu = {
|
const Menu = {
|
||||||
name: 'AMenu',
|
name: 'AMenu',
|
||||||
|
inheritAttrs: false,
|
||||||
props: menuProps,
|
props: menuProps,
|
||||||
Divider: { ...Divider, name: 'AMenuDivider' },
|
Divider: { ...Divider, name: 'AMenuDivider' },
|
||||||
Item: { ...Item, name: 'AMenuItem' },
|
Item: { ...Item, name: 'AMenuItem' },
|
||||||
SubMenu: { ...SubMenu, name: 'ASubMenu' },
|
SubMenu: { ...SubMenu, name: 'ASubMenu' },
|
||||||
ItemGroup: { ...ItemGroup, name: 'AMenuItemGroup' },
|
ItemGroup: { ...ItemGroup, name: 'AMenuItemGroup' },
|
||||||
provide() {
|
mixins: [BaseMixin],
|
||||||
|
created() {
|
||||||
|
provide('getInlineCollapsed', this.getInlineCollapsed);
|
||||||
|
provide('menuPropsContext', this.$props);
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
return {
|
return {
|
||||||
getInlineCollapsed: this.getInlineCollapsed,
|
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||||
menuPropsContext: this.$props,
|
layoutSiderContext: inject('layoutSiderContext', {}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mixins: [BaseMixin],
|
// model: {
|
||||||
inject: {
|
// prop: 'selectedKeys',
|
||||||
layoutSiderContext: { default: () => ({}) },
|
// event: 'selectChange',
|
||||||
configProvider: { default: () => ConfigConsumerProps },
|
// },
|
||||||
},
|
|
||||||
model: {
|
|
||||||
prop: 'selectedKeys',
|
|
||||||
event: 'selectChange',
|
|
||||||
},
|
|
||||||
updated() {
|
updated() {
|
||||||
this.propsUpdating = false;
|
this.propsUpdating = false;
|
||||||
},
|
},
|
||||||
|
@ -165,10 +166,12 @@ const Menu = {
|
||||||
},
|
},
|
||||||
handleSelect(info) {
|
handleSelect(info) {
|
||||||
this.$emit('select', info);
|
this.$emit('select', info);
|
||||||
|
this.$emit('update:selectedKeys', info.selectedKeys);
|
||||||
this.$emit('selectChange', info.selectedKeys);
|
this.$emit('selectChange', info.selectedKeys);
|
||||||
},
|
},
|
||||||
handleDeselect(info) {
|
handleDeselect(info) {
|
||||||
this.$emit('deselect', info);
|
this.$emit('deselect', info);
|
||||||
|
this.$emit('update:selectedKeys', info.selectedKeys);
|
||||||
this.$emit('selectChange', info.selectedKeys);
|
this.$emit('selectChange', info.selectedKeys);
|
||||||
},
|
},
|
||||||
handleOpenChange(openKeys) {
|
handleOpenChange(openKeys) {
|
||||||
|
@ -203,7 +206,7 @@ const Menu = {
|
||||||
if (menuMode === 'horizontal') {
|
if (menuMode === 'horizontal') {
|
||||||
menuOpenAnimation = 'slide-up';
|
menuOpenAnimation = 'slide-up';
|
||||||
} else if (menuMode === 'inline') {
|
} else if (menuMode === 'inline') {
|
||||||
menuOpenAnimation = { on: animation };
|
menuOpenAnimation = animation;
|
||||||
} else {
|
} else {
|
||||||
// When mode switch from inline
|
// When mode switch from inline
|
||||||
// submenu should hide without animation
|
// submenu should hide without animation
|
||||||
|
@ -219,7 +222,7 @@ const Menu = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { layoutSiderContext, $slots } = this;
|
const { layoutSiderContext } = this;
|
||||||
const { collapsedWidth } = layoutSiderContext;
|
const { collapsedWidth } = layoutSiderContext;
|
||||||
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
|
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
|
||||||
const props = getOptionProps(this);
|
const props = getOptionProps(this);
|
||||||
|
@ -235,37 +238,32 @@ const Menu = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuProps = {
|
const menuProps = {
|
||||||
props: {
|
|
||||||
...omit(props, ['inlineCollapsed']),
|
...omit(props, ['inlineCollapsed']),
|
||||||
getPopupContainer: getPopupContainer || getContextPopupContainer,
|
getPopupContainer: getPopupContainer || getContextPopupContainer,
|
||||||
openKeys: this.sOpenKeys,
|
openKeys: this.sOpenKeys,
|
||||||
mode: menuMode,
|
mode: menuMode,
|
||||||
prefixCls,
|
prefixCls,
|
||||||
},
|
...this.$attrs,
|
||||||
on: {
|
onSelect: this.handleSelect,
|
||||||
...getListeners(this),
|
onDeselect: this.handleDeselect,
|
||||||
select: this.handleSelect,
|
onOpenChange: this.handleOpenChange,
|
||||||
deselect: this.handleDeselect,
|
onMouseenter: this.handleMouseEnter,
|
||||||
openChange: this.handleOpenChange,
|
onTransitionend: this.handleTransitionEnd,
|
||||||
mouseenter: this.handleMouseEnter,
|
children: getSlot(this),
|
||||||
},
|
|
||||||
nativeOn: {
|
|
||||||
transitionend: this.handleTransitionEnd,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
if (!hasProp(this, 'selectedKeys')) {
|
if (!hasProp(this, 'selectedKeys')) {
|
||||||
delete menuProps.props.selectedKeys;
|
delete menuProps.selectedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuMode !== 'inline') {
|
if (menuMode !== 'inline') {
|
||||||
// closing vertical popup submenu after click it
|
// closing vertical popup submenu after click it
|
||||||
menuProps.on.click = this.handleClick;
|
menuProps.onClick = this.handleClick;
|
||||||
menuProps.props.openTransitionName = menuOpenAnimation;
|
menuProps.openTransitionName = menuOpenAnimation;
|
||||||
} else {
|
} else {
|
||||||
menuProps.on.click = e => {
|
menuProps.onClick = e => {
|
||||||
this.$emit('click', e);
|
this.$emit('click', e);
|
||||||
};
|
};
|
||||||
menuProps.props.openAnimation = menuOpenAnimation;
|
menuProps.openAnimation = menuOpenAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/ant-design/ant-design/issues/8587
|
// https://github.com/ant-design/ant-design/issues/8587
|
||||||
|
@ -273,24 +271,19 @@ const Menu = {
|
||||||
this.getInlineCollapsed() &&
|
this.getInlineCollapsed() &&
|
||||||
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
|
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
|
||||||
if (hideMenu) {
|
if (hideMenu) {
|
||||||
menuProps.props.openKeys = [];
|
menuProps.openKeys = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <VcMenu {...menuProps} class={menuClassName} />;
|
||||||
<VcMenu {...menuProps} class={menuClassName}>
|
|
||||||
{$slots.default}
|
|
||||||
</VcMenu>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
Menu.install = function(Vue) {
|
Menu.install = function(app) {
|
||||||
Vue.use(Base);
|
app.component(Menu.name, Menu);
|
||||||
Vue.component(Menu.name, Menu);
|
app.component(Menu.Item.name, Menu.Item);
|
||||||
Vue.component(Menu.Item.name, Menu.Item);
|
app.component(Menu.SubMenu.name, Menu.SubMenu);
|
||||||
Vue.component(Menu.SubMenu.name, Menu.SubMenu);
|
app.component(Menu.Divider.name, Menu.Divider);
|
||||||
Vue.component(Menu.Divider.name, Menu.Divider);
|
app.component(Menu.ItemGroup.name, Menu.ItemGroup);
|
||||||
Vue.component(Menu.ItemGroup.name, Menu.ItemGroup);
|
|
||||||
};
|
};
|
||||||
export default Menu;
|
export default Menu;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import SubMenu from './SubMenu';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
import { getWidth, setStyle, menuAllProps } from './util';
|
import { getWidth, setStyle, menuAllProps } from './util';
|
||||||
import { cloneElement } from '../_util/vnode';
|
import { cloneElement } from '../_util/vnode';
|
||||||
import { getClass, getPropsData, getEvents, getListeners } from '../_util/props-util';
|
import { getPropsData, getSlot, getAllProps } from '../_util/props-util';
|
||||||
|
|
||||||
const canUseDOM = !!(
|
const canUseDOM = !!(
|
||||||
typeof window !== 'undefined' &&
|
typeof window !== 'undefined' &&
|
||||||
|
@ -110,9 +110,8 @@ const DOMWrap = {
|
||||||
}
|
}
|
||||||
// put all the overflowed item inside a submenu
|
// put all the overflowed item inside a submenu
|
||||||
// with a title of overflow indicator ('...')
|
// with a title of overflow indicator ('...')
|
||||||
const copy = this.$slots.default[0];
|
const copy = getSlot(this)[0];
|
||||||
const { title, ...rest } = getPropsData(copy); // eslint-disable-line no-unused-vars
|
const { title, ...rest } = getAllProps(copy); // eslint-disable-line no-unused-vars
|
||||||
const events = getEvents(copy);
|
|
||||||
let style = {};
|
let style = {};
|
||||||
let key = `${keyPrefix}-overflowed-indicator`;
|
let key = `${keyPrefix}-overflowed-indicator`;
|
||||||
let eventKey = `${keyPrefix}-overflowed-indicator`;
|
let eventKey = `${keyPrefix}-overflowed-indicator`;
|
||||||
|
@ -133,29 +132,20 @@ const DOMWrap = {
|
||||||
|
|
||||||
const popupClassName = theme ? `${prefixCls}-${theme}` : '';
|
const popupClassName = theme ? `${prefixCls}-${theme}` : '';
|
||||||
const props = {};
|
const props = {};
|
||||||
const on = {};
|
menuAllProps.forEach(k => {
|
||||||
menuAllProps.props.forEach(k => {
|
|
||||||
if (rest[k] !== undefined) {
|
if (rest[k] !== undefined) {
|
||||||
props[k] = rest[k];
|
props[k] = rest[k];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menuAllProps.on.forEach(k => {
|
|
||||||
if (events[k] !== undefined) {
|
|
||||||
on[k] = events[k];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const subMenuProps = {
|
const subMenuProps = {
|
||||||
props: {
|
|
||||||
title: overflowedIndicator,
|
title: overflowedIndicator,
|
||||||
popupClassName,
|
popupClassName,
|
||||||
...props,
|
...props,
|
||||||
eventKey,
|
eventKey,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
|
||||||
class: `${prefixCls}-overflowed-submenu`,
|
class: `${prefixCls}-overflowed-submenu`,
|
||||||
key,
|
key,
|
||||||
style,
|
style,
|
||||||
on,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return <SubMenu {...subMenuProps}>{overflowedItems}</SubMenu>;
|
return <SubMenu {...subMenuProps}>{overflowedItems}</SubMenu>;
|
||||||
|
@ -245,7 +235,7 @@ const DOMWrap = {
|
||||||
renderChildren(children) {
|
renderChildren(children) {
|
||||||
// need to take care of overflowed items in horizontal mode
|
// need to take care of overflowed items in horizontal mode
|
||||||
const { lastVisibleIndex } = this.$data;
|
const { lastVisibleIndex } = this.$data;
|
||||||
const className = getClass(this);
|
const className = this.$attrs.class || '';
|
||||||
return (children || []).reduce((acc, childNode, index) => {
|
return (children || []).reduce((acc, childNode, index) => {
|
||||||
let item = childNode;
|
let item = childNode;
|
||||||
const eventKey = getPropsData(childNode).eventKey;
|
const eventKey = getPropsData(childNode).eventKey;
|
||||||
|
@ -258,7 +248,7 @@ const DOMWrap = {
|
||||||
// 这里修改 eventKey 是为了防止隐藏状态下还会触发 openkeys 事件
|
// 这里修改 eventKey 是为了防止隐藏状态下还会触发 openkeys 事件
|
||||||
{
|
{
|
||||||
style: { display: 'none' },
|
style: { display: 'none' },
|
||||||
props: { eventKey: `${eventKey}-hidden` },
|
eventKey: `${eventKey}-hidden`,
|
||||||
class: MENUITEM_OVERFLOWED_CLASSNAME,
|
class: MENUITEM_OVERFLOWED_CLASSNAME,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -271,7 +261,7 @@ const DOMWrap = {
|
||||||
// we have to overwrite with the correct key explicitly
|
// we have to overwrite with the correct key explicitly
|
||||||
{
|
{
|
||||||
key: getPropsData(c).eventKey,
|
key: getPropsData(c).eventKey,
|
||||||
props: { mode: 'vertical-left' },
|
mode: 'vertical-left',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -295,10 +285,8 @@ const DOMWrap = {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const Tag = this.$props.tag;
|
const Tag = this.$props.tag;
|
||||||
const tagProps = {
|
|
||||||
on: getListeners(this),
|
return <Tag>{this.renderChildren(getSlot(this))}</Tag>;
|
||||||
};
|
|
||||||
return <Tag {...tagProps}>{this.renderChildren(this.$slots.default)}</Tag>;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,6 +299,7 @@ DOMWrap.props = {
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
hiddenClassName: PropTypes.string,
|
hiddenClassName: PropTypes.string,
|
||||||
tag: PropTypes.string.def('div'),
|
tag: PropTypes.string.def('div'),
|
||||||
|
children: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DOMWrap;
|
export default DOMWrap;
|
||||||
|
|
|
@ -2,16 +2,12 @@ import PropTypes from '../_util/vue-types';
|
||||||
import { Provider, create } from '../_util/store';
|
import { Provider, create } from '../_util/store';
|
||||||
import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu';
|
import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
import hasProp, {
|
import hasProp, { getOptionProps, getComponent, filterEmpty } from '../_util/props-util';
|
||||||
getOptionProps,
|
|
||||||
getComponentFromProp,
|
|
||||||
filterEmpty,
|
|
||||||
getListeners,
|
|
||||||
} from '../_util/props-util';
|
|
||||||
import commonPropsType from './commonPropsType';
|
import commonPropsType from './commonPropsType';
|
||||||
|
|
||||||
const Menu = {
|
const Menu = {
|
||||||
name: 'Menu',
|
name: 'Menu',
|
||||||
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
...commonPropsType,
|
...commonPropsType,
|
||||||
selectable: PropTypes.bool.def(true),
|
selectable: PropTypes.bool.def(true),
|
||||||
|
@ -33,7 +29,7 @@ const Menu = {
|
||||||
selectedKeys,
|
selectedKeys,
|
||||||
openKeys,
|
openKeys,
|
||||||
activeKey: {
|
activeKey: {
|
||||||
'0-menu-': getActiveKey({ ...props, children: this.$slots.default || [] }, props.activeKey),
|
'0-menu-': getActiveKey({ ...props, children: props.children || [] }, props.activeKey),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,27 +154,20 @@ const Menu = {
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const props = getOptionProps(this);
|
const props = { ...getOptionProps(this), ...this.$attrs };
|
||||||
|
props.class += ` ${props.prefixCls}-root`;
|
||||||
const subPopupMenuProps = {
|
const subPopupMenuProps = {
|
||||||
props: {
|
|
||||||
...props,
|
...props,
|
||||||
itemIcon: getComponentFromProp(this, 'itemIcon', props),
|
itemIcon: getComponent(this, 'itemIcon', props),
|
||||||
expandIcon: getComponentFromProp(this, 'expandIcon', props),
|
expandIcon: getComponent(this, 'expandIcon', props),
|
||||||
overflowedIndicator: getComponentFromProp(this, 'overflowedIndicator', props) || (
|
overflowedIndicator: getComponent(this, 'overflowedIndicator', props) || <span>···</span>,
|
||||||
<span>···</span>
|
|
||||||
),
|
|
||||||
openTransitionName: this.getOpenTransitionName(),
|
openTransitionName: this.getOpenTransitionName(),
|
||||||
parentMenu: this,
|
parentMenu: this,
|
||||||
children: filterEmpty(this.$slots.default || []),
|
children: filterEmpty(props.children),
|
||||||
},
|
onClick: this.onClick,
|
||||||
class: `${props.prefixCls}-root`,
|
onOpenChange: this.onOpenChange,
|
||||||
on: {
|
onDeselect: this.onDeselect,
|
||||||
...getListeners(this),
|
onSelect: this.onSelect,
|
||||||
click: this.onClick,
|
|
||||||
openChange: this.onOpenChange,
|
|
||||||
deselect: this.onDeselect,
|
|
||||||
select: this.onSelect,
|
|
||||||
},
|
|
||||||
ref: 'innerMenu',
|
ref: 'innerMenu',
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,7 +4,7 @@ import BaseMixin from '../_util/BaseMixin';
|
||||||
import scrollIntoView from 'dom-scroll-into-view';
|
import scrollIntoView from 'dom-scroll-into-view';
|
||||||
import { connect } from '../_util/store';
|
import { connect } from '../_util/store';
|
||||||
import { noop, menuAllProps } from './util';
|
import { noop, menuAllProps } from './util';
|
||||||
import { getComponentFromProp, getListeners } from '../_util/props-util';
|
import { getComponent, getSlot } from '../_util/props-util';
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
attribute: PropTypes.object,
|
attribute: PropTypes.object,
|
||||||
|
@ -36,6 +36,7 @@ const props = {
|
||||||
};
|
};
|
||||||
const MenuItem = {
|
const MenuItem = {
|
||||||
name: 'MenuItem',
|
name: 'MenuItem',
|
||||||
|
inheritAttrs: false,
|
||||||
props,
|
props,
|
||||||
mixins: [BaseMixin],
|
mixins: [BaseMixin],
|
||||||
isMenuItem: true,
|
isMenuItem: true,
|
||||||
|
@ -141,8 +142,10 @@ const MenuItem = {
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const props = { ...this.$props };
|
const props = { ...this.$props, ...this.$attrs };
|
||||||
|
|
||||||
const className = {
|
const className = {
|
||||||
|
[props.class]: props.class,
|
||||||
[this.getPrefixCls()]: true,
|
[this.getPrefixCls()]: true,
|
||||||
[this.getActiveClassName()]: !props.disabled && props.active,
|
[this.getActiveClassName()]: !props.disabled && props.active,
|
||||||
[this.getSelectedClassName()]: props.isSelected,
|
[this.getSelectedClassName()]: props.isSelected,
|
||||||
|
@ -171,32 +174,27 @@ const MenuItem = {
|
||||||
}
|
}
|
||||||
// In case that onClick/onMouseLeave/onMouseEnter is passed down from owner
|
// In case that onClick/onMouseLeave/onMouseEnter is passed down from owner
|
||||||
const mouseEvent = {
|
const mouseEvent = {
|
||||||
click: props.disabled ? noop : this.onClick,
|
onClick: props.disabled ? noop : this.onClick,
|
||||||
mouseleave: props.disabled ? noop : this.onMouseLeave,
|
onMouseleave: props.disabled ? noop : this.onMouseLeave,
|
||||||
mouseenter: props.disabled ? noop : this.onMouseEnter,
|
onMouseenter: props.disabled ? noop : this.onMouseEnter,
|
||||||
};
|
};
|
||||||
|
|
||||||
const style = {};
|
const style = { ...(props.style || {}) };
|
||||||
if (props.mode === 'inline') {
|
if (props.mode === 'inline') {
|
||||||
style.paddingLeft = `${props.inlineIndent * props.level}px`;
|
style.paddingLeft = `${props.inlineIndent * props.level}px`;
|
||||||
}
|
}
|
||||||
const listeners = { ...getListeners(this) };
|
[...menuAllProps, 'children', 'slots', '__propsSymbol__', 'componentWillReceiveProps'].forEach(
|
||||||
menuAllProps.props.forEach(key => delete props[key]);
|
key => delete props[key],
|
||||||
menuAllProps.on.forEach(key => delete listeners[key]);
|
);
|
||||||
const liProps = {
|
const liProps = {
|
||||||
attrs: {
|
|
||||||
...props,
|
...props,
|
||||||
...attrs,
|
...attrs,
|
||||||
},
|
|
||||||
on: {
|
|
||||||
...listeners,
|
|
||||||
...mouseEvent,
|
...mouseEvent,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<li {...liProps} style={style} class={className}>
|
<li {...liProps} style={style} class={className}>
|
||||||
{this.$slots.default}
|
{getSlot(this)}
|
||||||
{getComponentFromProp(this, 'itemIcon', props)}
|
{getComponent(this, 'itemIcon', props)}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { getComponentFromProp, getListeners } from '../_util/props-util';
|
import { getComponent, getSlot } from '../_util/props-util';
|
||||||
|
|
||||||
// import { menuAllProps } from './util'
|
// import { menuAllProps } from './util'
|
||||||
|
|
||||||
const MenuItemGroup = {
|
const MenuItemGroup = {
|
||||||
name: 'MenuItemGroup',
|
name: 'MenuItemGroup',
|
||||||
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
renderMenuItem: PropTypes.func,
|
renderMenuItem: PropTypes.func,
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
|
@ -23,22 +23,19 @@ const MenuItemGroup = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const props = { ...this.$props };
|
const props = { ...this.$props, ...this.$attrs };
|
||||||
const { rootPrefixCls, title } = props;
|
const { class: cls = '', rootPrefixCls, title } = props;
|
||||||
const titleClassName = `${rootPrefixCls}-item-group-title`;
|
const titleClassName = `${rootPrefixCls}-item-group-title`;
|
||||||
const listClassName = `${rootPrefixCls}-item-group-list`;
|
const listClassName = `${rootPrefixCls}-item-group-list`;
|
||||||
// menuAllProps.props.forEach(key => delete props[key])
|
// menuAllProps.props.forEach(key => delete props[key])
|
||||||
const listeners = { ...getListeners(this) };
|
delete props.onClick;
|
||||||
delete listeners.click;
|
const children = getSlot(this);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li {...{ on: listeners, class: `${rootPrefixCls}-item-group` }}>
|
<li {...props} class={`${cls} ${rootPrefixCls}-item-group`}>
|
||||||
<div class={titleClassName} title={typeof title === 'string' ? title : undefined}>
|
<div class={titleClassName} title={typeof title === 'string' ? title : undefined}>
|
||||||
{getComponentFromProp(this, 'title')}
|
{getComponent(this, 'title')}
|
||||||
</div>
|
</div>
|
||||||
<ul class={listClassName}>
|
<ul class={listClassName}>{children && children.map(this.renderInnerMenuItem)}</ul>
|
||||||
{this.$slots.default && this.$slots.default.map(this.renderInnerMenuItem)}
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { connect } from '../_util/store';
|
||||||
import SubPopupMenu from './SubPopupMenu';
|
import SubPopupMenu from './SubPopupMenu';
|
||||||
import placements from './placements';
|
import placements from './placements';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
import { getComponentFromProp, filterEmpty, getListeners } from '../_util/props-util';
|
import { getComponent, filterEmpty, getSlot, splitAttrs } from '../_util/props-util';
|
||||||
import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout';
|
import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout';
|
||||||
import { noop, loopMenuItemRecursively, getMenuIdFromSubMenuEventKey } from './util';
|
import { noop, loopMenuItemRecursively, getMenuIdFromSubMenuEventKey } from './util';
|
||||||
import getTransitionProps from '../_util/getTransitionProps';
|
import getTransitionProps from '../_util/getTransitionProps';
|
||||||
|
@ -33,6 +33,7 @@ const updateDefaultActiveFirst = (store, eventKey, defaultActiveFirst) => {
|
||||||
|
|
||||||
const SubMenu = {
|
const SubMenu = {
|
||||||
name: 'SubMenu',
|
name: 'SubMenu',
|
||||||
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
parentMenu: PropTypes.object,
|
parentMenu: PropTypes.object,
|
||||||
title: PropTypes.any,
|
title: PropTypes.any,
|
||||||
|
@ -86,6 +87,9 @@ const SubMenu = {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDefaultActiveFirst(store, eventKey, value);
|
updateDefaultActiveFirst(store, eventKey, value);
|
||||||
|
this.internalMenuId = undefined;
|
||||||
|
this.haveRendered = undefined;
|
||||||
|
this.haveOpened = undefined;
|
||||||
return {
|
return {
|
||||||
// defaultActiveFirst: false,
|
// defaultActiveFirst: false,
|
||||||
};
|
};
|
||||||
|
@ -312,7 +316,7 @@ const SubMenu = {
|
||||||
|
|
||||||
isChildrenSelected() {
|
isChildrenSelected() {
|
||||||
const ret = { find: false };
|
const ret = { find: false };
|
||||||
loopMenuItemRecursively(this.$slots.default, this.$props.selectedKeys, ret);
|
loopMenuItemRecursively(getSlot(this), this.$props.selectedKeys, ret);
|
||||||
return ret.find;
|
return ret.find;
|
||||||
},
|
},
|
||||||
// isOpen () {
|
// isOpen () {
|
||||||
|
@ -334,10 +338,8 @@ const SubMenu = {
|
||||||
},
|
},
|
||||||
|
|
||||||
renderChildren(children) {
|
renderChildren(children) {
|
||||||
const props = this.$props;
|
const props = { ...this.$props, ...this.$attrs };
|
||||||
const { select, deselect, openChange } = getListeners(this);
|
|
||||||
const subPopupMenuProps = {
|
const subPopupMenuProps = {
|
||||||
props: {
|
|
||||||
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
|
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
|
||||||
visible: props.isOpen,
|
visible: props.isOpen,
|
||||||
level: props.level + 1,
|
level: props.level + 1,
|
||||||
|
@ -360,23 +362,20 @@ const SubMenu = {
|
||||||
multiple: props.multiple,
|
multiple: props.multiple,
|
||||||
prefixCls: props.rootPrefixCls,
|
prefixCls: props.rootPrefixCls,
|
||||||
manualRef: this.saveMenuInstance,
|
manualRef: this.saveMenuInstance,
|
||||||
itemIcon: getComponentFromProp(this, 'itemIcon'),
|
itemIcon: getComponent(this, 'itemIcon'),
|
||||||
expandIcon: getComponentFromProp(this, 'expandIcon'),
|
expandIcon: getComponent(this, 'expandIcon'),
|
||||||
children,
|
children,
|
||||||
},
|
|
||||||
on: {
|
|
||||||
click: this.onSubMenuClick,
|
click: this.onSubMenuClick,
|
||||||
select,
|
onSelect: props.onSelect || noop,
|
||||||
deselect,
|
onDeselect: props.onDeselect || noop,
|
||||||
openChange,
|
onOpenChange: props.onOpenChange || noop,
|
||||||
},
|
|
||||||
id: this.internalMenuId,
|
id: this.internalMenuId,
|
||||||
};
|
};
|
||||||
const baseProps = subPopupMenuProps.props;
|
|
||||||
const haveRendered = this.haveRendered;
|
const haveRendered = this.haveRendered;
|
||||||
this.haveRendered = true;
|
this.haveRendered = true;
|
||||||
|
|
||||||
this.haveOpened = this.haveOpened || baseProps.visible || baseProps.forceSubMenuRender;
|
this.haveOpened =
|
||||||
|
this.haveOpened || subPopupMenuProps.visible || subPopupMenuProps.forceSubMenuRender;
|
||||||
// never rendered not planning to, don't render
|
// never rendered not planning to, don't render
|
||||||
if (!this.haveOpened) {
|
if (!this.haveOpened) {
|
||||||
return <div />;
|
return <div />;
|
||||||
|
@ -385,28 +384,24 @@ const SubMenu = {
|
||||||
// don't show transition on first rendering (no animation for opened menu)
|
// 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 visible (not sure why)
|
||||||
// show appear transition if it's not inline mode
|
// show appear transition if it's not inline mode
|
||||||
const transitionAppear = haveRendered || !baseProps.visible || !baseProps.mode === 'inline';
|
const transitionAppear =
|
||||||
subPopupMenuProps.class = ` ${baseProps.prefixCls}-sub`;
|
haveRendered || !subPopupMenuProps.visible || !subPopupMenuProps.mode === 'inline';
|
||||||
let animProps = { appear: transitionAppear, css: false };
|
subPopupMenuProps.class = ` ${subPopupMenuProps.prefixCls}-sub`;
|
||||||
let transitionProps = {
|
let transitionProps = { appear: transitionAppear, css: false };
|
||||||
props: animProps,
|
|
||||||
on: {},
|
if (subPopupMenuProps.openTransitionName) {
|
||||||
};
|
transitionProps = getTransitionProps(subPopupMenuProps.openTransitionName, {
|
||||||
if (baseProps.openTransitionName) {
|
|
||||||
transitionProps = getTransitionProps(baseProps.openTransitionName, {
|
|
||||||
appear: transitionAppear,
|
appear: transitionAppear,
|
||||||
});
|
});
|
||||||
} else if (typeof baseProps.openAnimation === 'object') {
|
} else if (typeof subPopupMenuProps.openAnimation === 'object') {
|
||||||
animProps = { ...animProps, ...(baseProps.openAnimation.props || {}) };
|
transitionProps = { ...transitionProps, ...(subPopupMenuProps.openAnimation || {}) };
|
||||||
if (!transitionAppear) {
|
if (!transitionAppear) {
|
||||||
animProps.appear = false;
|
transitionProps.appear = false;
|
||||||
}
|
}
|
||||||
} else if (typeof baseProps.openAnimation === 'string') {
|
} else if (typeof subPopupMenuProps.openAnimation === 'string') {
|
||||||
transitionProps = getTransitionProps(baseProps.openAnimation, { appear: transitionAppear });
|
transitionProps = getTransitionProps(subPopupMenuProps.openAnimation, {
|
||||||
}
|
appear: transitionAppear,
|
||||||
|
});
|
||||||
if (typeof baseProps.openAnimation === 'object' && baseProps.openAnimation.on) {
|
|
||||||
transitionProps.on = baseProps.openAnimation.on;
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<transition {...transitionProps}>
|
<transition {...transitionProps}>
|
||||||
|
@ -417,7 +412,8 @@ const SubMenu = {
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const props = this.$props;
|
const props = { ...this.$props, ...this.$attrs };
|
||||||
|
const { onEvents } = splitAttrs(props);
|
||||||
const { rootPrefixCls, parentMenu } = this;
|
const { rootPrefixCls, parentMenu } = this;
|
||||||
const isOpen = props.isOpen;
|
const isOpen = props.isOpen;
|
||||||
const prefixCls = this.getPrefixCls();
|
const prefixCls = this.getPrefixCls();
|
||||||
|
@ -425,6 +421,7 @@ const SubMenu = {
|
||||||
const className = {
|
const className = {
|
||||||
[prefixCls]: true,
|
[prefixCls]: true,
|
||||||
[`${prefixCls}-${props.mode}`]: true,
|
[`${prefixCls}-${props.mode}`]: true,
|
||||||
|
[props.className]: !!props.className,
|
||||||
[this.getOpenClassName()]: isOpen,
|
[this.getOpenClassName()]: isOpen,
|
||||||
[this.getActiveClassName()]: props.active || (isOpen && !isInlineMode),
|
[this.getActiveClassName()]: props.active || (isOpen && !isInlineMode),
|
||||||
[this.getDisabledClassName()]: props.disabled,
|
[this.getDisabledClassName()]: props.disabled,
|
||||||
|
@ -444,17 +441,17 @@ const SubMenu = {
|
||||||
let titleMouseEvents = {};
|
let titleMouseEvents = {};
|
||||||
if (!props.disabled) {
|
if (!props.disabled) {
|
||||||
mouseEvents = {
|
mouseEvents = {
|
||||||
mouseleave: this.onMouseLeave,
|
onMouseleave: this.onMouseLeave,
|
||||||
mouseenter: this.onMouseEnter,
|
onMouseenter: this.onMouseEnter,
|
||||||
};
|
};
|
||||||
|
|
||||||
// only works in title, not outer li
|
// only works in title, not outer li
|
||||||
titleClickEvents = {
|
titleClickEvents = {
|
||||||
click: this.onTitleClick,
|
onClick: this.onTitleClick,
|
||||||
};
|
};
|
||||||
titleMouseEvents = {
|
titleMouseEvents = {
|
||||||
mouseenter: this.onTitleMouseEnter,
|
onMouseenter: this.onTitleMouseEnter,
|
||||||
mouseleave: this.onTitleMouseLeave,
|
onMouseleave: this.onTitleMouseLeave,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,16 +469,12 @@ const SubMenu = {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const titleProps = {
|
const titleProps = {
|
||||||
attrs: {
|
|
||||||
'aria-expanded': isOpen,
|
'aria-expanded': isOpen,
|
||||||
...ariaOwns,
|
...ariaOwns,
|
||||||
'aria-haspopup': 'true',
|
'aria-haspopup': 'true',
|
||||||
title: typeof props.title === 'string' ? props.title : undefined,
|
title: typeof props.title === 'string' ? props.title : undefined,
|
||||||
},
|
|
||||||
on: {
|
|
||||||
...titleMouseEvents,
|
...titleMouseEvents,
|
||||||
...titleClickEvents,
|
...titleClickEvents,
|
||||||
},
|
|
||||||
style,
|
style,
|
||||||
class: `${prefixCls}-title`,
|
class: `${prefixCls}-title`,
|
||||||
ref: 'subMenuTitle',
|
ref: 'subMenuTitle',
|
||||||
|
@ -489,15 +482,15 @@ const SubMenu = {
|
||||||
// expand custom icon should NOT be displayed in menu with horizontal mode.
|
// expand custom icon should NOT be displayed in menu with horizontal mode.
|
||||||
let icon = null;
|
let icon = null;
|
||||||
if (props.mode !== 'horizontal') {
|
if (props.mode !== 'horizontal') {
|
||||||
icon = getComponentFromProp(this, 'expandIcon', props);
|
icon = getComponent(this, 'expandIcon', props);
|
||||||
}
|
}
|
||||||
const title = (
|
const title = (
|
||||||
<div {...titleProps}>
|
<div {...titleProps}>
|
||||||
{getComponentFromProp(this, 'title')}
|
{getComponent(this, 'title')}
|
||||||
{icon || <i class={`${prefixCls}-arrow`} />}
|
{icon || <i class={`${prefixCls}-arrow`} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const children = this.renderChildren(filterEmpty(this.$slots.default));
|
const children = this.renderChildren(filterEmpty(getSlot(this)));
|
||||||
|
|
||||||
const getPopupContainer = this.parentMenu.isRootMenu
|
const getPopupContainer = this.parentMenu.isRootMenu
|
||||||
? this.parentMenu.getPopupContainer
|
? this.parentMenu.getPopupContainer
|
||||||
|
@ -506,7 +499,8 @@ const SubMenu = {
|
||||||
const popupAlign = props.popupOffset ? { offset: props.popupOffset } : {};
|
const popupAlign = props.popupOffset ? { offset: props.popupOffset } : {};
|
||||||
const popupClassName = props.mode === 'inline' ? '' : props.popupClassName;
|
const popupClassName = props.mode === 'inline' ? '' : props.popupClassName;
|
||||||
const liProps = {
|
const liProps = {
|
||||||
on: { ...omit(getListeners(this), ['click']), ...mouseEvents },
|
...omit(onEvents, ['onClick']),
|
||||||
|
...mouseEvents,
|
||||||
class: className,
|
class: className,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -533,8 +527,8 @@ const SubMenu = {
|
||||||
forceRender={props.forceSubMenuRender}
|
forceRender={props.forceSubMenuRender}
|
||||||
// popupTransitionName='rc-menu-open-slide-up'
|
// popupTransitionName='rc-menu-open-slide-up'
|
||||||
// popupAnimation={transitionProps}
|
// popupAnimation={transitionProps}
|
||||||
|
popup={children}
|
||||||
>
|
>
|
||||||
<template slot="popup">{children}</template>
|
|
||||||
{title}
|
{title}
|
||||||
</Trigger>
|
</Trigger>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import omit from 'omit.js';
|
import { Comment } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { connect } from '../_util/store';
|
import { connect } from '../_util/store';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
|
@ -7,14 +7,7 @@ import classNames from 'classnames';
|
||||||
import { getKeyFromChildrenIndex, loopMenuItem, noop, isMobileDevice } from './util';
|
import { getKeyFromChildrenIndex, loopMenuItem, noop, isMobileDevice } from './util';
|
||||||
import DOMWrap from './DOMWrap';
|
import DOMWrap from './DOMWrap';
|
||||||
import { cloneElement } from '../_util/vnode';
|
import { cloneElement } from '../_util/vnode';
|
||||||
import {
|
import { initDefaultProps, getOptionProps, getComponent, splitAttrs } from '../_util/props-util';
|
||||||
initDefaultProps,
|
|
||||||
getOptionProps,
|
|
||||||
getPropsData,
|
|
||||||
getEvents,
|
|
||||||
getComponentFromProp,
|
|
||||||
getListeners,
|
|
||||||
} from '../_util/props-util';
|
|
||||||
|
|
||||||
function allDisabled(arr) {
|
function allDisabled(arr) {
|
||||||
if (!arr.length) {
|
if (!arr.length) {
|
||||||
|
@ -77,6 +70,7 @@ export function getActiveKey(props, originalActiveKey) {
|
||||||
|
|
||||||
const SubPopupMenu = {
|
const SubPopupMenu = {
|
||||||
name: 'SubPopupMenu',
|
name: 'SubPopupMenu',
|
||||||
|
inheritAttrs: false,
|
||||||
props: initDefaultProps(
|
props: initDefaultProps(
|
||||||
{
|
{
|
||||||
// onSelect: PropTypes.func,
|
// onSelect: PropTypes.func,
|
||||||
|
@ -271,48 +265,23 @@ const SubPopupMenu = {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
getIcon(instance, name) {
|
getIcon(instance, name) {
|
||||||
if (instance.$createElement) {
|
return getComponent(instance, name);
|
||||||
const temp = instance[name];
|
|
||||||
if (temp !== undefined) {
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
return instance.$slots[name] || instance.$scopedSlots[name];
|
|
||||||
} else {
|
|
||||||
const temp = getPropsData(instance)[name];
|
|
||||||
if (temp !== undefined) {
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
const slotsProp = [];
|
|
||||||
const componentOptions = instance.componentOptions || {};
|
|
||||||
(componentOptions.children || []).forEach(child => {
|
|
||||||
if (child.data && child.data.slot === name) {
|
|
||||||
if (child.tag === 'template') {
|
|
||||||
slotsProp.push(child.children);
|
|
||||||
} else {
|
|
||||||
slotsProp.push(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return slotsProp.length ? slotsProp : undefined;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
renderCommonMenuItem(child, i, extraProps) {
|
renderCommonMenuItem(child, i, extraProps) {
|
||||||
if (child.tag === undefined) {
|
if (child.type === Comment) {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
const state = this.$props.store.getState();
|
const state = this.$props.store.getState();
|
||||||
const props = this.$props;
|
const props = this.$props;
|
||||||
const key = getKeyFromChildrenIndex(child, props.eventKey, i);
|
const key = getKeyFromChildrenIndex(child, props.eventKey, i);
|
||||||
const childProps = child.componentOptions.propsData || {};
|
const childProps = { ...getOptionProps(child), ...child.props }; // child.props 包含事件
|
||||||
|
|
||||||
const isActive = key === state.activeKey[getEventKey(this.$props)];
|
const isActive = key === state.activeKey[getEventKey(this.$props)];
|
||||||
if (!childProps.disabled) {
|
if (!childProps.disabled) {
|
||||||
// manualRef的执行顺序不能保证,使用key映射ref在this.instanceArray中的位置
|
// manualRef的执行顺序不能保证,使用key映射ref在this.instanceArray中的位置
|
||||||
this.instanceArrayKeyIndexMap[key] = Object.keys(this.instanceArrayKeyIndexMap).length;
|
this.instanceArrayKeyIndexMap[key] = Object.keys(this.instanceArrayKeyIndexMap).length;
|
||||||
}
|
}
|
||||||
const childListeners = getEvents(child);
|
|
||||||
const newChildProps = {
|
const newChildProps = {
|
||||||
props: {
|
|
||||||
mode: childProps.mode || props.mode,
|
mode: childProps.mode || props.mode,
|
||||||
level: props.level,
|
level: props.level,
|
||||||
inlineIndent: props.inlineIndent,
|
inlineIndent: props.inlineIndent,
|
||||||
|
@ -334,22 +303,19 @@ const SubPopupMenu = {
|
||||||
itemIcon: this.getIcon(child, 'itemIcon') || this.getIcon(this, 'itemIcon'),
|
itemIcon: this.getIcon(child, 'itemIcon') || this.getIcon(this, 'itemIcon'),
|
||||||
expandIcon: this.getIcon(child, 'expandIcon') || this.getIcon(this, 'expandIcon'),
|
expandIcon: this.getIcon(child, 'expandIcon') || this.getIcon(this, 'expandIcon'),
|
||||||
...extraProps,
|
...extraProps,
|
||||||
},
|
onClick: e => {
|
||||||
on: {
|
(childProps.onClick || noop)(e);
|
||||||
click: e => {
|
|
||||||
(childListeners.click || noop)(e);
|
|
||||||
this.onClick(e);
|
this.onClick(e);
|
||||||
},
|
},
|
||||||
itemHover: this.onItemHover,
|
onItemHover: this.onItemHover,
|
||||||
openChange: this.onOpenChange,
|
onOpenChange: this.onOpenChange,
|
||||||
deselect: this.onDeselect,
|
onDeselect: this.onDeselect,
|
||||||
// destroy: this.onDestroy,
|
// destroy: this.onDestroy,
|
||||||
select: this.onSelect,
|
onSelect: this.onSelect,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
// ref: https://github.com/ant-design/ant-design/issues/13943
|
// ref: https://github.com/ant-design/ant-design/issues/13943
|
||||||
if (props.mode === 'inline' || isMobileDevice()) {
|
if (props.mode === 'inline' || isMobileDevice()) {
|
||||||
newChildProps.props.triggerSubMenuAction = 'click';
|
newChildProps.triggerSubMenuAction = 'click';
|
||||||
}
|
}
|
||||||
return cloneElement(child, newChildProps);
|
return cloneElement(child, newChildProps);
|
||||||
},
|
},
|
||||||
|
@ -370,13 +336,16 @@ const SubPopupMenu = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { ...props } = this.$props;
|
const props = { ...this.$props };
|
||||||
|
const { onEvents } = splitAttrs(this.$attrs);
|
||||||
const { eventKey, prefixCls, visible, level, mode, theme } = props;
|
const { eventKey, prefixCls, visible, level, mode, theme } = props;
|
||||||
this.instanceArray = [];
|
this.instanceArray = [];
|
||||||
this.instanceArrayKeyIndexMap = {};
|
this.instanceArrayKeyIndexMap = {};
|
||||||
const className = classNames(props.prefixCls, `${props.prefixCls}-${props.mode}`);
|
const className = classNames(props.class, props.prefixCls, `${props.prefixCls}-${props.mode}`);
|
||||||
|
// Otherwise, the propagated click event will trigger another onClick
|
||||||
|
delete onEvents.onClick;
|
||||||
const domWrapProps = {
|
const domWrapProps = {
|
||||||
props: {
|
...props,
|
||||||
tag: 'ul',
|
tag: 'ul',
|
||||||
// hiddenClassName: `${prefixCls}-hidden`,
|
// hiddenClassName: `${prefixCls}-hidden`,
|
||||||
visible,
|
visible,
|
||||||
|
@ -384,21 +353,17 @@ const SubPopupMenu = {
|
||||||
level,
|
level,
|
||||||
mode,
|
mode,
|
||||||
theme,
|
theme,
|
||||||
overflowedIndicator: getComponentFromProp(this, 'overflowedIndicator'),
|
overflowedIndicator: getComponent(this, 'overflowedIndicator'),
|
||||||
},
|
|
||||||
attrs: {
|
|
||||||
role: props.role || 'menu',
|
role: props.role || 'menu',
|
||||||
},
|
|
||||||
class: className,
|
class: className,
|
||||||
// Otherwise, the propagated click event will trigger another onClick
|
...onEvents,
|
||||||
on: omit(getListeners(this), ['click']),
|
|
||||||
};
|
};
|
||||||
// if (props.id) {
|
// if (props.id) {
|
||||||
// domProps.id = props.id
|
// domProps.id = props.id
|
||||||
// }
|
// }
|
||||||
if (props.focusable) {
|
if (props.focusable) {
|
||||||
domWrapProps.attrs.tabIndex = '0';
|
domWrapProps.tabIndex = '0';
|
||||||
domWrapProps.on.keydown = this.onKeyDown;
|
domWrapProps.onKeydown = this.onKeyDown;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
// ESLint is not smart enough to know that the type of `children` was checked.
|
// ESLint is not smart enough to know that the type of `children` was checked.
|
||||||
|
|
|
@ -37,4 +37,5 @@ export default {
|
||||||
itemIcon: PropTypes.any,
|
itemIcon: PropTypes.any,
|
||||||
expandIcon: PropTypes.any,
|
expandIcon: PropTypes.any,
|
||||||
overflowedIndicator: PropTypes.any,
|
overflowedIndicator: PropTypes.any,
|
||||||
|
children: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,8 +51,7 @@ export function loopMenuItemRecursively(children, keys, ret) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const menuAllProps = {
|
export const menuAllProps = [
|
||||||
props: [
|
|
||||||
'defaultSelectedKeys',
|
'defaultSelectedKeys',
|
||||||
'selectedKeys',
|
'selectedKeys',
|
||||||
'defaultOpenKeys',
|
'defaultOpenKeys',
|
||||||
|
@ -101,18 +100,16 @@ export const menuAllProps = {
|
||||||
'theme',
|
'theme',
|
||||||
'itemIcon',
|
'itemIcon',
|
||||||
'expandIcon',
|
'expandIcon',
|
||||||
],
|
|
||||||
on: [
|
'onSelect',
|
||||||
'select',
|
'onDeselect',
|
||||||
'deselect',
|
'onDestroy',
|
||||||
'destroy',
|
'onOpenChange',
|
||||||
'openChange',
|
'onItemHover',
|
||||||
'itemHover',
|
'onTitleMouseenter',
|
||||||
'titleMouseenter',
|
'onTitleMouseleave',
|
||||||
'titleMouseleave',
|
'onTitleClick',
|
||||||
'titleClick',
|
];
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// ref: https://github.com/ant-design/ant-design/issues/14007
|
// ref: https://github.com/ant-design/ant-design/issues/14007
|
||||||
// ref: https://bugs.chromium.org/p/chromium/issues/detail?id=360889
|
// ref: https://bugs.chromium.org/p/chromium/issues/detail?id=360889
|
||||||
|
|
|
@ -29,6 +29,7 @@ import Popover from 'ant-design-vue/popover';
|
||||||
import notification from 'ant-design-vue/notification';
|
import notification from 'ant-design-vue/notification';
|
||||||
import message from 'ant-design-vue/message';
|
import message from 'ant-design-vue/message';
|
||||||
import Modal from 'ant-design-vue/modal';
|
import Modal from 'ant-design-vue/modal';
|
||||||
|
import Menu from 'ant-design-vue/menu';
|
||||||
import 'ant-design-vue/style.js';
|
import 'ant-design-vue/style.js';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
@ -61,4 +62,5 @@ app
|
||||||
.use(Popconfirm)
|
.use(Popconfirm)
|
||||||
.use(Popover)
|
.use(Popover)
|
||||||
.use(Modal)
|
.use(Modal)
|
||||||
|
.use(Menu)
|
||||||
.mount('#app');
|
.mount('#app');
|
||||||
|
|
Loading…
Reference in New Issue