mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-29 13:47:02 +08:00
Merge pull request #6686 from ant-design/feat-collapsed-menu
Better collapsed Sider and Menu
This commit is contained in:
commit
87932504b4
@ -17,6 +17,7 @@ if (typeof window !== 'undefined') {
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from '../icon';
|
||||
|
||||
const dimensionMap = {
|
||||
@ -55,6 +56,10 @@ export default class Sider extends React.Component<SiderProps, any> {
|
||||
style: {},
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
siderCollapsed: PropTypes.bool,
|
||||
};
|
||||
|
||||
private mql: any;
|
||||
|
||||
constructor(props) {
|
||||
@ -78,6 +83,12 @@ export default class Sider extends React.Component<SiderProps, any> {
|
||||
};
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
siderCollapsed: this.props.collapsed,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if ('collapsed' in nextProps) {
|
||||
this.setState({
|
||||
|
@ -152,9 +152,7 @@ exports[`renders ./components/layout/demo/custom-trigger.md correctly 1`] = `
|
||||
<i
|
||||
class="anticon anticon-user"
|
||||
/>
|
||||
<span
|
||||
class="nav-text"
|
||||
>
|
||||
<span>
|
||||
nav 1
|
||||
</span>
|
||||
</li>
|
||||
@ -167,9 +165,7 @@ exports[`renders ./components/layout/demo/custom-trigger.md correctly 1`] = `
|
||||
<i
|
||||
class="anticon anticon-video-camera"
|
||||
/>
|
||||
<span
|
||||
class="nav-text"
|
||||
>
|
||||
<span>
|
||||
nav 2
|
||||
</span>
|
||||
</li>
|
||||
@ -182,9 +178,7 @@ exports[`renders ./components/layout/demo/custom-trigger.md correctly 1`] = `
|
||||
<i
|
||||
class="anticon anticon-upload"
|
||||
/>
|
||||
<span
|
||||
class="nav-text"
|
||||
>
|
||||
<span>
|
||||
nav 3
|
||||
</span>
|
||||
</li>
|
||||
@ -708,6 +702,32 @@ exports[`renders ./components/layout/demo/side.md correctly 1`] = `
|
||||
role="menu"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
aria-selected="true"
|
||||
class="ant-menu-item-selected ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-pie-chart"
|
||||
/>
|
||||
<span>
|
||||
Option 1
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-desktop"
|
||||
/>
|
||||
<span>
|
||||
Option 2
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu"
|
||||
>
|
||||
@ -722,9 +742,7 @@ exports[`renders ./components/layout/demo/side.md correctly 1`] = `
|
||||
<i
|
||||
class="anticon anticon-user"
|
||||
/>
|
||||
<span
|
||||
class="nav-text"
|
||||
>
|
||||
<span>
|
||||
User
|
||||
</span>
|
||||
</span>
|
||||
@ -744,30 +762,24 @@ exports[`renders ./components/layout/demo/side.md correctly 1`] = `
|
||||
<i
|
||||
class="anticon anticon-team"
|
||||
/>
|
||||
<span
|
||||
class="nav-text"
|
||||
>
|
||||
<span>
|
||||
Team
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="true"
|
||||
class="ant-menu-item-selected ant-menu-item"
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
class="anticon anticon-file"
|
||||
/>
|
||||
<span
|
||||
class="nav-text"
|
||||
>
|
||||
<span>
|
||||
File
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -1039,7 +1051,7 @@ exports[`renders ./components/layout/demo/top-side.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu-selected ant-menu-submenu"
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
@ -1209,7 +1221,7 @@ exports[`renders ./components/layout/demo/top-side-2.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu-selected ant-menu-submenu"
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
|
@ -38,15 +38,15 @@ class SiderDemo extends React.Component {
|
||||
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
|
||||
<Menu.Item key="1">
|
||||
<Icon type="user" />
|
||||
<span className="nav-text">nav 1</span>
|
||||
<span>nav 1</span>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2">
|
||||
<Icon type="video-camera" />
|
||||
<span className="nav-text">nav 2</span>
|
||||
<span>nav 2</span>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="3">
|
||||
<Icon type="upload" />
|
||||
<span className="nav-text">nav 3</span>
|
||||
<span>nav 3</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>
|
||||
@ -89,12 +89,4 @@ ReactDOM.render(<SiderDemo />, mountNode);
|
||||
border-radius: 6px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
#components-layout-demo-custom-trigger .ant-layout-sider-collapsed .anticon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#components-layout-demo-custom-trigger .ant-layout-sider-collapsed .nav-text {
|
||||
display: none;
|
||||
}
|
||||
````
|
||||
|
@ -27,14 +27,10 @@ const SubMenu = Menu.SubMenu;
|
||||
class SiderDemo extends React.Component {
|
||||
state = {
|
||||
collapsed: false,
|
||||
mode: 'inline',
|
||||
};
|
||||
onCollapse = (collapsed) => {
|
||||
console.log(collapsed);
|
||||
this.setState({
|
||||
collapsed,
|
||||
mode: collapsed ? 'vertical' : 'inline',
|
||||
});
|
||||
this.setState({ collapsed });
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
@ -45,27 +41,33 @@ class SiderDemo extends React.Component {
|
||||
onCollapse={this.onCollapse}
|
||||
>
|
||||
<div className="logo" />
|
||||
<Menu theme="dark" mode={this.state.mode} defaultSelectedKeys={['6']}>
|
||||
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
|
||||
<Menu.Item key="1">
|
||||
<Icon type="pie-chart" />
|
||||
<span>Option 1</span>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2">
|
||||
<Icon type="desktop" />
|
||||
<span>Option 2</span>
|
||||
</Menu.Item>
|
||||
<SubMenu
|
||||
key="sub1"
|
||||
title={<span><Icon type="user" /><span className="nav-text">User</span></span>}
|
||||
title={<span><Icon type="user" /><span>User</span></span>}
|
||||
>
|
||||
<Menu.Item key="1">Tom</Menu.Item>
|
||||
<Menu.Item key="2">Bill</Menu.Item>
|
||||
<Menu.Item key="3">Alex</Menu.Item>
|
||||
<Menu.Item key="3">Tom</Menu.Item>
|
||||
<Menu.Item key="4">Bill</Menu.Item>
|
||||
<Menu.Item key="5">Alex</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu
|
||||
key="sub2"
|
||||
title={<span><Icon type="team" /><span className="nav-text">Team</span></span>}
|
||||
title={<span><Icon type="team" /><span>Team</span></span>}
|
||||
>
|
||||
<Menu.Item key="4">Team 1</Menu.Item>
|
||||
<Menu.Item key="5">Team 2</Menu.Item>
|
||||
<Menu.Item key="6">Team 1</Menu.Item>
|
||||
<Menu.Item key="8">Team 2</Menu.Item>
|
||||
</SubMenu>
|
||||
<Menu.Item key="6">
|
||||
<span>
|
||||
<Menu.Item key="8">
|
||||
<Icon type="file" />
|
||||
<span className="nav-text">File</span>
|
||||
</span>
|
||||
<span>File</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>
|
||||
@ -99,17 +101,4 @@ ReactDOM.render(<SiderDemo />, mountNode);
|
||||
border-radius: 6px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
#components-layout-demo-side .ant-layout-sider-collapsed .anticon {
|
||||
font-size: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#components-layout-demo-side .ant-layout-sider-collapsed .nav-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#components-layout-demo-side .ant-layout-sider-collapsed .ant-menu-submenu-vertical > .ant-menu-submenu-title:after {
|
||||
display: none;
|
||||
}
|
||||
````
|
||||
|
22
components/menu/MenuItem.tsx
Normal file
22
components/menu/MenuItem.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Item } from 'rc-menu';
|
||||
import PropTypes from 'prop-types';
|
||||
import Tooltip from '../tooltip';
|
||||
|
||||
const MenuItem: any = (props, { inlineCollapsed }) => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={inlineCollapsed && props.level === 1 ? props.children : ''}
|
||||
placement="right"
|
||||
overlayClassName={`${props.rootPrefixCls}-inline-collapsed-tooltip`}
|
||||
>
|
||||
<Item {...props} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
MenuItem.contextTypes = {
|
||||
inlineCollapsed: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default MenuItem;
|
@ -61,7 +61,7 @@ exports[`renders ./components/menu/demo/horizontal.md correctly 1`] = `
|
||||
</ul>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/sider.md correctly 1`] = `
|
||||
exports[`renders ./components/menu/demo/inline.md correctly 1`] = `
|
||||
<ul
|
||||
aria-activedescendant=""
|
||||
class="ant-menu ant-menu-inline ant-menu-light ant-menu-root"
|
||||
@ -70,7 +70,7 @@ exports[`renders ./components/menu/demo/sider.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu-selected ant-menu-submenu"
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
@ -197,6 +197,147 @@ exports[`renders ./components/menu/demo/sider.md correctly 1`] = `
|
||||
</ul>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/inline-collapsed.md correctly 1`] = `
|
||||
<div
|
||||
style="width:240px;"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
style="margin-bottom:16px;"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-menu-fold"
|
||||
/>
|
||||
</button>
|
||||
<ul
|
||||
aria-activedescendant=""
|
||||
class="ant-menu ant-menu-inline ant-menu-dark ant-menu-root"
|
||||
role="menu"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
aria-selected="true"
|
||||
class="ant-menu-item-selected ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-pie-chart"
|
||||
/>
|
||||
<span>
|
||||
Option 1
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-desktop"
|
||||
/>
|
||||
<span>
|
||||
Option 2
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<i
|
||||
class="anticon anticon-inbox"
|
||||
/>
|
||||
<span>
|
||||
Option 3
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
aria-owns="sub1$Menu"
|
||||
class="ant-menu-submenu-title"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
class="anticon anticon-mail"
|
||||
/>
|
||||
<span>
|
||||
Navigation One
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<ul
|
||||
aria-activedescendant=""
|
||||
class="ant-menu ant-menu-inline ant-menu-sub"
|
||||
id="sub1$Menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:48px;"
|
||||
>
|
||||
Option 5
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:48px;"
|
||||
>
|
||||
Option 6
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:48px;"
|
||||
>
|
||||
Option 7
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="ant-menu-item"
|
||||
role="menuitem"
|
||||
style="padding-left:48px;"
|
||||
>
|
||||
Option 8
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-owns="sub2$Menu"
|
||||
class="ant-menu-submenu-title"
|
||||
style="padding-left:24px;"
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
class="anticon anticon-appstore"
|
||||
/>
|
||||
<span>
|
||||
Navigation Two
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/sider-current.md correctly 1`] = `
|
||||
<ul
|
||||
aria-activedescendant=""
|
||||
@ -206,7 +347,7 @@ exports[`renders ./components/menu/demo/sider-current.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-selected ant-menu-submenu"
|
||||
class="ant-menu-submenu-inline ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
@ -426,7 +567,7 @@ exports[`renders ./components/menu/demo/theme.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu-selected ant-menu-submenu"
|
||||
class="ant-menu-submenu-inline ant-menu-submenu-open ant-menu-submenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Menu from '..';
|
||||
import Icon from '../../icon';
|
||||
|
||||
const SubMenu = Menu.SubMenu;
|
||||
|
||||
@ -127,4 +128,31 @@ describe('Menu', () => {
|
||||
wrapper.setProps({ mode: 'inline' });
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).not.toBe(true);
|
||||
});
|
||||
|
||||
it('should always follow openKeys when mode is switched', () => {
|
||||
const wrapper = mount(
|
||||
<Menu defaultOpenKeys={['1']} mode="inline">
|
||||
<Menu.Item key="menu1">
|
||||
<Icon type="inbox" />
|
||||
<span>Option</span>
|
||||
</Menu.Item>
|
||||
<SubMenu key="1" title="submenu1">
|
||||
<Menu.Item key="submenu1">
|
||||
Option
|
||||
</Menu.Item>
|
||||
<Menu.Item key="submenu2">
|
||||
Option
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
);
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-inline')).toBe(true);
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).toBe(false);
|
||||
wrapper.setProps({ inlineCollapsed: true });
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-vertical')).toBe(true);
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).toBe(true);
|
||||
wrapper.setProps({ inlineCollapsed: false });
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-inline')).toBe(true);
|
||||
expect(wrapper.find('.ant-menu-sub').at(0).hasClass('ant-menu-hidden')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
75
components/menu/demo/inline-collapsed.md
Normal file
75
components/menu/demo/inline-collapsed.md
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 缩起内嵌菜单
|
||||
en-US: Collapsed inline menu
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
内嵌菜单可以被缩起/展开。
|
||||
|
||||
## en-US
|
||||
|
||||
Inline menu could be collapsed.
|
||||
|
||||
````jsx
|
||||
import { Menu, Icon, Button } from 'antd';
|
||||
const SubMenu = Menu.SubMenu;
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
collapsed: false,
|
||||
}
|
||||
toggleCollapsed = () => {
|
||||
this.setState({
|
||||
collapsed: !this.state.collapsed,
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div style={{ width: 240 }}>
|
||||
<Button type="primary" onClick={this.toggleCollapsed} style={{ marginBottom: 16 }}>
|
||||
<Icon type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'} />
|
||||
</Button>
|
||||
<Menu
|
||||
defaultSelectedKeys={['1']}
|
||||
defaultOpenKeys={['sub1']}
|
||||
mode="inline"
|
||||
theme="dark"
|
||||
inlineCollapsed={this.state.collapsed}
|
||||
>
|
||||
<Menu.Item key="1">
|
||||
<Icon type="pie-chart" />
|
||||
<span>Option 1</span>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2">
|
||||
<Icon type="desktop" />
|
||||
<span>Option 2</span>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="3">
|
||||
<Icon type="inbox" />
|
||||
<span>Option 3</span>
|
||||
</Menu.Item>
|
||||
<SubMenu key="sub1" title={<span><Icon type="mail" /><span>Navigation One</span></span>}>
|
||||
<Menu.Item key="5">Option 5</Menu.Item>
|
||||
<Menu.Item key="6">Option 6</Menu.Item>
|
||||
<Menu.Item key="7">Option 7</Menu.Item>
|
||||
<Menu.Item key="8">Option 8</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu key="sub2" title={<span><Icon type="appstore" /><span>Navigation Two</span></span>}>
|
||||
<Menu.Item key="9">Option 9</Menu.Item>
|
||||
<Menu.Item key="10">Option 10</Menu.Item>
|
||||
<SubMenu key="sub3" title="Submenu">
|
||||
<Menu.Item key="11">Option 11</Menu.Item>
|
||||
<Menu.Item key="12">Option 12</Menu.Item>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
````
|
@ -2,7 +2,7 @@
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 内嵌菜单
|
||||
en-US: Vertical menu with inline children
|
||||
en-US: Inline menu
|
||||
---
|
||||
|
||||
## zh-CN
|
@ -42,6 +42,7 @@ More layout and samples: [layout](/docs/spec/layout).
|
||||
| style | style of the root node | object | |
|
||||
| inlineIndent | indent px of inline menu item on each level | number | 24 |
|
||||
| multiple | Allow select multiple item | boolean | false |
|
||||
| inlineCollapsed | specified the collapsed status when menu is inline mode | boolean | - |
|
||||
|
||||
> More options in [rc-menu](https://github.com/react-component/menu#api)
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React from 'react';
|
||||
import RcMenu, { Item, Divider, SubMenu, ItemGroup } from 'rc-menu';
|
||||
import RcMenu, { Divider, SubMenu, ItemGroup } from 'rc-menu';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import animation from '../_util/openAnimation';
|
||||
import warning from '../_util/warning';
|
||||
import Item from './MenuItem';
|
||||
|
||||
export interface SelectParam {
|
||||
key: string;
|
||||
@ -39,6 +42,7 @@ export interface MenuProps {
|
||||
prefixCls?: string;
|
||||
multiple?: boolean;
|
||||
inlineIndent?: number;
|
||||
inlineCollapsed?: boolean;
|
||||
}
|
||||
|
||||
export default class Menu extends React.Component<MenuProps, any> {
|
||||
@ -51,7 +55,14 @@ export default class Menu extends React.Component<MenuProps, any> {
|
||||
className: '',
|
||||
theme: 'light', // or dark
|
||||
};
|
||||
static childContextTypes = {
|
||||
inlineCollapsed: PropTypes.bool,
|
||||
};
|
||||
static contextTypes = {
|
||||
siderCollapsed: PropTypes.bool,
|
||||
};
|
||||
switchModeFromInline: boolean;
|
||||
inlineOpenKeys = [];
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@ -61,6 +72,11 @@ export default class Menu extends React.Component<MenuProps, any> {
|
||||
'see: http://u.ant.design/menu-on-open-change.',
|
||||
);
|
||||
|
||||
warning(
|
||||
!('inlineCollapsed' in props && props.mode !== 'inline'),
|
||||
'`inlineCollapsed` should only be used when Menu\'s `mode` is inline.',
|
||||
);
|
||||
|
||||
let openKeys;
|
||||
if ('defaultOpenKeys' in props) {
|
||||
openKeys = props.defaultOpenKeys;
|
||||
@ -72,11 +88,27 @@ export default class Menu extends React.Component<MenuProps, any> {
|
||||
openKeys: openKeys || [],
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
getChildContext() {
|
||||
return {
|
||||
inlineCollapsed: this.getInlineCollapsed(),
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
if (this.props.mode === 'inline' &&
|
||||
nextProps.mode !== 'inline') {
|
||||
this.switchModeFromInline = true;
|
||||
}
|
||||
if ((nextProps.inlineCollapsed && !this.props.inlineCollapsed) ||
|
||||
(nextContext.siderCollapsed && !this.context.siderCollapsed)) {
|
||||
this.switchModeFromInline = true;
|
||||
this.inlineOpenKeys = this.state.openKeys;
|
||||
this.setOpenKeys([]);
|
||||
}
|
||||
if ((!nextProps.inlineCollapsed && this.props.inlineCollapsed) ||
|
||||
(!nextContext.siderCollapsed && this.context.siderCollapsed)) {
|
||||
this.setOpenKeys(this.inlineOpenKeys);
|
||||
this.inlineOpenKeys = [];
|
||||
}
|
||||
if ('openKeys' in nextProps) {
|
||||
this.setState({ openKeys: nextProps.openKeys });
|
||||
}
|
||||
@ -102,48 +134,68 @@ export default class Menu extends React.Component<MenuProps, any> {
|
||||
this.setState({ openKeys });
|
||||
}
|
||||
}
|
||||
render() {
|
||||
let openAnimation = this.props.openAnimation || this.props.openTransitionName;
|
||||
if (this.props.openAnimation === undefined && this.props.openTransitionName === undefined) {
|
||||
switch (this.props.mode) {
|
||||
getRealMenuMode() {
|
||||
const { mode } = this.props;
|
||||
return this.getInlineCollapsed() ? 'vertical' : mode;
|
||||
}
|
||||
getInlineCollapsed() {
|
||||
const { inlineCollapsed } = this.props;
|
||||
if (this.context.siderCollapsed !== undefined) {
|
||||
return this.context.siderCollapsed;
|
||||
}
|
||||
return inlineCollapsed;
|
||||
}
|
||||
getMenuOpenAnimation() {
|
||||
const { openAnimation, openTransitionName } = this.props;
|
||||
const menuMode = this.getRealMenuMode();
|
||||
let menuOpenAnimation = openAnimation || openTransitionName;
|
||||
if (openAnimation === undefined && openTransitionName === undefined) {
|
||||
switch (menuMode) {
|
||||
case 'horizontal':
|
||||
openAnimation = 'slide-up';
|
||||
menuOpenAnimation = 'slide-up';
|
||||
break;
|
||||
case 'vertical':
|
||||
// When mode switch from inline
|
||||
// submenu should hide without animation
|
||||
if (this.switchModeFromInline) {
|
||||
openAnimation = '';
|
||||
menuOpenAnimation = '';
|
||||
this.switchModeFromInline = false;
|
||||
} else {
|
||||
openAnimation = 'zoom-big';
|
||||
menuOpenAnimation = 'zoom-big';
|
||||
}
|
||||
break;
|
||||
case 'inline':
|
||||
openAnimation = animation;
|
||||
menuOpenAnimation = animation;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
return menuOpenAnimation;
|
||||
}
|
||||
render() {
|
||||
const { prefixCls, className, theme } = this.props;
|
||||
const menuMode = this.getRealMenuMode();
|
||||
const menuOpenAnimation = this.getMenuOpenAnimation();
|
||||
|
||||
let props = {};
|
||||
const className = `${this.props.className} ${this.props.prefixCls}-${this.props.theme}`;
|
||||
if (this.props.mode !== 'inline') {
|
||||
// There is this.state.openKeys for
|
||||
// closing vertical popup submenu after click it
|
||||
props = {
|
||||
const menuClassName = classNames(className, `${prefixCls}-${theme}`, {
|
||||
[`${prefixCls}-inline-collapsed`]: this.getInlineCollapsed(),
|
||||
});
|
||||
|
||||
const menuProps: MenuProps = {
|
||||
openKeys: this.state.openKeys,
|
||||
onClick: this.handleClick,
|
||||
onOpenChange: this.handleOpenChange,
|
||||
openTransitionName: openAnimation,
|
||||
className,
|
||||
className: menuClassName,
|
||||
mode: menuMode,
|
||||
};
|
||||
|
||||
if (menuMode !== 'inline') {
|
||||
// closing vertical popup submenu after click it
|
||||
menuProps.onClick = this.handleClick;
|
||||
menuProps.openTransitionName = menuOpenAnimation;
|
||||
} else {
|
||||
props = {
|
||||
openAnimation,
|
||||
className,
|
||||
};
|
||||
menuProps.openAnimation = menuOpenAnimation;
|
||||
}
|
||||
return <RcMenu {...this.props} {...props} />;
|
||||
|
||||
return <RcMenu {...this.props} {...menuProps} />;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ subtitle: 导航菜单
|
||||
| style | 根节点样式 | object | |
|
||||
| inlineIndent | inline 模式的菜单缩进宽度 | number | 24 |
|
||||
| multiple | 是否允许多选 | boolean | false |
|
||||
| inlineCollapsed | inline 时菜单是否收起状态 | boolean | - |
|
||||
|
||||
> More options in [rc-menu](https://github.com/react-component/menu#api)
|
||||
|
||||
|
@ -158,6 +158,8 @@
|
||||
}
|
||||
|
||||
&-inline {
|
||||
width: 100%;
|
||||
transition: width .24s;
|
||||
.@{menu-prefix-cls}-selected,
|
||||
.@{menu-prefix-cls}-item-selected {
|
||||
&:after {
|
||||
@ -166,6 +168,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-inline-collapsed {
|
||||
width: 64px;
|
||||
transition: all .3s;
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title {
|
||||
text-align: center;
|
||||
left: 0;
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
padding: 0;
|
||||
.@{iconfont-css-prefix} {
|
||||
font-size: 16px;
|
||||
line-height: 42px;
|
||||
margin: 0;
|
||||
+ span {
|
||||
max-width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-tooltip {
|
||||
.@{iconfont-css-prefix} {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-submenu-horizontal > & {
|
||||
top: 100%;
|
||||
left: 0;
|
||||
@ -199,6 +232,15 @@
|
||||
.@{iconfont-css-prefix} {
|
||||
min-width: 14px;
|
||||
margin-right: 8px;
|
||||
transition: all .3s;
|
||||
vertical-align: middle;
|
||||
+ span {
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
transition: all .3s;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,5 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../tooltip/style';
|
||||
|
Loading…
Reference in New Issue
Block a user