feat: update menu
parent
c7b0cb0732
commit
b85bc0e738
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
dev: {
|
dev: {
|
||||||
componentName: 'list', // dev components
|
componentName: 'menu', // dev components
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,8 +8,9 @@ export default {
|
||||||
props: itemProps,
|
props: itemProps,
|
||||||
inject: {
|
inject: {
|
||||||
getInlineCollapsed: { default: () => noop },
|
getInlineCollapsed: { default: () => noop },
|
||||||
|
layoutSiderContext: { default: () => ({}) },
|
||||||
},
|
},
|
||||||
isMenuItem: 1,
|
isMenuItem: true,
|
||||||
methods: {
|
methods: {
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
this.$refs.menuItem.onKeyDown(e);
|
this.$refs.menuItem.onKeyDown(e);
|
||||||
|
@ -20,22 +21,28 @@ export default {
|
||||||
const { level, title, rootPrefixCls } = props;
|
const { level, title, rootPrefixCls } = props;
|
||||||
const { getInlineCollapsed, $slots, $attrs: attrs } = this;
|
const { getInlineCollapsed, $slots, $attrs: attrs } = this;
|
||||||
const inlineCollapsed = getInlineCollapsed();
|
const inlineCollapsed = getInlineCollapsed();
|
||||||
let titleNode;
|
const tooltipProps = {
|
||||||
if (inlineCollapsed) {
|
title: title || (level === 1 ? $slots.default : ''),
|
||||||
titleNode = title || (level === 1 ? $slots.default : '');
|
};
|
||||||
|
const siderCollapsed = this.layoutSiderContext.sCollapsed;
|
||||||
|
if (!siderCollapsed && !inlineCollapsed) {
|
||||||
|
tooltipProps.title = null;
|
||||||
|
// Reset `visible` to fix control mode tooltip display not correct
|
||||||
|
// ref: https://github.com/ant-design/ant-design/issues/16742
|
||||||
|
tooltipProps.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemProps = {
|
const itemProps = {
|
||||||
props: {
|
props: {
|
||||||
...props,
|
...props,
|
||||||
title: inlineCollapsed ? null : title,
|
title,
|
||||||
},
|
},
|
||||||
attrs,
|
attrs,
|
||||||
on: getListeners(this),
|
on: getListeners(this),
|
||||||
};
|
};
|
||||||
const toolTipProps = {
|
const toolTipProps = {
|
||||||
props: {
|
props: {
|
||||||
title: titleNode,
|
...tooltipProps,
|
||||||
placement: 'right',
|
placement: 'right',
|
||||||
overlayClassName: `${rootPrefixCls}-inline-collapsed-tooltip`,
|
overlayClassName: `${rootPrefixCls}-inline-collapsed-tooltip`,
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { SubMenu as VcSubMenu } from '../vc-menu';
|
||||||
|
import { getListeners } from '../_util/props-util';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ASubMenu',
|
||||||
|
isSubMenu: true,
|
||||||
|
props: { ...VcSubMenu.props },
|
||||||
|
inject: {
|
||||||
|
menuPropsContext: { default: () => ({}) },
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onKeyDown(e) {
|
||||||
|
this.$refs.subMenu.onKeyDown(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { $slots, $scopedSlots } = this;
|
||||||
|
const { rootPrefixCls, popupClassName } = this.$props;
|
||||||
|
const { theme: antdMenuTheme } = this.menuPropsContext;
|
||||||
|
const props = {
|
||||||
|
props: {
|
||||||
|
...this.$props,
|
||||||
|
popupClassName: classNames(`${rootPrefixCls}-${antdMenuTheme}`, popupClassName),
|
||||||
|
},
|
||||||
|
ref: 'subMenu',
|
||||||
|
on: getListeners(this),
|
||||||
|
scopedSlots: $scopedSlots,
|
||||||
|
};
|
||||||
|
const slotsKey = Object.keys($slots);
|
||||||
|
return (
|
||||||
|
<VcSubMenu {...props}>
|
||||||
|
{slotsKey.length
|
||||||
|
? slotsKey.map(name => {
|
||||||
|
return <template slot={name}>{$slots[name]}</template>;
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
</VcSubMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
|
||||||
import { asyncExpect } from '@/tests/utils';
|
import { asyncExpect } from '@/tests/utils';
|
||||||
import Menu from '..';
|
import Menu from '..';
|
||||||
import Icon from '../../icon';
|
import Icon from '../../icon';
|
||||||
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
|
|
||||||
jest.mock('mutationobserver-shim', () => {
|
jest.mock('mutationobserver-shim', () => {
|
||||||
global.MutationObserver = function MutationObserver() {
|
global.MutationObserver = function MutationObserver() {
|
||||||
|
@ -15,6 +16,17 @@ function $$(className) {
|
||||||
return document.body.querySelectorAll(className);
|
return document.body.querySelectorAll(className);
|
||||||
}
|
}
|
||||||
describe('Menu', () => {
|
describe('Menu', () => {
|
||||||
|
mountTest({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item />
|
||||||
|
<Menu.ItemGroup />
|
||||||
|
<Menu.SubMenu />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
document.body.innerHTML = '';
|
document.body.innerHTML = '';
|
||||||
// jest.useFakeTimers()
|
// jest.useFakeTimers()
|
||||||
|
|
|
@ -16,9 +16,13 @@ const md = {
|
||||||
导航菜单是一个网站的灵魂,用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航,顶部导航提供全局性的类目和功能,侧边导航提供多级结构来收纳和排列网站架构。
|
导航菜单是一个网站的灵魂,用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航,顶部导航提供全局性的类目和功能,侧边导航提供多级结构来收纳和排列网站架构。
|
||||||
## 代码演示`,
|
## 代码演示`,
|
||||||
us: `# Menu
|
us: `# Menu
|
||||||
Menu list of Navigation.
|
A versatile menu for navigation.
|
||||||
|
|
||||||
## When To Use
|
## When To Use
|
||||||
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.
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
More layouts with navigation: [Layout](/components/layout).
|
||||||
## Examples`,
|
## Examples`,
|
||||||
};
|
};
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -50,11 +50,12 @@
|
||||||
|
|
||||||
### Menu.SubMenu
|
### Menu.SubMenu
|
||||||
|
|
||||||
| Param | Description | Type | Default value |
|
| Param | Description | Type | Default value | Version |
|
||||||
| -------- | ----------------------------------- | ------------ | ------------- |
|
| -------------- | ----------------------------------- | ------------ | ------------- | ------- |
|
||||||
| disabled | whether sub menu is disabled or not | boolean | false |
|
| popupClassName | Sub-menu class name | string | | 1.5.0 |
|
||||||
| key | unique id of the sub menu | string | |
|
| disabled | whether sub menu is disabled or not | boolean | false | |
|
||||||
| title | title of the sub menu | string\|slot | |
|
| key | Unique ID of the sub menu | string | | |
|
||||||
|
| title | title of the sub menu | string\|slot | | |
|
||||||
|
|
||||||
The children of Menu.SubMenu must be `MenuItem` or `SubMenu`.
|
The children of Menu.SubMenu must be `MenuItem` or `SubMenu`.
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ The children of Menu.SubMenu must be `MenuItem` or `SubMenu`.
|
||||||
|
|
||||||
| Param | Description | Type | Default value |
|
| Param | Description | Type | Default value |
|
||||||
| -------- | ------------------ | ------------ | ------------- |
|
| -------- | ------------------ | ------------ | ------------- |
|
||||||
| children | sub menu items | MenuItem\[] | |
|
| children | sub-menu items | MenuItem\[] | |
|
||||||
| title | title of the group | string\|slot | |
|
| title | title of the group | string\|slot | |
|
||||||
|
|
||||||
The children of Menu.ItemGroup must be `MenuItem`.
|
The children of Menu.ItemGroup must be `MenuItem`.
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import omit from 'omit.js';
|
import omit from 'omit.js';
|
||||||
import VcMenu, { Divider, ItemGroup, SubMenu } from '../vc-menu';
|
import VcMenu, { Divider, ItemGroup } from '../vc-menu';
|
||||||
|
import SubMenu from './SubMenu';
|
||||||
import PropTypes from '../_util/vue-types';
|
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 } from '../_util/props-util';
|
import { hasProp, getListeners, getOptionProps } 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 Base from '../base';
|
||||||
|
// import raf from '../_util/raf';
|
||||||
|
|
||||||
export const MenuMode = PropTypes.oneOf([
|
export const MenuMode = PropTypes.oneOf([
|
||||||
'vertical',
|
'vertical',
|
||||||
|
@ -47,6 +49,7 @@ const Menu = {
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
getInlineCollapsed: this.getInlineCollapsed,
|
getInlineCollapsed: this.getInlineCollapsed,
|
||||||
|
menuPropsContext: this.$props,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mixins: [BaseMixin],
|
mixins: [BaseMixin],
|
||||||
|
@ -58,12 +61,12 @@ const Menu = {
|
||||||
prop: 'selectedKeys',
|
prop: 'selectedKeys',
|
||||||
event: 'selectChange',
|
event: 'selectChange',
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.preProps = { ...this.$props };
|
|
||||||
},
|
|
||||||
updated() {
|
updated() {
|
||||||
this.propsUpdating = false;
|
this.propsUpdating = false;
|
||||||
},
|
},
|
||||||
|
// beforeDestroy() {
|
||||||
|
// raf.cancel(this.mountRafId);
|
||||||
|
// },
|
||||||
watch: {
|
watch: {
|
||||||
mode(val, oldVal) {
|
mode(val, oldVal) {
|
||||||
if (oldVal === 'inline' && val !== 'inline') {
|
if (oldVal === 'inline' && val !== 'inline') {
|
||||||
|
@ -81,9 +84,10 @@ const Menu = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const props = this.$props;
|
const props = getOptionProps(this);
|
||||||
warning(
|
warning(
|
||||||
!(hasProp(this, 'inlineCollapsed') && props.mode !== 'inline'),
|
!('inlineCollapsed' in props && props.mode !== 'inline'),
|
||||||
|
'Menu',
|
||||||
"`inlineCollapsed` should only be used when Menu's `mode` is inline.",
|
"`inlineCollapsed` should only be used when Menu's `mode` is inline.",
|
||||||
);
|
);
|
||||||
this.switchingModeFromInline = false;
|
this.switchingModeFromInline = false;
|
||||||
|
@ -91,9 +95,9 @@ const Menu = {
|
||||||
this.inlineOpenKeys = [];
|
this.inlineOpenKeys = [];
|
||||||
let sOpenKeys;
|
let sOpenKeys;
|
||||||
|
|
||||||
if (hasProp(this, 'openKeys')) {
|
if ('openKeys' in props) {
|
||||||
sOpenKeys = props.openKeys;
|
sOpenKeys = props.openKeys;
|
||||||
} else if (hasProp(this, 'defaultOpenKeys')) {
|
} else if ('defaultOpenKeys' in props) {
|
||||||
sOpenKeys = props.defaultOpenKeys;
|
sOpenKeys = props.defaultOpenKeys;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -137,10 +141,20 @@ const Menu = {
|
||||||
// when inlineCollapsed menu width animation finished
|
// when inlineCollapsed menu width animation finished
|
||||||
// https://github.com/ant-design/ant-design/issues/12864
|
// https://github.com/ant-design/ant-design/issues/12864
|
||||||
const widthCollapsed = e.propertyName === 'width' && e.target === e.currentTarget;
|
const widthCollapsed = e.propertyName === 'width' && e.target === e.currentTarget;
|
||||||
|
|
||||||
|
// Fix SVGElement e.target.className.indexOf is not a function
|
||||||
|
// https://github.com/ant-design/ant-design/issues/15699
|
||||||
|
const { className } = e.target;
|
||||||
|
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during an animation.
|
||||||
|
const classNameValue =
|
||||||
|
Object.prototype.toString.call(className) === '[object SVGAnimatedString]'
|
||||||
|
? className.animVal
|
||||||
|
: className;
|
||||||
|
|
||||||
// Fix for <Menu style={{ width: '100%' }} />, the width transition won't trigger when menu is collapsed
|
// Fix for <Menu style={{ width: '100%' }} />, the width transition won't trigger when menu is collapsed
|
||||||
// https://github.com/ant-design/ant-design-pro/issues/2783
|
// https://github.com/ant-design/ant-design-pro/issues/2783
|
||||||
const iconScaled =
|
const iconScaled = e.propertyName === 'font-size' && classNameValue.indexOf('anticon') >= 0;
|
||||||
e.propertyName === 'font-size' && e.target.className.indexOf('anticon') >= 0;
|
|
||||||
if (widthCollapsed || iconScaled) {
|
if (widthCollapsed || iconScaled) {
|
||||||
this.restoreModeVerticalFromInline();
|
this.restoreModeVerticalFromInline();
|
||||||
}
|
}
|
||||||
|
@ -254,11 +268,11 @@ const Menu = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/ant-design/ant-design/issues/8587
|
// https://github.com/ant-design/ant-design/issues/8587
|
||||||
if (
|
const hideMenu =
|
||||||
this.getInlineCollapsed() &&
|
this.getInlineCollapsed() &&
|
||||||
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px')
|
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
|
||||||
) {
|
if (hideMenu) {
|
||||||
return null;
|
menuProps.props.openKeys = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -49,11 +49,12 @@
|
||||||
|
|
||||||
### Menu.SubMenu
|
### Menu.SubMenu
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| -------- | ---------- | ------------ | ------ |
|
| -------------- | ---------- | ------------ | ------ | ----- |
|
||||||
| disabled | 是否禁用 | boolean | false |
|
| popupClassName | 子菜单样式 | string | | 1.5.0 |
|
||||||
| key | 唯一标志 | string | |
|
| disabled | 是否禁用 | boolean | false | |
|
||||||
| title | 子菜单项值 | string\|slot | |
|
| key | 唯一标志 | string | | |
|
||||||
|
| title | 子菜单项值 | string\|slot | | |
|
||||||
|
|
||||||
Menu.SubMenu 的子元素必须是 `MenuItem` 或者 `SubMenu`.
|
Menu.SubMenu 的子元素必须是 `MenuItem` 或者 `SubMenu`.
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ const DOMWrap = {
|
||||||
this.resizeObserver.disconnect();
|
this.resizeObserver.disconnect();
|
||||||
}
|
}
|
||||||
if (this.mutationObserver) {
|
if (this.mutationObserver) {
|
||||||
this.resizeObserver.disconnect();
|
this.mutationObserver.disconnect();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -98,9 +98,9 @@ const DOMWrap = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter out all overflowed indicator placeholder
|
// filter out all overflowed indicator placeholder
|
||||||
return [].slice.call(ul.children).filter(node => {
|
return [].slice
|
||||||
return node.className.split(' ').indexOf(`${prefixCls}-overflowed-submenu`) < 0;
|
.call(ul.children)
|
||||||
});
|
.filter(node => node.className.split(' ').indexOf(`${prefixCls}-overflowed-submenu`) < 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
getOverflowedSubMenuItem(keyPrefix, overflowedItems, renderPlaceholder) {
|
getOverflowedSubMenuItem(keyPrefix, overflowedItems, renderPlaceholder) {
|
||||||
|
@ -111,10 +111,11 @@ 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 = this.$slots.default[0];
|
||||||
const { title, eventKey, ...rest } = getPropsData(copy); // eslint-disable-line no-unused-vars
|
const { title, ...rest } = getPropsData(copy); // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
let style = {};
|
let style = {};
|
||||||
let key = `${keyPrefix}-overflowed-indicator`;
|
let key = `${keyPrefix}-overflowed-indicator`;
|
||||||
|
let eventKey = `${keyPrefix}-overflowed-indicator`;
|
||||||
|
|
||||||
if (overflowedItems.length === 0 && renderPlaceholder !== true) {
|
if (overflowedItems.length === 0 && renderPlaceholder !== true) {
|
||||||
style = {
|
style = {
|
||||||
|
@ -127,6 +128,7 @@ const DOMWrap = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
};
|
};
|
||||||
key = `${key}-placeholder`;
|
key = `${key}-placeholder`;
|
||||||
|
eventKey = `${eventKey}-placeholder`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const popupClassName = theme ? `${prefixCls}-${theme}` : '';
|
const popupClassName = theme ? `${prefixCls}-${theme}` : '';
|
||||||
|
@ -141,7 +143,7 @@ const DOMWrap = {
|
||||||
title: overflowedIndicator,
|
title: overflowedIndicator,
|
||||||
popupClassName,
|
popupClassName,
|
||||||
...props,
|
...props,
|
||||||
eventKey: `${keyPrefix}-overflowed-indicator`,
|
eventKey,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
class: `${prefixCls}-overflowed-submenu`,
|
class: `${prefixCls}-overflowed-submenu`,
|
||||||
|
@ -226,7 +228,7 @@ const DOMWrap = {
|
||||||
this.menuItemSizes.forEach(liWidth => {
|
this.menuItemSizes.forEach(liWidth => {
|
||||||
currentSumWidth += liWidth;
|
currentSumWidth += liWidth;
|
||||||
if (currentSumWidth + this.overflowedIndicatorWidth <= width) {
|
if (currentSumWidth + this.overflowedIndicatorWidth <= width) {
|
||||||
lastVisibleIndex++;
|
lastVisibleIndex += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -251,7 +253,7 @@ const DOMWrap = {
|
||||||
{
|
{
|
||||||
style: { display: 'none' },
|
style: { display: 'none' },
|
||||||
props: { eventKey: `${eventKey}-hidden` },
|
props: { eventKey: `${eventKey}-hidden` },
|
||||||
class: { ...getClass(childNode), [MENUITEM_OVERFLOWED_CLASSNAME]: true },
|
class: MENUITEM_OVERFLOWED_CLASSNAME,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,16 +40,22 @@ const MenuItem = {
|
||||||
mixins: [BaseMixin],
|
mixins: [BaseMixin],
|
||||||
isMenuItem: true,
|
isMenuItem: true,
|
||||||
created() {
|
created() {
|
||||||
|
this.prevActive = this.active;
|
||||||
// invoke customized ref to expose component to mixin
|
// invoke customized ref to expose component to mixin
|
||||||
this.callRef();
|
this.callRef();
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.active) {
|
const { active, parentMenu, eventKey } = this.$props;
|
||||||
|
if (!this.prevActive && active && (!parentMenu || !parentMenu[`scrolled-${eventKey}`])) {
|
||||||
scrollIntoView(this.$el, this.parentMenu.$el, {
|
scrollIntoView(this.$el, this.parentMenu.$el, {
|
||||||
onlyScrollIfNeeded: true,
|
onlyScrollIfNeeded: true,
|
||||||
});
|
});
|
||||||
|
parentMenu[`scrolled-${eventKey}`] = true;
|
||||||
|
} else if (parentMenu && parentMenu[`scrolled-${eventKey}`]) {
|
||||||
|
delete parentMenu[`scrolled-${eventKey}`];
|
||||||
}
|
}
|
||||||
|
this.prevActive = active;
|
||||||
});
|
});
|
||||||
this.callRef();
|
this.callRef();
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { getComponentFromProp, getListeners } from '../_util/props-util';
|
import { getComponentFromProp, getListeners } from '../_util/props-util';
|
||||||
|
|
||||||
// import { menuAllProps } from './util'
|
// import { menuAllProps } from './util'
|
||||||
|
|
||||||
const MenuItemGroup = {
|
const MenuItemGroup = {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { getComponentFromProp, filterEmpty, getListeners } from '../_util/props-
|
||||||
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';
|
||||||
|
import { MenuItem } from './MenuItem';
|
||||||
|
|
||||||
let guid = 0;
|
let guid = 0;
|
||||||
|
|
||||||
|
@ -171,6 +172,7 @@ const SubMenu = {
|
||||||
if (isOpen && (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN)) {
|
if (isOpen && (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN)) {
|
||||||
return menu.onKeyDown(e);
|
return menu.onKeyDown(e);
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
onPopupVisibleChange(visible) {
|
onPopupVisibleChange(visible) {
|
||||||
|
@ -368,7 +370,7 @@ const SubMenu = {
|
||||||
deselect,
|
deselect,
|
||||||
openChange,
|
openChange,
|
||||||
},
|
},
|
||||||
id: this._menuId,
|
id: this.internalMenuId,
|
||||||
};
|
};
|
||||||
const baseProps = subPopupMenuProps.props;
|
const baseProps = subPopupMenuProps.props;
|
||||||
const haveRendered = this.haveRendered;
|
const haveRendered = this.haveRendered;
|
||||||
|
@ -429,11 +431,11 @@ const SubMenu = {
|
||||||
[this.getSelectedClassName()]: this.isChildrenSelected(),
|
[this.getSelectedClassName()]: this.isChildrenSelected(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this._menuId) {
|
if (!this.internalMenuId) {
|
||||||
if (props.eventKey) {
|
if (props.eventKey) {
|
||||||
this._menuId = `${props.eventKey}$Menu`;
|
this.internalMenuId = `${props.eventKey}$Menu`;
|
||||||
} else {
|
} else {
|
||||||
this._menuId = `$__$${++guid}$Menu`;
|
this.internalMenuId = `$__$${++guid}$Menu`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +468,7 @@ const SubMenu = {
|
||||||
// since corresponding node cannot be found
|
// since corresponding node cannot be found
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
ariaOwns = {
|
ariaOwns = {
|
||||||
'aria-owns': this._menuId,
|
'aria-owns': this.internalMenuId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const titleProps = {
|
const titleProps = {
|
||||||
|
|
|
@ -193,6 +193,7 @@ const SubPopupMenu = {
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
onItemHover(e) {
|
onItemHover(e) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// based on rc-menu 7.4.21
|
// based on rc-menu 7.5.5
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
import SubMenu from './SubMenu';
|
import SubMenu from './SubMenu';
|
||||||
import MenuItem, { menuItemProps } from './MenuItem';
|
import MenuItem, { menuItemProps } from './MenuItem';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const isMobile = require('ismobilejs');
|
import isMobile from './utils/isMobile';
|
||||||
|
|
||||||
export function noop() {}
|
export function noop() {}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
// MIT License from https://github.com/kaimallea/isMobile
|
||||||
|
|
||||||
|
const applePhone = /iPhone/i;
|
||||||
|
const appleIpod = /iPod/i;
|
||||||
|
const appleTablet = /iPad/i;
|
||||||
|
const androidPhone = /\bAndroid(?:.+)Mobile\b/i; // Match 'Android' AND 'Mobile'
|
||||||
|
const androidTablet = /Android/i;
|
||||||
|
const amazonPhone = /\bAndroid(?:.+)SD4930UR\b/i;
|
||||||
|
const amazonTablet = /\bAndroid(?:.+)(?:KF[A-Z]{2,4})\b/i;
|
||||||
|
const windowsPhone = /Windows Phone/i;
|
||||||
|
const windowsTablet = /\bWindows(?:.+)ARM\b/i; // Match 'Windows' AND 'ARM'
|
||||||
|
const otherBlackberry = /BlackBerry/i;
|
||||||
|
const otherBlackberry10 = /BB10/i;
|
||||||
|
const otherOpera = /Opera Mini/i;
|
||||||
|
const otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i;
|
||||||
|
const otherFirefox = /Mobile(?:.+)Firefox\b/i; // Match 'Mobile' AND 'Firefox'
|
||||||
|
|
||||||
|
function match(regex, userAgent) {
|
||||||
|
return regex.test(userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMobile(userAgent) {
|
||||||
|
let ua = userAgent || (typeof navigator !== 'undefined' ? navigator.userAgent : '');
|
||||||
|
|
||||||
|
// Facebook mobile app's integrated browser adds a bunch of strings that
|
||||||
|
// match everything. Strip it out if it exists.
|
||||||
|
let tmp = ua.split('[FBAN');
|
||||||
|
if (typeof tmp[1] !== 'undefined') {
|
||||||
|
[ua] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Twitter mobile app's integrated browser on iPad adds a "Twitter for
|
||||||
|
// iPhone" string. Same probably happens on other tablet platforms.
|
||||||
|
// This will confuse detection so strip it out if it exists.
|
||||||
|
tmp = ua.split('Twitter');
|
||||||
|
if (typeof tmp[1] !== 'undefined') {
|
||||||
|
[ua] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
apple: {
|
||||||
|
phone: match(applePhone, ua) && !match(windowsPhone, ua),
|
||||||
|
ipod: match(appleIpod, ua),
|
||||||
|
tablet: !match(applePhone, ua) && match(appleTablet, ua) && !match(windowsPhone, ua),
|
||||||
|
device:
|
||||||
|
(match(applePhone, ua) || match(appleIpod, ua) || match(appleTablet, ua)) &&
|
||||||
|
!match(windowsPhone, ua),
|
||||||
|
},
|
||||||
|
amazon: {
|
||||||
|
phone: match(amazonPhone, ua),
|
||||||
|
tablet: !match(amazonPhone, ua) && match(amazonTablet, ua),
|
||||||
|
device: match(amazonPhone, ua) || match(amazonTablet, ua),
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
phone:
|
||||||
|
(!match(windowsPhone, ua) && match(amazonPhone, ua)) ||
|
||||||
|
(!match(windowsPhone, ua) && match(androidPhone, ua)),
|
||||||
|
tablet:
|
||||||
|
!match(windowsPhone, ua) &&
|
||||||
|
!match(amazonPhone, ua) &&
|
||||||
|
!match(androidPhone, ua) &&
|
||||||
|
(match(amazonTablet, ua) || match(androidTablet, ua)),
|
||||||
|
device:
|
||||||
|
(!match(windowsPhone, ua) &&
|
||||||
|
(match(amazonPhone, ua) ||
|
||||||
|
match(amazonTablet, ua) ||
|
||||||
|
match(androidPhone, ua) ||
|
||||||
|
match(androidTablet, ua))) ||
|
||||||
|
match(/\bokhttp\b/i, ua),
|
||||||
|
},
|
||||||
|
windows: {
|
||||||
|
phone: match(windowsPhone, ua),
|
||||||
|
tablet: match(windowsTablet, ua),
|
||||||
|
device: match(windowsPhone, ua) || match(windowsTablet, ua),
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
blackberry: match(otherBlackberry, ua),
|
||||||
|
blackberry10: match(otherBlackberry10, ua),
|
||||||
|
opera: match(otherOpera, ua),
|
||||||
|
firefox: match(otherFirefox, ua),
|
||||||
|
chrome: match(otherChrome, ua),
|
||||||
|
device:
|
||||||
|
match(otherBlackberry, ua) ||
|
||||||
|
match(otherBlackberry10, ua) ||
|
||||||
|
match(otherOpera, ua) ||
|
||||||
|
match(otherFirefox, ua) ||
|
||||||
|
match(otherChrome, ua),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Additional
|
||||||
|
any: null,
|
||||||
|
phone: null,
|
||||||
|
tablet: null,
|
||||||
|
};
|
||||||
|
result.any =
|
||||||
|
result.apple.device || result.android.device || result.windows.device || result.other.device;
|
||||||
|
|
||||||
|
// excludes 'other' devices and ipods, targeting touchscreen phones
|
||||||
|
result.phone = result.apple.phone || result.android.phone || result.windows.phone;
|
||||||
|
result.tablet = result.apple.tablet || result.android.tablet || result.windows.tablet;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultResult = {
|
||||||
|
...isMobile(),
|
||||||
|
isMobile,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defaultResult;
|
|
@ -24,4 +24,6 @@ export declare class SubMenu extends AntdComponent {
|
||||||
* @type string | slot
|
* @type string | slot
|
||||||
*/
|
*/
|
||||||
title: any;
|
title: any;
|
||||||
|
|
||||||
|
popupClassName: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue