feat: Menu support items (#34559)

* docs: Update Menu cn doc

* chore: update ts def

* chore: support convert

* docs: more demo

* docs: more demo

* docs: all menu demos

* docs: dropdown demo

* docs: dropdown all demos

* docs: update demo

* test: coverage

* docs: more demo

* docs: layout demo

* docs: all demo

* chore: fix ts lint

* docs: fix doc

* docs: all docs
This commit is contained in:
二货机器人 2022-03-18 15:20:35 +08:00 committed by GitHub
parent 78705e34f0
commit a67d39cd6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1492 additions and 996 deletions

View File

@ -97,13 +97,12 @@ const Breadcrumb: BreadcrumbInterface = ({
let overlay;
if (route.children && route.children.length) {
overlay = (
<Menu>
{route.children.map(child => (
<Menu.Item key={child.path || child.breadcrumbName}>
{itemRender(child, params, routes, addChildPath(paths, child.path, params))}
</Menu.Item>
))}
</Menu>
<Menu
items={route.children.map(child => ({
key: child.path || child.breadcrumbName,
label: itemRender(child, params, routes, addChildPath(paths, child.path, params)),
}))}
/>
);
}
@ -144,9 +143,7 @@ const Breadcrumb: BreadcrumbInterface = ({
return (
<nav className={breadcrumbClassName} style={style} {...restProps}>
<ol>
{crumbs}
</ol>
<ol>{crumbs}</ol>
</nav>
);
};

View File

@ -17,23 +17,31 @@ Breadcrumbs support drop down menu.
import { Breadcrumb, Menu } from 'antd';
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
General
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
Layout
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
Navigation
</a>
</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
General
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
Layout
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
Navigation
</a>
),
},
]}
/>
);
ReactDOM.render(

View File

@ -13,19 +13,31 @@ title:
If you need several buttons, we recommend that you use 1 primary button + n secondary buttons, and if there are more than three operations, you can group some of them into [Dropdown.Button](/components/dropdown/#components-dropdown-demo-dropdown-button).
```jsx
import { Button, Menu, Dropdown } from 'antd';
```tsx
import { Button, Menu, Dropdown, MenuProps } from 'antd';
function handleMenuClick(e) {
const onMenuClick: MenuProps['onClick'] = e => {
console.log('click', e);
}
};
const menu = (
<Menu onClick={handleMenuClick}>
<Menu.Item key="1">1st item</Menu.Item>
<Menu.Item key="2">2nd item</Menu.Item>
<Menu.Item key="3">3rd item</Menu.Item>
</Menu>
<Menu
onClick={onMenuClick}
items={[
{
key: '1',
label: '1st item',
},
{
key: '2',
label: '2nd item',
},
{
key: '3',
label: '3rd item',
},
]}
/>
);
ReactDOM.render(

View File

@ -61,6 +61,35 @@ import {
const SplitSpace = props => <Space split={<Divider type="vertical" />} size={4} {...props} />;
const menuItems = [
{
key: 'mail',
icon: <MailOutlined />,
label: 'Mail',
},
{
key: 'SubMenu',
icon: <SettingOutlined />,
label: 'Submenu',
children: [
{
type: 'group',
label: 'Item 1',
children: [
{
key: 'setting:1',
label: 'Option 1',
},
{
key: 'setting:2',
label: 'Option 2',
},
],
},
],
},
];
const inputProps = {
style: { width: 128 },
};
@ -281,10 +310,17 @@ const FormSizeDemo = () => {
{/* Dropdown */}
<Dropdown
overlay={
<Menu>
<Menu.Item>1st menu item</Menu.Item>
<Menu.Item danger>a danger item</Menu.Item>
</Menu>
<Menu
items={[
{
label: '1st menu item',
},
{
label: 'a danger item',
danger: true,
},
]}
/>
}
>
<a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
@ -299,60 +335,25 @@ const FormSizeDemo = () => {
{/* Menu - horizontal */}
<Row gutter={16}>
<Col span={12}>
<Menu mode="horizontal" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
<Menu mode="horizontal" defaultSelectedKeys={['mail']} items={menuItems} />
</Col>
<Col span={12}>
<Menu mode="horizontal" theme="dark" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
<Menu
mode="horizontal"
theme="dark"
defaultSelectedKeys={['mail']}
items={menuItems}
/>
</Col>
</Row>
{/* Menu - vertical */}
<Row gutter={16}>
<Col span={12}>
<Menu mode="inline" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
<Menu mode="inline" defaultSelectedKeys={['mail']} items={menuItems} />
</Col>
<Col span={12}>
<Menu mode="vertical" theme="dark" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
<Menu mode="vertical" theme="dark" defaultSelectedKeys={['mail']} items={menuItems} />
</Col>
</Row>

View File

@ -17,23 +17,31 @@ By specifying `arrow` prop with `{ pointAtCenter: true }`, the arrow will point
import { Menu, Dropdown, Button } from 'antd';
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
),
},
]}
/>
);
ReactDOM.render(

View File

@ -17,23 +17,31 @@ You could display an arrow.
import { Menu, Dropdown, Button } from 'antd';
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
),
},
]}
/>
);
ReactDOM.render(

View File

@ -18,24 +18,38 @@ import { Menu, Dropdown } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
</Menu.Item>
<Menu.Item icon={<DownOutlined />} disabled>
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item (disabled)
</a>
</Menu.Item>
<Menu.Item disabled>
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item (disabled)
</a>
</Menu.Item>
<Menu.Item danger>a danger item</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item (disabled)
</a>
),
icon: <DownOutlined />,
disabled: true,
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item (disabled)
</a>
),
disabled: true,
},
{
danger: true,
label: 'a danger item',
},
]}
/>
);
ReactDOM.render(

View File

@ -17,11 +17,22 @@ The default trigger mode is `hover`, you can change it to `contextMenu`.
import { Menu, Dropdown } from 'antd';
const menu = (
<Menu>
<Menu.Item key="1">1st menu item</Menu.Item>
<Menu.Item key="2">2nd menu item</Menu.Item>
<Menu.Item key="3">3rd menu item</Menu.Item>
</Menu>
<Menu
items={[
{
label: '1st menu item',
key: '1',
},
{
label: '2nd menu item',
key: '2',
},
{
label: '3rd menu item',
key: '3',
},
]}
/>
);
ReactDOM.render(

View File

@ -28,17 +28,26 @@ function handleMenuClick(e) {
}
const menu = (
<Menu onClick={handleMenuClick}>
<Menu.Item key="1" icon={<UserOutlined />}>
1st menu item
</Menu.Item>
<Menu.Item key="2" icon={<UserOutlined />}>
2nd menu item
</Menu.Item>
<Menu.Item key="3" icon={<UserOutlined />}>
3rd menu item
</Menu.Item>
</Menu>
<Menu
onClick={handleMenuClick}
items={[
{
label: '1st menu item',
key: '1',
icon: <UserOutlined />,
},
{
label: '2nd menu item',
key: '2',
icon: <UserOutlined />,
},
{
label: '3rd menu item',
key: '3',
icon: <UserOutlined />,
},
]}
/>
);
ReactDOM.render(

View File

@ -22,11 +22,23 @@ const onClick = ({ key }) => {
};
const menu = (
<Menu onClick={onClick}>
<Menu.Item key="1">1st menu item</Menu.Item>
<Menu.Item key="2">2nd menu item</Menu.Item>
<Menu.Item key="3">3rd menu item</Menu.Item>
</Menu>
<Menu
onClick={onClick}
items={[
{
label: '1st menu item',
key: '1',
},
{
label: '2nd menu item',
key: '2',
},
{
label: '3rd menu item',
key: '3',
},
]}
/>
);
ReactDOM.render(

View File

@ -18,22 +18,34 @@ import { Menu, Dropdown } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item key="0">
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
</Menu.Item>
<Menu.Item key="1">
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="3" disabled>
3rd menu itemdisabled
</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
key: '0',
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
),
key: '1',
},
{
type: 'divider',
},
{
label: '3rd menu itemdisabled',
key: '3',
disabled: true,
},
]}
/>
);
ReactDOM.render(

View File

@ -18,9 +18,14 @@ import { Menu, Dropdown, Space } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item key="1">Submit and continue</Menu.Item>
</Menu>
<Menu
items={[
{
label: 'Submit and continue',
key: '1',
},
]}
/>
);
class App extends React.Component {
state = {

View File

@ -18,44 +18,56 @@ This demo was created for debugging Menu styles inside Dropdown.
[#19150](https://github.com/ant-design/ant-design/pull/19150)
```jsx
import { Menu, Dropdown } from 'antd';
```tsx
import { Menu, Dropdown, MenuProps } from 'antd';
import { MailOutlined, AppstoreOutlined, SettingOutlined, DownOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
const menu = (
<Menu selectedKeys={['1']} openKeys={['sub1']}>
<Menu.ItemGroup key="group" title="Item Group">
<Menu.Item key="01">Option 0</Menu.Item>
<Menu.Item key="02">Option 0</Menu.Item>
</Menu.ItemGroup>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
<Menu.ItemGroup key="g1" title="Item 1">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup key="g2" title="Item 2">
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
</SubMenu>
</SubMenu>
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
<Menu.Item key="9">Option 9</Menu.Item>
<Menu.Item key="10">Option 10</Menu.Item>
<Menu.Item key="11">Option 11</Menu.Item>
<Menu.Item key="12">Option 12</Menu.Item>
</SubMenu>
</Menu>
);
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
): MenuItem {
return {
key,
icon,
children,
label,
type,
} as MenuItem;
}
const items: MenuItem[] = [
getItem(
'Item Group',
'group',
null,
[getItem('Option 0', '01'), getItem('Option 0', '02')],
'group',
),
getItem('Navigation One', 'sub1', <MailOutlined />, [
getItem('Item 1', 'g1', null, [getItem('Option 1', '1'), getItem('Option 2', '2')], 'group'),
getItem('Item 2', 'g2', null, [getItem('Option 3', '3'), getItem('Option 4', '4')], 'group'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Submenu', 'sub3', null, [getItem('Option 7', '7'), getItem('Option 8', '8')]),
]),
getItem('Navigation Three', 'sub4', <SettingOutlined />, [
getItem('Option 9', '9'),
getItem('Option 10', '10'),
getItem('Option 11', '11'),
getItem('Option 12', '12'),
]),
// Not crash
null as any,
];
const menu = <Menu selectedKeys={['1']} openKeys={['sub1']} items={items} />;
ReactDOM.render(
<Dropdown overlay={menu}>

View File

@ -34,11 +34,23 @@ class OverlayVisible extends React.Component {
render() {
const menu = (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Clicking me will not close the menu.</Menu.Item>
<Menu.Item key="2">Clicking me will not close the menu also.</Menu.Item>
<Menu.Item key="3">Clicking me will close the menu.</Menu.Item>
</Menu>
<Menu
onClick={this.handleMenuClick}
items={[
{
label: 'Clicking me will not close the menu.',
key: '1',
},
{
label: 'Clicking me will not close the menu also.',
key: '2',
},
{
label: 'Clicking me will close the menu.',
key: '3',
},
]}
/>
);
return (
<Dropdown

View File

@ -17,23 +17,31 @@ Support 6 placements.
import { Menu, Dropdown, Button, Space } from 'antd';
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
),
},
]}
/>
);
ReactDOM.render(

View File

@ -20,20 +20,45 @@ import { DownOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
const menu = (
<Menu>
<Menu.ItemGroup title="Group title">
<Menu.Item>1st menu item</Menu.Item>
<Menu.Item>2nd menu item</Menu.Item>
</Menu.ItemGroup>
<SubMenu title="sub menu">
<Menu.Item>3rd menu item</Menu.Item>
<Menu.Item>4th menu item</Menu.Item>
</SubMenu>
<SubMenu title="disabled sub menu" disabled>
<Menu.Item>5d menu item</Menu.Item>
<Menu.Item>6th menu item</Menu.Item>
</SubMenu>
</Menu>
<Menu
items={[
{
type: 'group',
label: 'Group title',
children: [
{
label: '1st menu item',
},
{
label: '2nd menu item',
},
],
},
{
label: 'sub menu',
children: [
{
label: '3rd menu item',
},
{
label: '4th menu item',
},
],
},
{
label: 'disabled sub menu',
disabled: true,
children: [
{
label: '5d menu item',
},
{
label: '6th menu item',
},
],
},
]}
/>
);
ReactDOM.render(

View File

@ -13,21 +13,30 @@ title:
The default trigger mode is `hover`, you can change it to `click`.
```jsx
```tsx
import { Menu, Dropdown } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item key="0">
<a href="https://www.antgroup.com">1st menu item</a>
</Menu.Item>
<Menu.Item key="1">
<a href="https://www.aliyun.com">2nd menu item</a>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="3">3rd menu item</Menu.Item>
</Menu>
<Menu
items={[
{
label: <a href="https://www.antgroup.com">1st menu item</a>,
key: '0',
},
{
label: <a href="https://www.aliyun.com">2nd menu item</a>,
key: '1',
},
{
type: 'divider',
},
{
label: '3rd menu item',
key: '3',
},
]}
/>
);
ReactDOM.render(

View File

@ -10,8 +10,8 @@ debug: true
修改内容前,请尝试此 Demo 查看样式是否抖动。
```jsx
import { Layout, Menu } from 'antd';
```tsx
import { Layout, Menu, MenuProps } from 'antd';
import {
TeamOutlined,
UserOutlined,
@ -23,7 +23,57 @@ import {
} from '@ant-design/icons';
const { Header, Sider, Content } = Layout;
const { SubMenu } = Menu;
const items: MenuProps['items'] = [
{
key: '1',
icon: <PieChartOutlined />,
label: 'Option 1',
},
{
key: '2',
icon: <DesktopOutlined />,
label: 'Option 2',
},
{
key: 'sub1',
icon: <UserOutlined />,
label: 'User',
children: [
{
key: '3',
label: 'Tom',
},
{
key: '4',
label: 'Bill',
},
{
key: '5',
label: 'Alex',
},
],
},
{
key: 'sub2',
icon: <TeamOutlined />,
label: 'Team',
children: [
{
key: '6',
label: 'Team 1',
},
{
key: '7',
label: 'Team 2',
},
],
},
{
key: '9',
icon: <FileOutlined />,
},
];
class SiderDemo extends React.Component {
state = {
@ -41,24 +91,13 @@ class SiderDemo extends React.Component {
<Layout>
<Sider trigger={null} collapsible collapsed={this.state.collapsed}>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['3']} defaultOpenKeys={['sub1']}>
<Menu.Item key="1" icon={<PieChartOutlined />}>
Option 1
</Menu.Item>
<Menu.Item key="2" icon={<DesktopOutlined />}>
Option 2
</Menu.Item>
<SubMenu key="sub1" icon={<UserOutlined />} title="User">
<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" icon={<TeamOutlined />} title="Team">
<Menu.Item key="6">Team 1</Menu.Item>
<Menu.Item key="8">Team 2</Menu.Item>
</SubMenu>
<Menu.Item key="9" icon={<FileOutlined />} />
</Menu>
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={['3']}
defaultOpenKeys={['sub1']}
items={items}
/>
</Sider>
<Layout>
<Header className="site-layout-background" style={{ padding: 0 }}>

View File

@ -41,17 +41,28 @@ class SiderDemo extends React.Component {
<Layout>
<Sider trigger={null} collapsible collapsed={this.state.collapsed}>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1" icon={<UserOutlined />}>
nav 1
</Menu.Item>
<Menu.Item key="2" icon={<VideoCameraOutlined />}>
nav 2
</Menu.Item>
<Menu.Item key="3" icon={<UploadOutlined />}>
nav 3
</Menu.Item>
</Menu>
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={['1']}
items={[
{
key: '1',
icon: <UserOutlined />,
label: 'nav 1',
},
{
key: '2',
icon: <VideoCameraOutlined />,
label: 'nav 2',
},
{
key: '3',
icon: <UploadOutlined />,
label: 'nav 3',
},
]}
/>
</Sider>
<Layout className="site-layout">
<Header className="site-layout-background" style={{ padding: 0 }}>

View File

@ -14,8 +14,8 @@ title:
When dealing with long content, a fixed sider can provide a better user experience.
```jsx
import { Layout, Menu } from 'antd';
```tsx
import { Layout, Menu, MenuProps } from 'antd';
import {
AppstoreOutlined,
BarChartOutlined,
@ -29,6 +29,21 @@ import {
const { Header, Content, Footer, Sider } = Layout;
const items: MenuProps['items'] = [
UserOutlined,
VideoCameraOutlined,
UploadOutlined,
BarChartOutlined,
CloudOutlined,
AppstoreOutlined,
TeamOutlined,
ShopOutlined,
].map((icon, index) => ({
key: String(index + 1),
icon: React.createElement(icon),
label: `nav ${index + 1}`,
}));
ReactDOM.render(
<Layout hasSider>
<Sider
@ -42,32 +57,7 @@ ReactDOM.render(
}}
>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['4']}>
<Menu.Item key="1" icon={<UserOutlined />}>
nav 1
</Menu.Item>
<Menu.Item key="2" icon={<VideoCameraOutlined />}>
nav 2
</Menu.Item>
<Menu.Item key="3" icon={<UploadOutlined />}>
nav 3
</Menu.Item>
<Menu.Item key="4" icon={<BarChartOutlined />}>
nav 4
</Menu.Item>
<Menu.Item key="5" icon={<CloudOutlined />}>
nav 5
</Menu.Item>
<Menu.Item key="6" icon={<AppstoreOutlined />}>
nav 6
</Menu.Item>
<Menu.Item key="7" icon={<TeamOutlined />}>
nav 7
</Menu.Item>
<Menu.Item key="8" icon={<ShopOutlined />}>
nav 8
</Menu.Item>
</Menu>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['4']} items={items} />
</Sider>
<Layout className="site-layout" style={{ marginLeft: 200 }}>
<Header className="site-layout-background" style={{ padding: 0 }} />

View File

@ -14,7 +14,7 @@ title:
Fixed Header is generally used to fix the top navigation to facilitate page switching.
```jsx
```tsx
import { Layout, Menu, Breadcrumb } from 'antd';
const { Header, Content, Footer } = Layout;
@ -23,11 +23,15 @@ ReactDOM.render(
<Layout>
<Header style={{ position: 'fixed', zIndex: 1, width: '100%' }}>
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
</Menu>
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
items={new Array(3).fill(null).map((_, index) => ({
key: String(index + 1),
label: `nav ${index + 1}`,
}))}
/>
</Header>
<Content className="site-layout" style={{ padding: '0 50px', marginTop: 64 }}>
<Breadcrumb style={{ margin: '16px 0' }}>

View File

@ -36,20 +36,18 @@ ReactDOM.render(
}}
>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['4']}>
<Menu.Item key="1" icon={<UserOutlined />}>
nav 1
</Menu.Item>
<Menu.Item key="2" icon={<VideoCameraOutlined />}>
nav 2
</Menu.Item>
<Menu.Item key="3" icon={<UploadOutlined />}>
nav 3
</Menu.Item>
<Menu.Item key="4" icon={<UserOutlined />}>
nav 4
</Menu.Item>
</Menu>
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={['4']}
items={[UserOutlined, VideoCameraOutlined, UploadOutlined, UserOutlined].map(
(icon, index) => ({
key: String(index + 1),
icon: React.createElement(icon),
label: `nav ${index + 1}`,
}),
)}
/>
</Sider>
<Layout>
<Header className="site-layout-sub-header-background" style={{ padding: 0 }} />

View File

@ -22,8 +22,8 @@ Generally, the mainnav is placed on the left side of the page, and the secondary
The level of the aside navigation is scalable. The first, second, and third level navigations could be present more fluently and relevantly, and aside navigation can be fixed, allowing the user to quickly switch and spot the current position, improving the user experience. However, this navigation occupies some horizontal space of the contents.
```jsx
import { Layout, Menu, Breadcrumb } from 'antd';
```tsx
import { Layout, Menu, Breadcrumb, MenuProps } from 'antd';
import {
DesktopOutlined,
PieChartOutlined,
@ -33,14 +33,41 @@ import {
} from '@ant-design/icons';
const { Header, Content, Footer, Sider } = Layout;
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Option 1', '1', <PieChartOutlined />),
getItem('Option 2', '2', <DesktopOutlined />),
getItem('User', 'sub1', <UserOutlined />, [
getItem('Tom', '3'),
getItem('Bill', '4'),
getItem('Alex', '5'),
]),
getItem('Team', 'sub2', <TeamOutlined />, [getItem('Team 1', '6'), getItem('Team 2', '8')]),
getItem('Files', '9', <FileOutlined />),
];
class SiderDemo extends React.Component {
state = {
collapsed: false,
};
onCollapse = collapsed => {
onCollapse = (collapsed: boolean) => {
console.log(collapsed);
this.setState({ collapsed });
};
@ -51,26 +78,7 @@ class SiderDemo extends React.Component {
<Layout style={{ minHeight: '100vh' }}>
<Sider collapsible collapsed={collapsed} onCollapse={this.onCollapse}>
<div className="logo" />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
<Menu.Item key="1" icon={<PieChartOutlined />}>
Option 1
</Menu.Item>
<Menu.Item key="2" icon={<DesktopOutlined />}>
Option 2
</Menu.Item>
<SubMenu key="sub1" icon={<UserOutlined />} title="User">
<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" icon={<TeamOutlined />} title="Team">
<Menu.Item key="6">Team 1</Menu.Item>
<Menu.Item key="8">Team 2</Menu.Item>
</SubMenu>
<Menu.Item key="9" icon={<FileOutlined />}>
Files
</Menu.Item>
</Menu>
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline" items={items} />
</Sider>
<Layout className="site-layout">
<Header className="site-layout-background" style={{ padding: 0 }} />

View File

@ -13,22 +13,42 @@ title:
Both the top navigation and the sidebar, commonly used in application site.
```jsx
import { Layout, Menu, Breadcrumb } from 'antd';
```tsx
import { Layout, Menu, Breadcrumb, MenuProps } from 'antd';
import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
const { Header, Content, Sider } = Layout;
const items1: MenuProps['items'] = ['1', '2', '3'].map(key => ({
key,
label: `nav ${key}`,
}));
const items2: MenuProps['items'] = [UserOutlined, LaptopOutlined, NotificationOutlined].map(
(icon, index) => {
const key = String(index + 1);
return {
key: `sub${key}`,
icon: React.createElement(icon),
label: `subnav ${key}`,
children: new Array(4).fill(null).map((_, j) => {
const subKey = index * 4 + j + 1;
return {
key: subKey,
label: `option${subKey}`,
};
}),
};
},
);
ReactDOM.render(
<Layout>
<Header className="header">
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
</Menu>
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']} items={items1} />
</Header>
<Layout>
<Sider width={200} className="site-layout-background">
@ -37,26 +57,8 @@ ReactDOM.render(
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
style={{ height: '100%', borderRight: 0 }}
>
<SubMenu key="sub1" icon={<UserOutlined />} title="subnav 1">
<Menu.Item key="1">option1</Menu.Item>
<Menu.Item key="2">option2</Menu.Item>
<Menu.Item key="3">option3</Menu.Item>
<Menu.Item key="4">option4</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<LaptopOutlined />} title="subnav 2">
<Menu.Item key="5">option5</Menu.Item>
<Menu.Item key="6">option6</Menu.Item>
<Menu.Item key="7">option7</Menu.Item>
<Menu.Item key="8">option8</Menu.Item>
</SubMenu>
<SubMenu key="sub3" icon={<NotificationOutlined />} title="subnav 3">
<Menu.Item key="9">option9</Menu.Item>
<Menu.Item key="10">option10</Menu.Item>
<Menu.Item key="11">option11</Menu.Item>
<Menu.Item key="12">option12</Menu.Item>
</SubMenu>
</Menu>
items={items2}
/>
</Sider>
<Layout style={{ padding: '0 24px 24px' }}>
<Breadcrumb style={{ margin: '16px 0' }}>

View File

@ -13,22 +13,42 @@ title:
Both the top navigation and the sidebar, commonly used in documentation site.
```jsx
import { Layout, Menu, Breadcrumb } from 'antd';
```tsx
import { Layout, Menu, Breadcrumb, MenuProps } from 'antd';
import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
const { Header, Content, Footer, Sider } = Layout;
const items1: MenuProps['items'] = ['1', '2', '3'].map(key => ({
key,
label: `nav ${key}`,
}));
const items2: MenuProps['items'] = [UserOutlined, LaptopOutlined, NotificationOutlined].map(
(icon, index) => {
const key = String(index + 1);
return {
key: `sub${key}`,
icon: React.createElement(icon),
label: `subnav ${key}`,
children: new Array(4).fill(null).map((_, j) => {
const subKey = index * 4 + j + 1;
return {
key: subKey,
label: `option${subKey}`,
};
}),
};
},
);
ReactDOM.render(
<Layout>
<Header className="header">
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
</Menu>
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']} items={items1} />
</Header>
<Content style={{ padding: '0 50px' }}>
<Breadcrumb style={{ margin: '16px 0' }}>
@ -43,26 +63,8 @@ ReactDOM.render(
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
style={{ height: '100%' }}
>
<SubMenu key="sub1" icon={<UserOutlined />} title="subnav 1">
<Menu.Item key="1">option1</Menu.Item>
<Menu.Item key="2">option2</Menu.Item>
<Menu.Item key="3">option3</Menu.Item>
<Menu.Item key="4">option4</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<LaptopOutlined />} title="subnav 2">
<Menu.Item key="5">option5</Menu.Item>
<Menu.Item key="6">option6</Menu.Item>
<Menu.Item key="7">option7</Menu.Item>
<Menu.Item key="8">option8</Menu.Item>
</SubMenu>
<SubMenu key="sub3" icon={<NotificationOutlined />} title="subnav 3">
<Menu.Item key="9">option9</Menu.Item>
<Menu.Item key="10">option10</Menu.Item>
<Menu.Item key="11">option11</Menu.Item>
<Menu.Item key="12">option12</Menu.Item>
</SubMenu>
</Menu>
items={items2}
/>
</Sider>
<Content style={{ padding: '0 24px', minHeight: 280 }}>Content</Content>
</Layout>

View File

@ -28,12 +28,18 @@ ReactDOM.render(
<Layout className="layout">
<Header>
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
{new Array(15).fill(null).map((_, index) => {
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
items={new Array(15).fill(null).map((_, index) => {
const key = index + 1;
return <Menu.Item key={key}>{`nav ${key}`}</Menu.Item>;
return {
key,
label: `nav ${key}`,
};
})}
</Menu>
/>
</Header>
<Content style={{ padding: '0 50px' }}>
<Breadcrumb style={{ margin: '16px 0' }}>

View File

@ -13,51 +13,77 @@ title:
Horizontal top navigation menu.
```jsx
import { Menu } from 'antd';
```tsx
import { Menu, MenuProps } from 'antd';
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
const items: MenuProps['items'] = [
{
label: 'Navigation One',
key: 'mail',
icon: <MailOutlined />,
},
{
label: 'Navigation Two',
key: 'app',
icon: <AppstoreOutlined />,
disabled: true,
},
{
label: 'Navigation Three - Submenu',
key: 'SubMenu',
icon: <SettingOutlined />,
children: [
{
type: 'group',
label: 'Item 1',
children: [
{
label: 'Option 1',
key: 'setting:1',
},
{
label: 'Option 2',
key: 'setting:2',
},
],
},
{
type: 'group',
label: 'Item 2',
children: [
{
label: 'Option 3',
key: 'setting:3',
},
{
label: 'Option 4',
key: 'setting:4',
},
],
},
],
},
{
label: (
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
),
key: 'alipay',
},
];
class App extends React.Component {
state = {
current: 'mail',
};
const App = () => {
const [current, setCurrent] = React.useState('mail');
handleClick = e => {
const onClick: MenuProps['onClick'] = e => {
console.log('click ', e);
this.setState({ current: e.key });
setCurrent(e.key);
};
render() {
const { current } = this.state;
return (
<Menu onClick={this.handleClick} selectedKeys={[current]} mode="horizontal">
<Menu.Item key="mail" icon={<MailOutlined />}>
Navigation One
</Menu.Item>
<Menu.Item key="app" disabled icon={<AppstoreOutlined />}>
Navigation Two
</Menu.Item>
<SubMenu key="SubMenu" icon={<SettingOutlined />} title="Navigation Three - Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup title="Item 2">
<Menu.Item key="setting:3">Option 3</Menu.Item>
<Menu.Item key="setting:4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<Menu.Item key="alipay">
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
</Menu.Item>
</Menu>
);
}
}
return <Menu onClick={onClick} selectedKeys={[current]} mode="horizontal" items={items} />;
};
ReactDOM.render(<App />, mountNode);
```

View File

@ -17,8 +17,8 @@ Inline menu could be collapsed.
Here is [a complete demo](/components/layout/#components-layout-demo-side) with sider layout.
```jsx
import { Menu, Button } from 'antd';
```tsx
import { Menu, Button, MenuProps } from 'antd';
import {
AppstoreOutlined,
MenuUnfoldOutlined,
@ -29,60 +29,67 @@ import {
MailOutlined,
} from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
class App extends React.Component {
state = {
collapsed: false,
};
toggleCollapsed = () => {
this.setState({
collapsed: !this.state.collapsed,
});
};
render() {
return (
<div style={{ width: 256 }}>
<Button type="primary" onClick={this.toggleCollapsed} style={{ marginBottom: 16 }}>
{React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined)}
</Button>
<Menu
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
mode="inline"
theme="dark"
inlineCollapsed={this.state.collapsed}
>
<Menu.Item key="1" icon={<PieChartOutlined />}>
Option 1
</Menu.Item>
<Menu.Item key="2" icon={<DesktopOutlined />}>
Option 2
</Menu.Item>
<Menu.Item key="3" icon={<ContainerOutlined />}>
Option 3
</Menu.Item>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
<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" icon={<AppstoreOutlined />} title="Navigation Two">
<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>
);
}
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
): MenuItem {
return {
key,
icon,
children,
label,
type,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Option 1', '1', <PieChartOutlined />),
getItem('Option 2', '2', <DesktopOutlined />),
getItem('Option 3', '3', <ContainerOutlined />),
getItem('Navigation One', 'sub1', <MailOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Option 7', '7'),
getItem('Option 8', '8'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 9', '9'),
getItem('Option 10', '10'),
getItem('Submenu', 'sub3', null, [getItem('Option 11', '11'), getItem('Option 12', '12')]),
]),
];
const App = () => {
const [collapsed, setCollapsed] = React.useState(false);
const toggleCollapsed = () => {
setCollapsed(!collapsed);
};
return (
<div style={{ width: 256 }}>
<Button type="primary" onClick={toggleCollapsed} style={{ marginBottom: 16 }}>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</Button>
<Menu
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
mode="inline"
theme="dark"
inlineCollapsed={collapsed}
items={items}
/>
</div>
);
};
ReactDOM.render(<App />, mountNode);
```

View File

@ -13,54 +13,64 @@ title:
Vertical menu with inline submenus.
```jsx
import { Menu } from 'antd';
```tsx
import { Menu, MenuProps } from 'antd';
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
class Sider extends React.Component {
handleClick = e => {
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
): MenuItem {
return {
key,
icon,
children,
label,
type,
} as MenuItem;
}
const items: MenuProps['items'] = [
getItem('Navigation One', 'sub1', <MailOutlined />, [
getItem('Item 1', 'g1', null, [getItem('Option 1', '1'), getItem('Option 2', '2')], 'group'),
getItem('Item 2', 'g2', null, [getItem('Option 3', '3'), getItem('Option 4', '4')], 'group'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Submenu', 'sub3', null, [getItem('Option 7', '7'), getItem('Option 8', '8')]),
]),
getItem('Navigation Three', 'sub4', <SettingOutlined />, [
getItem('Option 9', '9'),
getItem('Option 10', '10'),
getItem('Option 11', '11'),
getItem('Option 12', '12'),
]),
];
const Sider = () => {
const onClick: MenuProps['onClick'] = e => {
console.log('click ', e);
};
render() {
return (
<Menu
onClick={this.handleClick}
style={{ width: 256 }}
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
mode="inline"
>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
<Menu.ItemGroup key="g1" title="Item 1">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup key="g2" title="Item 2">
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
</SubMenu>
</SubMenu>
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
<Menu.Item key="9">Option 9</Menu.Item>
<Menu.Item key="10">Option 10</Menu.Item>
<Menu.Item key="11">Option 11</Menu.Item>
<Menu.Item key="12">Option 12</Menu.Item>
</SubMenu>
</Menu>
);
}
}
return (
<Menu
onClick={onClick}
style={{ width: 256 }}
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
mode="inline"
items={items}
/>
);
};
ReactDOM.render(<Sider />, mountNode);
```

View File

@ -13,11 +13,47 @@ title:
Click the menu and you will see that all the other menus gets collapsed to keep the entire menu compact.
```jsx
import { Menu } from 'antd';
```tsx
import { Menu, MenuProps } from 'antd';
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
): MenuItem {
return {
key,
icon,
children,
label,
type,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Navigation One', 'sub1', <MailOutlined />, [
getItem('Option 1', '1'),
getItem('Option 2', '2'),
getItem('Option 3', '3'),
getItem('Option 4', '4'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Submenu', 'sub3', null, [getItem('Option 7', '7'), getItem('Option 8', '8')]),
]),
getItem('Navigation Three', 'sub4', <SettingOutlined />, [
getItem('Option 9', '9'),
getItem('Option 10', '10'),
getItem('Option 11', '11'),
getItem('Option 12', '12'),
]),
];
// submenu keys of first level
const rootSubmenuKeys = ['sub1', 'sub2', 'sub4'];
@ -25,9 +61,9 @@ const rootSubmenuKeys = ['sub1', 'sub2', 'sub4'];
const Sider = () => {
const [openKeys, setOpenKeys] = React.useState(['sub1']);
const onOpenChange = keys => {
const onOpenChange: MenuProps['onOpenChange'] = keys => {
const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
if (rootSubmenuKeys.indexOf(latestOpenKey!) === -1) {
setOpenKeys(keys);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
@ -35,28 +71,13 @@ const Sider = () => {
};
return (
<Menu mode="inline" openKeys={openKeys} onOpenChange={onOpenChange} style={{ width: 256 }}>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
</SubMenu>
</SubMenu>
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
<Menu.Item key="9">Option 9</Menu.Item>
<Menu.Item key="10">Option 10</Menu.Item>
<Menu.Item key="11">Option 11</Menu.Item>
<Menu.Item key="12">Option 12</Menu.Item>
</SubMenu>
</Menu>
<Menu
mode="inline"
openKeys={openKeys}
onOpenChange={onOpenChange}
style={{ width: 256 }}
items={items}
/>
);
};

View File

@ -19,91 +19,92 @@ import * as React from 'react';
import { Menu, MenuProps, Switch } from 'antd';
import { MailOutlined, AppstoreOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
interface DemoState {
theme: 'light' | 'dark';
current: string;
function getItem(
label: React.ReactNode,
key?: React.Key | null,
icon?: React.ReactNode,
children?: MenuItem[],
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
class Demo extends React.Component<{}, DemoState> {
state: DemoState = {
theme: 'dark',
current: '1',
const items: MenuItem[] = [
getItem('Navigation One Long Long Long Long', 'sub1', <MailOutlined />, [
getItem('Option 1', '1'),
getItem('Option 2', '2'),
getItem('Option 3', '3'),
getItem('Option 4', '4'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Submenu', 'sub3', null, [getItem('Option 7', '7'), getItem('Option 8', '8')]),
]),
getItem('Option 11', '11'),
getItem('Option 12', '12'),
];
const Demo = () => {
const [theme, setTheme] = React.useState<'dark' | 'light'>('dark');
const [current, setCurrent] = React.useState('1');
const changeTheme = (value: boolean) => {
setTheme(value ? 'dark' : 'light');
};
changeTheme = (value: boolean) => {
this.setState({
theme: value ? 'dark' : 'light',
});
};
handleClick: MenuProps['onClick'] = e => {
const onClick: MenuProps['onClick'] = e => {
console.log('click ', e);
this.setState({
current: e.key,
});
setCurrent(e.key);
};
render() {
return (
<>
<Switch
checked={this.state.theme === 'dark'}
onChange={this.changeTheme}
checkedChildren="Dark"
unCheckedChildren="Light"
/>
<br />
<br />
<Menu
theme={this.state.theme}
onClick={this.handleClick}
selectedKeys={[this.state.current]}
mode="inline"
inlineCollapsed
// Test only. Remove in future.
_internalRenderMenuItem={node =>
React.cloneElement(node, {
style: {
...node.props.style,
textDecoration: 'underline',
},
})
}
// Test only. Remove in future.
_internalRenderSubMenuItem={node =>
React.cloneElement(node, {
style: {
...node.props.style,
background: 'rgba(255,255,255,0.3)',
},
})
}
// Test only. Remove in future.
_internalDisableMenuItemTitleTooltip
>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One Long Long Long Long">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
</SubMenu>
</SubMenu>
<Menu.Item key="11">Option 11</Menu.Item>
<Menu.Item key="12">Option 12</Menu.Item>
</Menu>
</>
);
}
}
return (
<>
<Switch
checked={theme === 'dark'}
onChange={changeTheme}
checkedChildren="Dark"
unCheckedChildren="Light"
/>
<br />
<br />
<Menu
theme={theme}
onClick={onClick}
selectedKeys={[current]}
mode="inline"
items={items}
inlineCollapsed
// Test only. Remove in future.
_internalRenderMenuItem={node =>
React.cloneElement(node, {
style: {
...node.props.style,
textDecoration: 'underline',
},
})
}
// Test only. Remove in future.
_internalRenderSubMenuItem={node =>
React.cloneElement(node, {
style: {
...node.props.style,
background: 'rgba(255,255,255,0.3)',
},
})
}
// Test only. Remove in future.
_internalDisableMenuItemTitleTooltip
/>
</>
);
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -13,24 +13,52 @@ title:
The Sub-menu will inherit the theme of `Menu`, but you can override this at the `SubMenu` level via the `theme` prop.
```jsx
import { Menu, Switch } from 'antd';
```tsx
import { Menu, Switch, MenuProps } from 'antd';
import { MailOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
label: React.ReactNode,
key?: React.Key | null,
icon?: React.ReactNode,
children?: MenuItem[],
theme?: 'light' | 'dark',
): MenuItem {
return {
key,
icon,
children,
label,
theme,
} as MenuItem;
}
const SubMenuTheme = () => {
const [theme, setTheme] = React.useState('light');
const [theme, setTheme] = React.useState<'light' | 'dark'>('light');
const [current, setCurrent] = React.useState('1');
const changeTheme = value => {
const changeTheme = (value: boolean) => {
setTheme(value ? 'dark' : 'light');
};
const handleClick = e => {
const onClick: MenuProps['onClick'] = e => {
setCurrent(e.key);
};
const items: MenuItem[] = [
getItem(
'Navigation One',
'sub1',
<MailOutlined />,
[getItem('Option 1', '1'), getItem('Option 2', '2'), getItem('Option 3', '3')],
theme,
),
getItem('Option 5', '5'),
getItem('Option 6', '6'),
];
return (
<>
<Switch
@ -42,21 +70,14 @@ const SubMenuTheme = () => {
<br />
<br />
<Menu
onClick={handleClick}
onClick={onClick}
style={{ width: 256 }}
defaultOpenKeys={['sub1']}
selectedKeys={[current]}
mode="vertical"
theme="dark"
>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One" theme={theme}>
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<Menu.Item key="3">Option 3</Menu.Item>
</SubMenu>
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
</Menu>
items={items}
/>
</>
);
};

View File

@ -13,8 +13,8 @@ title:
Show the dynamic switching mode (between `inline` and `vertical`).
```jsx
import { Menu, Switch, Divider } from 'antd';
```tsx
import { Menu, Switch, Divider, MenuProps } from 'antd';
import {
MailOutlined,
CalendarOutlined,
@ -23,17 +23,54 @@ import {
LinkOutlined,
} from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
label: React.ReactNode,
key?: React.Key | null,
icon?: React.ReactNode,
children?: MenuItem[],
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Navigation One', '1', <MailOutlined />),
getItem('Navigation Two', '2', <CalendarOutlined />),
getItem('Navigation Two', 'sub1', <AppstoreOutlined />, [
getItem('Option 3', '3'),
getItem('Option 4', '4'),
getItem('Submenu', 'sub1-2', null, [getItem('Option 5', '5'), getItem('Option 6', '6')]),
]),
getItem('Navigation Three', 'sub2', <SettingOutlined />, [
getItem('Option 7', '7'),
getItem('Option 8', '8'),
getItem('Option 9', '9'),
getItem('Option 10', '10'),
]),
getItem(
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Ant Design
</a>,
'link',
<LinkOutlined />,
),
];
const Demo = () => {
const [mode, setMode] = React.useState('inline');
const [theme, setTheme] = React.useState('light');
const [mode, setMode] = React.useState<'inline' | 'vertical'>('inline');
const [theme, setTheme] = React.useState<'dark' | 'light'>('light');
const changeMode = value => {
const changeMode = (value: boolean) => {
setMode(value ? 'vertical' : 'inline');
};
const changeTheme = value => {
const changeTheme = (value: boolean) => {
setTheme(value ? 'dark' : 'light');
};
@ -50,33 +87,8 @@ const Demo = () => {
defaultOpenKeys={['sub1']}
mode={mode}
theme={theme}
>
<Menu.Item key="1" icon={<MailOutlined />}>
Navigation One
</Menu.Item>
<Menu.Item key="2" icon={<CalendarOutlined />}>
Navigation Two
</Menu.Item>
<SubMenu key="sub1" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
<SubMenu key="sub1-2" title="Submenu">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
</SubMenu>
</SubMenu>
<SubMenu key="sub2" icon={<SettingOutlined />} title="Navigation Three">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
<Menu.Item key="9">Option 9</Menu.Item>
<Menu.Item key="10">Option 10</Menu.Item>
</SubMenu>
<Menu.Item key="link" icon={<LinkOutlined />}>
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Ant Design
</a>
</Menu.Item>
</Menu>
items={items}
/>
</>
);
};

View File

@ -13,75 +13,85 @@ title:
There are two built-in themes: `light` and `dark`. The default value is `light`.
```jsx
import { Menu, Switch } from 'antd';
```tsx
import { Menu, Switch, MenuProps } from 'antd';
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
class Sider extends React.Component {
state = {
theme: 'dark',
current: '1',
};
changeTheme = value => {
this.setState({
theme: value ? 'dark' : 'light',
});
};
handleClick = e => {
console.log('click ', e);
this.setState({
current: e.key,
});
};
render() {
return (
<>
<Switch
checked={this.state.theme === 'dark'}
onChange={this.changeTheme}
checkedChildren="Dark"
unCheckedChildren="Light"
/>
<br />
<br />
<Menu
theme={this.state.theme}
onClick={this.handleClick}
style={{ width: 256 }}
defaultOpenKeys={['sub1']}
selectedKeys={[this.state.current]}
mode="inline"
>
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
</SubMenu>
</SubMenu>
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
<Menu.Item key="9">Option 9</Menu.Item>
<Menu.Item key="10">Option 10</Menu.Item>
<Menu.Item key="11">Option 11</Menu.Item>
<Menu.Item key="12">Option 12</Menu.Item>
</SubMenu>
</Menu>
</>
);
}
function getItem(
label: React.ReactNode,
key?: React.Key | null,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
): MenuItem {
return {
key,
icon,
children,
label,
type,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Navigation One', 'sub1', <MailOutlined />, [
getItem('Option 1', '1'),
getItem('Option 2', '2'),
getItem('Option 3', '3'),
getItem('Option 4', '4'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Submenu', 'sub3', null, [getItem('Option 7', '7'), getItem('Option 8', '8')]),
]),
getItem('Navigation Three', 'sub4', <SettingOutlined />, [
getItem('Option 9', '9'),
getItem('Option 10', '10'),
getItem('Option 11', '11'),
getItem('Option 12', '12'),
]),
];
const Sider = () => {
const [theme, setTheme] = React.useState<'dark' | 'light'>('dark');
const [current, setCurrent] = React.useState('1');
const changeTheme = (value: boolean) => {
setTheme(value ? 'dark' : 'light');
};
const onClick: MenuProps['onClick'] = e => {
console.log('click ', e);
setCurrent(e.key);
};
return (
<>
<Switch
checked={theme === 'dark'}
onChange={changeTheme}
checkedChildren="Dark"
unCheckedChildren="Light"
/>
<br />
<br />
<Menu
theme={theme}
onClick={onClick}
style={{ width: 256 }}
defaultOpenKeys={['sub1']}
selectedKeys={[current]}
mode="inline"
items={items}
/>
</>
);
};
ReactDOM.render(<Sider />, mountNode);
```

View File

@ -13,43 +13,54 @@ title:
Submenus open as pop-ups.
```jsx
import { Menu } from 'antd';
```tsx
import { Menu, MenuProps } from 'antd';
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons';
const { SubMenu } = Menu;
type MenuItem = Required<MenuProps>['items'][number];
function handleClick(e) {
console.log('click', e);
function getItem(
label: React.ReactNode,
key?: React.Key | null,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
): MenuItem {
return {
key,
icon,
children,
label,
type,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Navigation One', 'sub1', <MailOutlined />, [
getItem('Item 1', null, null, [getItem('Option 1', '1'), getItem('Option 2', '2')], 'group'),
getItem('Item 2', null, null, [getItem('Option 3', '3'), getItem('Option 4', '4')], 'group'),
]),
getItem('Navigation Two', 'sub2', <AppstoreOutlined />, [
getItem('Option 5', '5'),
getItem('Option 6', '6'),
getItem('Submenu', 'sub3', null, [getItem('Option 7', '7'), getItem('Option 8', '8')]),
]),
getItem('Navigation Three', 'sub4', <SettingOutlined />, [
getItem('Option 9', '9'),
getItem('Option 10', '10'),
getItem('Option 11', '11'),
getItem('Option 12', '12'),
]),
];
const onClick: MenuProps['onClick'] = e => {
console.log('click', e);
};
ReactDOM.render(
<Menu onClick={handleClick} style={{ width: 256 }} mode="vertical">
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup title="Item 2">
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">
<Menu.Item key="7">Option 7</Menu.Item>
<Menu.Item key="8">Option 8</Menu.Item>
</SubMenu>
</SubMenu>
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
<Menu.Item key="9">Option 9</Menu.Item>
<Menu.Item key="10">Option 10</Menu.Item>
<Menu.Item key="11">Option 11</Menu.Item>
<Menu.Item key="12">Option 12</Menu.Item>
</SubMenu>
</Menu>,
<Menu onClick={onClick} style={{ width: 256 }} mode="vertical" items={items} />,
mountNode,
);
```

View File

@ -0,0 +1,93 @@
import * as React from 'react';
import { ItemGroup } from 'rc-menu';
import type {
MenuItemType as RcMenuItemType,
MenuDividerType as RcMenuDividerType,
SubMenuType as RcSubMenuType,
MenuItemGroupType as RcMenuItemGroupType,
} from 'rc-menu/lib/interface';
import SubMenu from '../SubMenu';
import MenuDivider from '../MenuDivider';
import MenuItem from '../MenuItem';
interface MenuItemType extends RcMenuItemType {
danger?: boolean;
icon?: React.ReactNode;
title?: string;
}
interface SubMenuType extends Omit<RcSubMenuType, 'children'> {
icon?: React.ReactNode;
theme?: 'dark' | 'light';
children: ItemType[];
}
interface MenuItemGroupType extends Omit<RcMenuItemGroupType, 'children'> {
children?: MenuItemType[];
key?: React.Key;
}
interface MenuDividerType extends RcMenuDividerType {
dashed?: boolean;
key?: React.Key;
}
export type ItemType = MenuItemType | SubMenuType | MenuItemGroupType | MenuDividerType | null;
function convertItemsToNodes(list: ItemType[]) {
return (list || [])
.map((opt, index) => {
if (opt && typeof opt === 'object') {
const { label, children, key, type, ...restProps } = opt as any;
const mergedKey = key ?? `tmp-${index}`;
// MenuItemGroup & SubMenuItem
if (children || type === 'group') {
if (type === 'group') {
// Group
return (
<ItemGroup key={mergedKey} {...restProps} title={label}>
{convertItemsToNodes(children)}
</ItemGroup>
);
}
// Sub Menu
return (
<SubMenu key={mergedKey} {...restProps} title={label}>
{convertItemsToNodes(children)}
</SubMenu>
);
}
// MenuItem & Divider
if (type === 'divider') {
return <MenuDivider key={mergedKey} {...restProps} />;
}
return (
<MenuItem key={mergedKey} {...restProps}>
{label}
</MenuItem>
);
}
return null;
})
.filter(opt => opt);
}
// FIXME: Move logic here in v5
/**
* We simply convert `items` to ReactNode for reuse origin component logic. But we need move all the
* logic from component into this hooks when in v5
*/
export default function useItems(items?: ItemType[]) {
return React.useMemo(() => {
if (!items) {
return items;
}
return convertItemsToNodes(items);
}, [items]);
}

View File

@ -22,12 +22,14 @@ More layouts with navigation: [Layout](/components/layout).
## API
```jsx
<Menu>
<Menu.Item>Menu</Menu.Item>
<SubMenu title="SubMenu">
<Menu.Item>SubMenuItem</Menu.Item>
</SubMenu>
</Menu>
const items = [
{ label: 'Menu' },
{
label: 'SubMenu',
children: [{ label: 'SubMenuItem' }],
},
];
<Menu items={items} />;
```
### Menu
@ -40,6 +42,7 @@ More layouts with navigation: [Layout](/components/layout).
| forceSubMenuRender | Render submenu into DOM before it becomes visible | boolean | false | |
| inlineCollapsed | Specifies the collapsed status when menu is inline mode | boolean | - | |
| inlineIndent | Indent (in pixels) of inline menu items on each level | number | 24 | |
| items | Menu item content | [ItemType\[\]](#ItemType) | - | 4.20.0 |
| mode | Type of menu | `vertical` \| `horizontal` \| `inline` | `vertical` | |
| multiple | Allows selection of multiple items | boolean | false | |
| openKeys | Array with the keys of currently opened sub-menus | string\[] | - | |
@ -58,14 +61,19 @@ More layouts with navigation: [Layout](/components/layout).
> More options in [rc-menu](https://github.com/react-component/menu#api)
### Menu.Item
### ItemType
> type ItemType = [MenuItemType](#MenuItemType) | [SubMenuType](#SubMenuType) | [MenuItemGroupType](#MenuItemGroupType) | [MenuDividerType](#MenuDividerType);
#### MenuItemType
| Param | Description | Type | Default value | Version |
| -------- | ------------------------------------ | --------- | ------------- | ------- |
| danger | Display the danger style | boolean | false | 4.3.0 |
| danger | Display the danger style | boolean | false | |
| disabled | Whether menu item is disabled | boolean | false | |
| icon | The icon of the menu item | ReactNode | - | 4.2.0 |
| icon | The icon of the menu item | ReactNode | - | |
| key | Unique ID of the menu item | string | - | |
| label | Menu label | ReactNode | - | |
| title | Set display title for collapsed item | string | - | |
> Note: `icon` is a newly added prop in `4.2.0`. For previous versions, please use the following method to define the icon.
@ -87,34 +95,51 @@ More layouts with navigation: [Layout](/components/layout).
> </Menu.SubMenu>
> ```
### Menu.SubMenu
#### SubMenuType
| Param | Description | Type | Default value | Version |
| --- | --- | --- | --- | --- | --- |
| children | Sub-menus or sub-menu items | Array&lt;MenuItem \| SubMenu> | - | |
| children | Sub-menus or sub-menu items | [ItemType\[\]](#ItemType) | - | |
| disabled | Whether sub-menu is disabled | boolean | false | |
| icon | Icon of sub menu | ReactNode | - | 4.2.0 |
| icon | Icon of sub menu | ReactNode | - | |
| key | Unique ID of the sub-menu | string | - | |
| label | Menu label | ReactNode | - | |
| popupClassName | Sub-menu class name, not working when `mode="inline"` | string | - | |
| popupOffset | Sub-menu offset, not working when `mode="inline"` | \[number, number] | - | |
| title | Title of sub menu | ReactNode | - | |
| theme | Color theme of the SubMenu (inherits from Menu by default) | | `light` \| `dark` | - | 4.19.0 |
| theme | Color theme of the SubMenu (inherits from Menu by default) | | `light` \| `dark` | - | |
| onTitleClick | Callback executed when the sub-menu title is clicked | function({ key, domEvent }) | - | |
### Menu.ItemGroup
#### MenuItemGroupType
Define `type` as `group` to make as group:
```ts
const groupItem = {
type: 'group', // Must have
label: 'My Group',
chidlren: [],
};
```
| Param | Description | Type | Default value | Version |
| -------- | ---------------------- | ----------- | ------------- | ------- |
| children | Sub-menu items | MenuItem\[] | - | |
| title | The title of the group | ReactNode | - | |
| label | The title of the group | ReactNode | - | |
### Menu.Divider
#### MenuDividerType
Divider line in between menu items, only used in vertical popup Menu or Dropdown Menu.
Divider line in between menu items, only used in vertical popup Menu or Dropdown Menu. Need define the `type` as `divider`
```ts
const dividerItem = {
type: 'divider', // Must have
};
```
| Param | Description | Type | Default value | Version |
| ------ | ---------------------- | ------- | ------------- | ------- |
| dashed | Whether line is dashed | boolean | false | 4.17.0 |
| dashed | Whether line is dashed | boolean | false | |
## FAQ

View File

@ -12,6 +12,8 @@ import collapseMotion from '../_util/motion';
import { cloneElement } from '../_util/reactNode';
import MenuContext, { MenuTheme } from './MenuContext';
import MenuDivider from './MenuDivider';
import type { ItemType } from './hooks/useItems';
import useItems from './hooks/useItems';
export { MenuDividerProps } from './MenuDivider';
@ -19,7 +21,7 @@ export { MenuItemGroupProps } from 'rc-menu';
export type MenuMode = 'vertical' | 'vertical-left' | 'vertical-right' | 'horizontal' | 'inline';
export interface MenuProps extends RcMenuProps {
export interface MenuProps extends Omit<RcMenuProps, 'items'> {
theme?: MenuTheme;
inlineIndent?: number;
@ -29,6 +31,8 @@ export interface MenuProps extends RcMenuProps {
* for removing.
*/
_internalDisableMenuItemTitleTooltip?: boolean;
items?: ItemType[];
}
type InternalMenuProps = MenuProps &
@ -49,11 +53,16 @@ function InternalMenu(props: InternalMenuProps) {
_internalDisableMenuItemTitleTooltip,
inlineCollapsed,
siderCollapsed,
items,
children,
...restProps
} = props;
const passedProps = omit(restProps, ['collapsedWidth']);
// ========================= Items ===========================
const mergedChildren = useItems(items) || children;
// ======================== Warning ==========================
devWarning(
!('inlineCollapsed' in props && props.mode !== 'inline'),
@ -67,6 +76,12 @@ function InternalMenu(props: InternalMenuProps) {
'`inlineCollapsed` not control Menu under Sider. Should set `collapsed` on Sider instead.',
);
devWarning(
!!items && !children,
'Menu',
'`children` will be removed in next major version. Please use `items` instead.',
);
// ======================== Collapsed ========================
// Inline Collapsed
const mergedInlineCollapsed = React.useMemo(() => {
@ -114,7 +129,9 @@ function InternalMenu(props: InternalMenuProps) {
expandIcon={cloneElement(expandIcon, {
className: `${prefixCls}-submenu-expand-icon`,
})}
/>
>
{mergedChildren}
</RcMenu>
</MenuContext.Provider>
);
}

View File

@ -23,12 +23,15 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3XZcjGpvK/Menu.svg
## API
```jsx
<Menu>
<Menu.Item>菜单项</Menu.Item>
<SubMenu title="子菜单">
<Menu.Item>子菜单项</Menu.Item>
</SubMenu>
</Menu>
const items = [
{ label: '菜单项' },
{
label: '子菜单',
children: [{ label: '子菜单项' }],
},
];
<Menu items={items} />;
```
### Menu
@ -41,6 +44,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3XZcjGpvK/Menu.svg
| forceSubMenuRender | 在子菜单展示之前就渲染进 DOM | boolean | false | |
| inlineCollapsed | inline 时菜单是否收起状态 | boolean | - | |
| inlineIndent | inline 模式的菜单缩进宽度 | number | 24 | |
| items | 菜单内容 | [ItemType\[\]](#ItemType) | - | 4.20.0 |
| mode | 菜单类型,现在支持垂直、水平、和内嵌模式三种 | `vertical` \| `horizontal` \| `inline` | `vertical` | |
| multiple | 是否允许多选 | boolean | false | |
| openKeys | 当前展开的 SubMenu 菜单项 key 数组 | string\[] | - | |
@ -59,15 +63,20 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3XZcjGpvK/Menu.svg
> 更多属性查看 [rc-menu](https://github.com/react-component/menu#api)
### Menu.Item
### ItemType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------------------ | --------- | ------ | ----- |
| danger | 展示错误状态样式 | boolean | false | 4.3.0 |
| disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | ReactNode | - | 4.2.0 |
| key | item 的唯一标志 | string | - | |
| title | 设置收缩时展示的悬浮标题 | string | - | |
> type ItemType = [MenuItemType](#MenuItemType) | [SubMenuType](#SubMenuType) | [MenuItemGroupType](#MenuItemGroupType) | [MenuDividerType](#MenuDividerType);
#### MenuItemType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------------------ | --------- | ------ | ---- |
| danger | 展示错误状态样式 | boolean | false | |
| disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | ReactNode | - | |
| key | item 的唯一标志 | string | - | |
| label | 菜单项标题 | string | - | |
| title | 设置收缩时展示的悬浮标题 | string | - | |
> 注意:`icon` 是 `4.2.0` 新增的属性,之前的版本请使用下面的方式定义图标。
>
@ -88,34 +97,50 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3XZcjGpvK/Menu.svg
> </Menu.SubMenu>
> ```
### Menu.SubMenu
#### SubMenuType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | --- |
| children | 子菜单的菜单项 | Array&lt;MenuItem \| SubMenu> | - | |
| children | 子菜单的菜单项 | [ItemType\[\]](#ItemType) | - | |
| disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | ReactNode | - | 4.2.0 |
| icon | 菜单图标 | ReactNode | - | |
| key | 唯一标志 | string | - | |
| label | 菜单项标题 | ReactNode | - | |
| popupClassName | 子菜单样式,`mode="inline"` 时无效 | string | - | |
| popupOffset | 子菜单偏移量,`mode="inline"` 时无效 | \[number, number] | - | |
| title | 子菜单项值 | ReactNode | - | |
| onTitleClick | 点击子菜单标题 | function({ key, domEvent }) | - | |
| theme | 设置子菜单的主题,默认从 Menu 上继承 | | `light` \| `dark` | - | 4.19.0 |
| theme | 设置子菜单的主题,默认从 Menu 上继承 | | `light` \| `dark` | - | |
### Menu.ItemGroup
#### MenuItemGroupType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------ | ----------- | ------ | ---- |
| children | 分组的菜单项 | MenuItem\[] | - | |
| title | 分组标题 | ReactNode | - | |
定义类型为 `group` 时,会作为分组处理:
### Menu.Divider
```ts
const groupItem = {
type: 'group', // Must have
label: 'My Group',
chidlren: [],
};
```
菜单项分割线,只用在弹出菜单内。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------ | --------------------------------- | ------ | ---- |
| children | 分组的菜单项 | [MenuItemType\[\]](#MenuItemType) | - | |
| label | 分组标题 | ReactNode | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------ | -------- | ------- | ------ | ------ |
| dashed | 是否虚线 | boolean | false | 4.17.0 |
#### MenuDividerType
菜单项分割线,只用在弹出菜单内,需要定义类型为 `divider`
```ts
const dividerItem = {
type: 'divider', // Must have
};
```
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------ | -------- | ------- | ------ | ---- |
| dashed | 是否虚线 | boolean | false | |
## FAQ

View File

@ -20,23 +20,31 @@ import { MoreOutlined } from '@ant-design/icons';
const { Paragraph } = Typography;
const menu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
3rd menu item
</a>
</Menu.Item>
</Menu>
<Menu
items={[
{
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
1st menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
2nd menu item
</a>
),
},
{
label: (
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
3rd menu item
</a>
),
},
]}
/>
);
const DropdownMenu = () => (

View File

@ -18,12 +18,7 @@ To see if bordered style applied to other tables.
import { Table, Badge, Menu, Dropdown, Switch, Form, Space } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item>Action 1</Menu.Item>
<Menu.Item>Action 2</Menu.Item>
</Menu>
);
const menu = <Menu items={[{ label: 'Action 1' }, { label: 'Action 2' }]} />;
function NestedTable() {
const createExpandedRowRender = bordered => () => {

View File

@ -17,12 +17,7 @@ Showing more detailed info of every row.
import { Table, Badge, Menu, Dropdown, Space } from 'antd';
import { DownOutlined } from '@ant-design/icons';
const menu = (
<Menu>
<Menu.Item>Action 1</Menu.Item>
<Menu.Item>Action 2</Menu.Item>
</Menu>
);
const menu = <Menu items={[{ label: 'Action 1' }, { label: 'Action 2' }]} />;
function NestedTable() {
const expandedRowRender = () => {

View File

@ -4,6 +4,7 @@ import isEqual from 'lodash/isEqual';
import FilterFilled from '@ant-design/icons/FilterFilled';
import Button from '../../../button';
import Menu from '../../../menu';
import type { MenuProps } from '../../../menu';
import Tree from '../../../tree';
import type { DataNode, EventDataNode } from '../../../tree';
import Checkbox from '../../../checkbox';
@ -55,42 +56,42 @@ function renderFilterItems({
filterMultiple: boolean;
searchValue: string;
filterSearch: FilterSearchType;
}) {
}): Required<MenuProps>['items'] {
return filters.map((filter, index) => {
const key = String(filter.value);
if (filter.children) {
return (
<Menu.SubMenu
key={key || index}
title={filter.text}
popupClassName={`${prefixCls}-dropdown-submenu`}
>
{renderFilterItems({
filters: filter.children,
prefixCls,
filteredKeys,
filterMultiple,
searchValue,
filterSearch,
})}
</Menu.SubMenu>
);
return {
key: key || index,
label: filter.text,
popupClassName: `${prefixCls}-dropdown-submenu`,
children: renderFilterItems({
filters: filter.children,
prefixCls,
filteredKeys,
filterMultiple,
searchValue,
filterSearch,
}),
};
}
const Component = filterMultiple ? Checkbox : Radio;
const item = (
<Menu.Item key={filter.value !== undefined ? key : index}>
<Component checked={filteredKeys.includes(key)} />
<span>{filter.text}</span>
</Menu.Item>
);
const item = {
key: filter.value !== undefined ? key : index,
label: (
<>
<Component checked={filteredKeys.includes(key)} />
<span>{filter.text}</span>
</>
),
};
if (searchValue.trim()) {
if (typeof filterSearch === 'function') {
return filterSearch(searchValue, filter) ? item : undefined;
return filterSearch(searchValue, filter) ? item : null;
}
return searchValueMatched(searchValue, filter.text) ? item : undefined;
return searchValueMatched(searchValue, filter.text) ? item : null;
}
return item;
});
@ -381,8 +382,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
getPopupContainer={getPopupContainer}
openKeys={openKeys}
onOpenChange={onOpenChange}
>
{renderFilterItems({
items={renderFilterItems({
filters: column.filters || [],
filterSearch,
prefixCls,
@ -390,7 +390,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
filterMultiple,
searchValue,
})}
</Menu>
/>
</>
);
};

View File

@ -405,21 +405,20 @@ export default function useSelection<RecordType>(
let customizeSelections: React.ReactNode;
if (mergedSelections) {
const menu = (
<Menu getPopupContainer={getPopupContainer}>
{mergedSelections.map((selection, index) => {
<Menu
getPopupContainer={getPopupContainer}
items={mergedSelections.map((selection, index) => {
const { key, text, onSelect: onSelectionClick } = selection;
return (
<Menu.Item
key={key || index}
onClick={() => {
onSelectionClick?.(recordKeys);
}}
>
{text}
</Menu.Item>
);
return {
key: key || index,
onClick: () => {
onSelectionClick?.(recordKeys);
},
label: text,
};
})}
</Menu>
/>
);
customizeSelections = (
<div className={`${prefixCls}-selection-extra`}>

View File

@ -365,88 +365,85 @@ export default class TransferList<
let menu: React.ReactElement | null = null;
if (showRemove) {
menu = (
<Menu>
{/* Remove Current Page */}
{pagination && (
<Menu.Item
key="removeCurrent"
onClick={() => {
const items = [
/* Remove Current Page */
pagination
? {
key: 'removeCurrent',
onClick: () => {
const pageKeys = getEnabledItemKeys(
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
);
onItemRemove?.(pageKeys);
}}
>
{removeCurrent}
</Menu.Item>
)}
},
label: removeCurrent,
}
: null,
{/* Remove All */}
<Menu.Item
key="removeAll"
onClick={() => {
onItemRemove?.(getEnabledItemKeys(filteredItems));
}}
>
{removeAll}
</Menu.Item>
</Menu>
);
/* Remove All */
{
key: 'removeAll',
onClick: () => {
onItemRemove?.(getEnabledItemKeys(filteredItems));
},
label: removeAll,
},
].filter(item => item);
menu = <Menu items={items} />;
} else {
menu = (
<Menu>
<Menu.Item
key="selectAll"
onClick={() => {
const keys = getEnabledItemKeys(filteredItems);
onItemSelectAll(keys, keys.length !== checkedKeys.length);
}}
>
{selectAll}
</Menu.Item>
{pagination && (
<Menu.Item
onClick={() => {
const items = [
{
key: 'selectAll',
onClick: () => {
const keys = getEnabledItemKeys(filteredItems);
onItemSelectAll(keys, keys.length !== checkedKeys.length);
},
label: selectAll,
},
pagination
? {
key: 'selectCurrent',
onClick: () => {
const pageItems = this.defaultListBodyRef.current?.getItems() || [];
onItemSelectAll(getEnabledItemKeys(pageItems.map(entity => entity.item)), true);
}}
>
{selectCurrent}
</Menu.Item>
)}
<Menu.Item
key="selectInvert"
onClick={() => {
let availableKeys: string[];
if (pagination) {
availableKeys = getEnabledItemKeys(
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
);
},
label: selectCurrent,
}
: null,
{
key: 'selectInvert',
onClick: () => {
let availableKeys: string[];
if (pagination) {
availableKeys = getEnabledItemKeys(
(this.defaultListBodyRef.current?.getItems() || []).map(entity => entity.item),
);
} else {
availableKeys = getEnabledItemKeys(filteredItems);
}
const checkedKeySet = new Set(checkedKeys);
const newCheckedKeys: string[] = [];
const newUnCheckedKeys: string[] = [];
availableKeys.forEach(key => {
if (checkedKeySet.has(key)) {
newUnCheckedKeys.push(key);
} else {
availableKeys = getEnabledItemKeys(filteredItems);
newCheckedKeys.push(key);
}
});
const checkedKeySet = new Set(checkedKeys);
const newCheckedKeys: string[] = [];
const newUnCheckedKeys: string[] = [];
onItemSelectAll(newCheckedKeys, true);
onItemSelectAll(newUnCheckedKeys, false);
},
label: selectInvert,
},
];
availableKeys.forEach(key => {
if (checkedKeySet.has(key)) {
newUnCheckedKeys.push(key);
} else {
newCheckedKeys.push(key);
}
});
onItemSelectAll(newCheckedKeys, true);
onItemSelectAll(newUnCheckedKeys, false);
}}
>
{selectInvert}
</Menu.Item>
</Menu>
);
menu = <Menu items={items} />;
}
const dropdown = (

View File

@ -132,7 +132,7 @@
"rc-input": "~0.0.1-alpha.5",
"rc-input-number": "~7.3.0",
"rc-mentions": "~1.6.1",
"rc-menu": "~9.3.2",
"rc-menu": "~9.5.1",
"rc-motion": "^2.4.4",
"rc-notification": "~4.5.7",
"rc-pagination": "~3.1.9",