Skip to content

Commit b85bc0e

Browse files
committed
feat: update menu
1 parent c7b0cb0 commit b85bc0e

File tree

17 files changed

+256
-51
lines changed

17 files changed

+256
-51
lines changed

build/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
22
dev: {
3-
componentName: 'list', // dev components
3+
componentName: 'menu', // dev components
44
},
55
};

components/menu/MenuItem.jsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ export default {
88
props: itemProps,
99
inject: {
1010
getInlineCollapsed: { default: () => noop },
11+
layoutSiderContext: { default: () => ({}) },
1112
},
12-
isMenuItem: 1,
13+
isMenuItem: true,
1314
methods: {
1415
onKeyDown(e) {
1516
this.$refs.menuItem.onKeyDown(e);
@@ -20,22 +21,28 @@ export default {
2021
const { level, title, rootPrefixCls } = props;
2122
const { getInlineCollapsed, $slots, $attrs: attrs } = this;
2223
const inlineCollapsed = getInlineCollapsed();
23-
let titleNode;
24-
if (inlineCollapsed) {
25-
titleNode = title || (level === 1 ? $slots.default : '');
24+
const tooltipProps = {
25+
title: title || (level === 1 ? $slots.default : ''),
26+
};
27+
const siderCollapsed = this.layoutSiderContext.sCollapsed;
28+
if (!siderCollapsed && !inlineCollapsed) {
29+
tooltipProps.title = null;
30+
// Reset `visible` to fix control mode tooltip display not correct
31+
// ref: https://github.com/ant-design/ant-design/issues/16742
32+
tooltipProps.visible = false;
2633
}
2734

2835
const itemProps = {
2936
props: {
3037
...props,
31-
title: inlineCollapsed ? null : title,
38+
title,
3239
},
3340
attrs,
3441
on: getListeners(this),
3542
};
3643
const toolTipProps = {
3744
props: {
38-
title: titleNode,
45+
...tooltipProps,
3946
placement: 'right',
4047
overlayClassName: `${rootPrefixCls}-inline-collapsed-tooltip`,
4148
},

components/menu/SubMenu.jsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { SubMenu as VcSubMenu } from '../vc-menu';
2+
import { getListeners } from '../_util/props-util';
3+
import classNames from 'classnames';
4+
5+
export default {
6+
name: 'ASubMenu',
7+
isSubMenu: true,
8+
props: { ...VcSubMenu.props },
9+
inject: {
10+
menuPropsContext: { default: () => ({}) },
11+
},
12+
methods: {
13+
onKeyDown(e) {
14+
this.$refs.subMenu.onKeyDown(e);
15+
},
16+
},
17+
18+
render() {
19+
const { $slots, $scopedSlots } = this;
20+
const { rootPrefixCls, popupClassName } = this.$props;
21+
const { theme: antdMenuTheme } = this.menuPropsContext;
22+
const props = {
23+
props: {
24+
...this.$props,
25+
popupClassName: classNames(`${rootPrefixCls}-${antdMenuTheme}`, popupClassName),
26+
},
27+
ref: 'subMenu',
28+
on: getListeners(this),
29+
scopedSlots: $scopedSlots,
30+
};
31+
const slotsKey = Object.keys($slots);
32+
return (
33+
<VcSubMenu {...props}>
34+
{slotsKey.length
35+
? slotsKey.map(name => {
36+
return <template slot={name}>{$slots[name]}</template>;
37+
})
38+
: null}
39+
</VcSubMenu>
40+
);
41+
},
42+
};

components/menu/__tests__/index.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
22
import { asyncExpect } from '@/tests/utils';
33
import Menu from '..';
44
import Icon from '../../icon';
5+
import mountTest from '../../../tests/shared/mountTest';
56

67
jest.mock('mutationobserver-shim', () => {
78
global.MutationObserver = function MutationObserver() {
@@ -15,6 +16,17 @@ function $$(className) {
1516
return document.body.querySelectorAll(className);
1617
}
1718
describe('Menu', () => {
19+
mountTest({
20+
render() {
21+
return (
22+
<Menu>
23+
<Menu.Item />
24+
<Menu.ItemGroup />
25+
<Menu.SubMenu />
26+
</Menu>
27+
);
28+
},
29+
});
1830
beforeEach(() => {
1931
document.body.innerHTML = '';
2032
// jest.useFakeTimers()

components/menu/demo/index.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ const md = {
1616
导航菜单是一个网站的灵魂,用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航,顶部导航提供全局性的类目和功能,侧边导航提供多级结构来收纳和排列网站架构。
1717
## 代码演示`,
1818
us: `# Menu
19-
Menu list of Navigation.
19+
A versatile menu for navigation.
20+
2021
## When To Use
21-
Navigation menu is important for a website, it helps users jump from one site section to another quickly. Mostly, it includes top navigation and side navigation. Top navigation provides all the category and functions of the website. Side navigation provides the Multi-level structure of the website.
22+
23+
Navigation is an important part of any website, as a good navigation setup allows users to move around the site quickly and efficiently. Ant Design offers top and side navigation options. Top navigation provides all the categories and functions of the website. Side navigation provides the multi-level structure of the website.
24+
25+
More layouts with navigation: [Layout](/components/layout).
2226
## Examples`,
2327
};
2428
export default {

components/menu/index.en-US.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@
5050

5151
### Menu.SubMenu
5252

53-
| Param | Description | Type | Default value |
54-
| -------- | ----------------------------------- | ------------ | ------------- |
55-
| disabled | whether sub menu is disabled or not | boolean | false |
56-
| key | unique id of the sub menu | string | |
57-
| title | title of the sub menu | string\|slot | |
53+
| Param | Description | Type | Default value | Version |
54+
| -------------- | ----------------------------------- | ------------ | ------------- | ------- |
55+
| popupClassName | Sub-menu class name | string | | 1.5.0 |
56+
| disabled | whether sub menu is disabled or not | boolean | false | |
57+
| key | Unique ID of the sub menu | string | | |
58+
| title | title of the sub menu | string\|slot | | |
5859

5960
The children of Menu.SubMenu must be `MenuItem` or `SubMenu`.
6061

@@ -68,7 +69,7 @@ The children of Menu.SubMenu must be `MenuItem` or `SubMenu`.
6869

6970
| Param | Description | Type | Default value |
7071
| -------- | ------------------ | ------------ | ------------- |
71-
| children | sub menu items | MenuItem\[] | |
72+
| children | sub-menu items | MenuItem\[] | |
7273
| title | title of the group | string\|slot | |
7374

7475
The children of Menu.ItemGroup must be `MenuItem`.

components/menu/index.jsx

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import omit from 'omit.js';
2-
import VcMenu, { Divider, ItemGroup, SubMenu } from '../vc-menu';
2+
import VcMenu, { Divider, ItemGroup } from '../vc-menu';
3+
import SubMenu from './SubMenu';
34
import PropTypes from '../_util/vue-types';
45
import animation from '../_util/openAnimation';
56
import warning from '../_util/warning';
67
import Item from './MenuItem';
7-
import { hasProp, getListeners } from '../_util/props-util';
8+
import { hasProp, getListeners, getOptionProps } from '../_util/props-util';
89
import BaseMixin from '../_util/BaseMixin';
910
import commonPropsType from '../vc-menu/commonPropsType';
1011
import { ConfigConsumerProps } from '../config-provider';
1112
import Base from '../base';
13+
// import raf from '../_util/raf';
1214

1315
export const MenuMode = PropTypes.oneOf([
1416
'vertical',
@@ -47,6 +49,7 @@ const Menu = {
4749
provide() {
4850
return {
4951
getInlineCollapsed: this.getInlineCollapsed,
52+
menuPropsContext: this.$props,
5053
};
5154
},
5255
mixins: [BaseMixin],
@@ -58,12 +61,12 @@ const Menu = {
5861
prop: 'selectedKeys',
5962
event: 'selectChange',
6063
},
61-
created() {
62-
this.preProps = { ...this.$props };
63-
},
6464
updated() {
6565
this.propsUpdating = false;
6666
},
67+
// beforeDestroy() {
68+
// raf.cancel(this.mountRafId);
69+
// },
6770
watch: {
6871
mode(val, oldVal) {
6972
if (oldVal === 'inline' && val !== 'inline') {
@@ -81,19 +84,20 @@ const Menu = {
8184
},
8285
},
8386
data() {
84-
const props = this.$props;
87+
const props = getOptionProps(this);
8588
warning(
86-
!(hasProp(this, 'inlineCollapsed') && props.mode !== 'inline'),
89+
!('inlineCollapsed' in props && props.mode !== 'inline'),
90+
'Menu',
8791
"`inlineCollapsed` should only be used when Menu's `mode` is inline.",
8892
);
8993
this.switchingModeFromInline = false;
9094
this.leaveAnimationExecutedWhenInlineCollapsed = false;
9195
this.inlineOpenKeys = [];
9296
let sOpenKeys;
9397

94-
if (hasProp(this, 'openKeys')) {
98+
if ('openKeys' in props) {
9599
sOpenKeys = props.openKeys;
96-
} else if (hasProp(this, 'defaultOpenKeys')) {
100+
} else if ('defaultOpenKeys' in props) {
97101
sOpenKeys = props.defaultOpenKeys;
98102
}
99103
return {
@@ -137,10 +141,20 @@ const Menu = {
137141
// when inlineCollapsed menu width animation finished
138142
// https://github.com/ant-design/ant-design/issues/12864
139143
const widthCollapsed = e.propertyName === 'width' && e.target === e.currentTarget;
144+
145+
// Fix SVGElement e.target.className.indexOf is not a function
146+
// https://github.com/ant-design/ant-design/issues/15699
147+
const { className } = e.target;
148+
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during an animation.
149+
const classNameValue =
150+
Object.prototype.toString.call(className) === '[object SVGAnimatedString]'
151+
? className.animVal
152+
: className;
153+
140154
// Fix for <Menu style={{ width: '100%' }} />, the width transition won't trigger when menu is collapsed
141155
// https://github.com/ant-design/ant-design-pro/issues/2783
142-
const iconScaled =
143-
e.propertyName === 'font-size' && e.target.className.indexOf('anticon') >= 0;
156+
const iconScaled = e.propertyName === 'font-size' && classNameValue.indexOf('anticon') >= 0;
157+
144158
if (widthCollapsed || iconScaled) {
145159
this.restoreModeVerticalFromInline();
146160
}
@@ -254,11 +268,11 @@ const Menu = {
254268
}
255269

256270
// https://github.com/ant-design/ant-design/issues/8587
257-
if (
271+
const hideMenu =
258272
this.getInlineCollapsed() &&
259-
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px')
260-
) {
261-
return null;
273+
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
274+
if (hideMenu) {
275+
menuProps.props.openKeys = [];
262276
}
263277

264278
return (

components/menu/index.zh-CN.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@
4949

5050
### Menu.SubMenu
5151

52-
| 参数 | 说明 | 类型 | 默认值 |
53-
| -------- | ---------- | ------------ | ------ |
54-
| disabled | 是否禁用 | boolean | false |
55-
| key | 唯一标志 | string | |
56-
| title | 子菜单项值 | string\|slot | |
52+
| 参数 | 说明 | 类型 | 默认值 | 版本 |
53+
| -------------- | ---------- | ------------ | ------ | ----- |
54+
| popupClassName | 子菜单样式 | string | | 1.5.0 |
55+
| disabled | 是否禁用 | boolean | false | |
56+
| key | 唯一标志 | string | | |
57+
| title | 子菜单项值 | string\|slot | | |
5758

5859
Menu.SubMenu 的子元素必须是 `MenuItem` 或者 `SubMenu`.
5960

components/vc-menu/DOMWrap.jsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const DOMWrap = {
8585
this.resizeObserver.disconnect();
8686
}
8787
if (this.mutationObserver) {
88-
this.resizeObserver.disconnect();
88+
this.mutationObserver.disconnect();
8989
}
9090
},
9191
methods: {
@@ -98,9 +98,9 @@ const DOMWrap = {
9898
}
9999

100100
// filter out all overflowed indicator placeholder
101-
return [].slice.call(ul.children).filter(node => {
102-
return node.className.split(' ').indexOf(`${prefixCls}-overflowed-submenu`) < 0;
103-
});
101+
return [].slice
102+
.call(ul.children)
103+
.filter(node => node.className.split(' ').indexOf(`${prefixCls}-overflowed-submenu`) < 0);
104104
},
105105

106106
getOverflowedSubMenuItem(keyPrefix, overflowedItems, renderPlaceholder) {
@@ -111,10 +111,11 @@ const DOMWrap = {
111111
// put all the overflowed item inside a submenu
112112
// with a title of overflow indicator ('...')
113113
const copy = this.$slots.default[0];
114-
const { title, eventKey, ...rest } = getPropsData(copy); // eslint-disable-line no-unused-vars
114+
const { title, ...rest } = getPropsData(copy); // eslint-disable-line no-unused-vars
115115

116116
let style = {};
117117
let key = `${keyPrefix}-overflowed-indicator`;
118+
let eventKey = `${keyPrefix}-overflowed-indicator`;
118119

119120
if (overflowedItems.length === 0 && renderPlaceholder !== true) {
120121
style = {
@@ -127,6 +128,7 @@ const DOMWrap = {
127128
position: 'absolute',
128129
};
129130
key = `${key}-placeholder`;
131+
eventKey = `${eventKey}-placeholder`;
130132
}
131133

132134
const popupClassName = theme ? `${prefixCls}-${theme}` : '';
@@ -141,7 +143,7 @@ const DOMWrap = {
141143
title: overflowedIndicator,
142144
popupClassName,
143145
...props,
144-
eventKey: `${keyPrefix}-overflowed-indicator`,
146+
eventKey,
145147
disabled: false,
146148
},
147149
class: `${prefixCls}-overflowed-submenu`,
@@ -226,7 +228,7 @@ const DOMWrap = {
226228
this.menuItemSizes.forEach(liWidth => {
227229
currentSumWidth += liWidth;
228230
if (currentSumWidth + this.overflowedIndicatorWidth <= width) {
229-
lastVisibleIndex++;
231+
lastVisibleIndex += 1;
230232
}
231233
});
232234
}
@@ -251,7 +253,7 @@ const DOMWrap = {
251253
{
252254
style: { display: 'none' },
253255
props: { eventKey: `${eventKey}-hidden` },
254-
class: { ...getClass(childNode), [MENUITEM_OVERFLOWED_CLASSNAME]: true },
256+
class: MENUITEM_OVERFLOWED_CLASSNAME,
255257
},
256258
);
257259
}

components/vc-menu/MenuItem.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,22 @@ const MenuItem = {
4040
mixins: [BaseMixin],
4141
isMenuItem: true,
4242
created() {
43+
this.prevActive = this.active;
4344
// invoke customized ref to expose component to mixin
4445
this.callRef();
4546
},
4647
updated() {
4748
this.$nextTick(() => {
48-
if (this.active) {
49+
const { active, parentMenu, eventKey } = this.$props;
50+
if (!this.prevActive && active && (!parentMenu || !parentMenu[`scrolled-${eventKey}`])) {
4951
scrollIntoView(this.$el, this.parentMenu.$el, {
5052
onlyScrollIfNeeded: true,
5153
});
54+
parentMenu[`scrolled-${eventKey}`] = true;
55+
} else if (parentMenu && parentMenu[`scrolled-${eventKey}`]) {
56+
delete parentMenu[`scrolled-${eventKey}`];
5257
}
58+
this.prevActive = active;
5359
});
5460
this.callRef();
5561
},

0 commit comments

Comments
 (0)