Menu 菜单

如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令npm install @alifd/next@latest -S

API

Menu

参数说明类型默认值
children菜单项和子菜单ReactNode-
onItemClick点击菜单项触发的回调函数签名:Function(key: String, item: Object, event: Object) => void参数:key: {String} 点击的菜单项的 key 值item: {Object} 点击的菜单项对象event: {Object} 点击的事件对象Function() => {}
openKeys当前打开的子菜单的 key 值String/Array-
defaultOpenKeys初始打开的子菜单的 key 值String/Array[]
defaultOpenAll初始展开所有的子菜单,只在 mode 设置为 'inline' 以及 openMode 设置为 'multiple' 下生效,优先级高于 defaultOpenKeysBooleanfalse
onOpen打开或关闭子菜单触发的回调函数签名:Function(key: String, extra: Object) => void参数:key: {String} 打开的所有子菜单的 key 值extra: {Object} 额外参数extra.key: {String} 当前操作子菜单的 key 值extra.open: {Boolean} 是否是打开Function() => {}
mode子菜单打开的模式可选值:'inline', 'popup'Enum'inline'
triggerType子菜单打开的触发行为可选值:'click', 'hover'Enum'click'
openMode展开内连子菜单的模式,同时可以展开一个子菜单还是多个子菜单,该属性仅在 mode 为 inline 时生效可选值:'single', 'multiple'Enum'multiple'
inlineIndent内连子菜单缩进距离Number20
popupAutoWidth是否自动让弹层的宽度和菜单项保持一致,如果弹层的宽度比菜单项小则和菜单项保持一致,如果宽度大于菜单项则不做处理Booleanfalse
popupAlign弹层的对齐方式可选值:'follow', 'outside'Enum'follow'
popupProps弹层自定义 propsObject/Function{}
popupClassName弹出子菜单自定义 classNameString-
popupStyle弹出子菜单自定义 styleObject-
selectedKeys当前选中菜单项的 key 值String/Array-
defaultSelectedKeys初始选中菜单项的 key 值String/Array[]
onSelect选中或取消选中菜单项触发的回调函数签名:Function(selectedKeys: Array, item: Object, extra: Object) => void参数:selectedKeys: {Array} 选中的所有菜单项的值item: {Object} 选中或取消选中的菜单项extra: {Object} 额外参数extra.select: {Boolean} 是否是选中extra.key: {Array} 菜单项的 keyextra.label: {Object} 菜单项的文本extra.keyPath: {Array} 菜单项 key 的路径Function() => {}
selectMode选中模式,单选还是多选,默认无值,不可选可选值:'single', 'multiple'Enum-
shallowSelect是否只能选择第一层菜单项(不能选择子菜单中的菜单项)Booleanfalse
hasSelectedIcon是否显示选中图标,如果设置为 false 需配合配置平台设置选中时的背景色以示区分Booleantrue
isSelectIconRight是否将选中图标居右,仅当 hasSelectedIcon 为true 时生效。注意:SubMenu 上的选中图标一直居左,不受此API控制Booleanfalse
direction菜单第一层展示方向可选值:'ver', 'hoz'Enum'ver'
hozAlign横向菜单条 item 和 footer 的对齐方向,在 direction 设置为 'hoz' 并且 header 存在时生效可选值:'left', 'right'Enum'left'
hozInLine横向菜单模式下,是否维持在一行,即超出一行折叠成 SubMenu 显示, 仅在 direction='hoz' mode='popup' 时生效Booleanfalse
header自定义菜单头部ReactNode-
footer自定义菜单尾部ReactNode-
autoFocus是否自动获得焦点Booleanfalse
focusedKey当前获得焦点的子菜单或菜单项 key 值String-
embeddable是否开启嵌入式模式,一般用于Layout的布局中,开启后没有默认背景、外层border、box-shadow,可以配合<Menu style={{lineHeight: '100px'}}> 自定义高度Booleanfalse

Menu.Item

参数说明类型默认值
disabled是否禁用Booleanfalse
helper帮助文本ReactNode-
children菜单项标签内容ReactNode-

Menu.SubMenu

参数说明类型默认值
label标签内容ReactNode-
selectable是否可选,该属性仅在设置 Menu 组件 selectMode 属性后生效Booleanfalse
mode子菜单打开方式,如果设置会覆盖 Menu 上的同名属性可选值:'inline', 'popup'EnumMenu 的 mode 属性值
children菜单项或下一级子菜单ReactNode-

Menu.PopupItem

参数说明类型默认值
label标签内容ReactNode-
children自定义弹层内容ReactNode-

Menu.CheckboxItem

该子组件选中情况不受 defaultSelectedKeys/selectedKeys 控制,请自行控制选中逻辑

参数说明类型默认值
checked是否选中Booleanfalse
indeterminate是否半选中Booleanfalse
disabled是否禁用Booleanfalse
onChange选中或取消选中触发的回调函数签名:Function(checked: Boolean, event: Object) => void参数:checked: {Boolean} 是否选中event: {Object} 选中事件对象Function() => {}
helper帮助文本ReactNode-
children标签内容ReactNode-

Menu.RadioItem

该子组件选中情况不受 defaultSelectedKeys/selectedKeys 控制,请自行控制选中逻辑

参数说明类型默认值
checked是否选中Booleanfalse
disabled是否禁用Booleanfalse
onChange选中或取消选中触发的回调函数签名:Function(checked: Boolean, event: Object) => void参数:checked: {Boolean} 是否选中event: {Object} 选中事件对象Function() => {}
helper帮助文本ReactNode-
children标签内容ReactNode-

Menu.Group

参数说明类型默认值
label标签内容ReactNode-
children菜单项ReactNode-

Menu.Divider

<!— api-extra-start —>

Menu.create(props)

创建上下文菜单。

  • props 参数可传入 Menu 所有支持的 props

  • props 额外支持 overlayProps,用来自定义 Menu 所在的弹层<!— api-extra-end —>

ARIA and KeyBoard

按键说明
Up Arrow导航到上一项
Down Arrow导航到下一项
Right Arrow打开子菜单,导航到子菜单第一项;横向菜单条第一层,导航到右一项
Left Arrow关闭子菜单,导航到父级菜单;横向菜单条第一层,导航都左一项
Enter打开子菜单,导航到子菜单第一项
Esc关闭子菜单,导航到父级菜单
SPACE切换选中状态

代码示例

基本

展示最基本的用法。

Menu 菜单 - 图1

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item, Group, Divider } = Menu;
  3. ReactDOM.render(
  4. <Menu className="my-menu" defaultOpenKeys="sub-menu">
  5. <Item key="1">Option 1</Item>
  6. <Item disabled key="2">Disabled option 2</Item>
  7. <Divider key="divider" />
  8. <Group label="Group">
  9. <Item key="group-1">Group option 1</Item>
  10. <Item key="group-2">Group option 2</Item>
  11. </Group>
  12. <Divider />
  13. <SubMenu key="sub-menu" label="Sub menu">
  14. <Item key="sub-1">Sub option 1</Item>
  15. <Item key="sub-2">Sub option 2</Item>
  16. <Item disabled key="sub-3">
  17. <a href="https://www.taobao.com/" target="__blank">Disabled Option Link 3</a>
  18. </Item>
  19. <Item key="sub-4">
  20. <a href="https://www.taobao.com/" target="__blank">Option Link 4</a>
  21. </Item>
  22. </SubMenu>
  23. <Item key="3" helper="CTRL+P">Option 3</Item>
  24. <Item disabled key="4">
  25. <a href="https://www.taobao.com/" target="__blank">Disabled Option Link</a>
  26. </Item>
  27. <Item key="5">
  28. <a href="https://www.taobao.com/" target="__blank">Option Link</a>
  29. </Item>
  30. </Menu>
  31. , mountNode);
  1. .my-menu {
  2. width: 200px;
  3. }

内连菜单展开模式

通过设置 openMode 为 'single',可以让菜单同时只能展开一个内连子菜单,默认为可以同时展开多个。

Menu 菜单 - 图2

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item } = Menu;
  3. ReactDOM.render(
  4. <Menu defaultOpenKeys="1" className="my-menu" openMode="single">
  5. <SubMenu key="0" label="Sub menu 1">
  6. <Item key="0-0">Sub option 1</Item>
  7. <Item key="0-1">Sub option 2</Item>
  8. <Item key="0-2">Sub option 3</Item>
  9. </SubMenu>
  10. <SubMenu key="1" label="Sub menu 2">
  11. <Item key="1-0">Sub option 1</Item>
  12. <Item key="1-1">Sub option 2</Item>
  13. <Item key="1-2">Sub option 3</Item>
  14. </SubMenu>
  15. <SubMenu key="2" label="Sub menu 3">
  16. <Item key="2-0">Sub option 1</Item>
  17. <Item key="2-1">Sub option 2</Item>
  18. <Item key="2-2">Sub option 3</Item>
  19. </SubMenu>
  20. <SubMenu key="3" label="Sub menu 4">
  21. <Item key="3-0">Sub option 1</Item>
  22. <Item key="3-1">Sub option 2</Item>
  23. <Item key="3-2">Sub option 3</Item>
  24. </SubMenu>
  25. </Menu>
  26. , mountNode);
  1. .my-menu {
  2. width: 200px;
  3. }

弹出菜单

展示弹出菜单的用法。

Menu 菜单 - 图3

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item, Divider } = Menu;
  3. ReactDOM.render(
  4. <Menu className="my-menu" mode="popup">
  5. <Item key="1">Option 1</Item>
  6. <Item key="2">Option 2</Item>
  7. <Item key="3">Option 3</Item>
  8. <Divider key="divider" />
  9. <SubMenu key="sub-1" label="Popup menu 1">
  10. <Item key="popup-1-1">Popup option 1</Item>
  11. <Item key="popup-1-2">Popup option 2</Item>
  12. </SubMenu>
  13. <SubMenu key="sub-2" label="Popup menu 2">
  14. <Item key="popup-2-1">Popup option 1</Item>
  15. <Item key="popup-2-2">Popup option 2</Item>
  16. </SubMenu>
  17. </Menu>
  18. , mountNode);
  1. .my-menu {
  2. width: 200px;
  3. }

hover 打开子菜单

可以设置 triggerType 为 'hover',来 hover 打开子菜单,默认点击打开子菜单。

Menu 菜单 - 图4

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item, Divider } = Menu;
  3. ReactDOM.render(
  4. <Menu className="my-menu" mode="popup" triggerType="hover">
  5. <Item key="1">Option 1</Item>
  6. <Item key="2">Option 2</Item>
  7. <Item key="3">Option 3</Item>
  8. <Divider key="divider" />
  9. <SubMenu key="sub-1" label="Popup menu 1">
  10. <Item key="popup-1-1">Popup option 1</Item>
  11. <Item key="popup-1-2">Popup option 2</Item>
  12. </SubMenu>
  13. <SubMenu key="sub-2" label="Popup menu 2">
  14. <Item key="popup-2-1">Popup option 1</Item>
  15. <Item key="popup-2-2">Popup option 2</Item>
  16. </SubMenu>
  17. </Menu>
  18. , mountNode);
  1. .my-menu {
  2. width: 200px;
  3. }

弹出菜单对齐方式

可以通过设置 popupAlign 为 'outside',使弹出菜单和父级菜单对齐。

Menu 菜单 - 图5

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item, Divider } = Menu;
  3. ReactDOM.render(
  4. <Menu className="my-menu" mode="popup" popupAlign="outside">
  5. <Item key="1">Option 1</Item>
  6. <Item key="2">Option 2</Item>
  7. <Item key="3">Option 3</Item>
  8. <Divider key="divider" />
  9. <SubMenu key="sub-1" label="Popup menu 1">
  10. <Item key="popup-1-1">Popup option 1</Item>
  11. <Item key="popup-1-2">Popup option 2</Item>
  12. </SubMenu>
  13. <SubMenu key="sub-2" label="Popup menu 2">
  14. <Item key="popup-2-1">Popup option 1</Item>
  15. <Item key="popup-2-2">Popup option 2</Item>
  16. </SubMenu>
  17. </Menu>
  18. , mountNode);
  1. .my-menu {
  2. width: 200px;
  3. }

自定义弹出内容

自定义菜单弹出内容。

Menu 菜单 - 图6

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { PopupItem } = Menu;
  3. class Demo extends React.Component {
  4. render() {
  5. const popupProps = {
  6. target: () => ReactDOM.findDOMNode(this),
  7. offset: [-1, 0],
  8. animation: false
  9. };
  10. return (
  11. <Menu className="my-custom-menu" popupProps={popupProps}>
  12. <PopupItem key="0" label="Popup item 1">
  13. <div className="my-custom-content">Custom content 1</div>
  14. </PopupItem>
  15. <PopupItem key="1" label="Popup item 2">
  16. <div className="my-custom-content">Custom content 2</div>
  17. </PopupItem>
  18. <PopupItem key="2" label="Popup item 3">
  19. <div className="my-custom-content">Custom content 3</div>
  20. </PopupItem>
  21. <PopupItem key="3" label="Popup item 4">
  22. <div className="my-custom-content">Custom content 4</div>
  23. </PopupItem>
  24. </Menu>
  25. );
  26. }
  27. }
  28. ReactDOM.render(<Demo />, mountNode);
  1. .my-custom-menu {
  2. width: 200px;
  3. border: 1px solid #ccc;
  4. padding: 0;
  5. box-shadow: none;
  6. z-index: 1000;
  7. }
  8. .my-custom-content {
  9. width: 400px;
  10. height: 200px;
  11. background: #fff;
  12. border: 1px solid #ccc;
  13. line-height: 200px;
  14. text-align: center;
  15. font-size: 20px;
  16. }

菜单项选择

展示菜单项选择用法。

Menu 菜单 - 图7

查看源码在线预览

  1. import { Switch, Menu } from '@alifd/next';
  2. const { SubMenu, Item } = Menu;
  3. class Demo extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.state = {
  7. multiple: false,
  8. subMenuSelectable: false,
  9. shallowSelect: false,
  10. isSelectIconRight: false,
  11. selectedKeys: ['1']
  12. };
  13. [
  14. 'handleMultipleChange', 'handleSubMenuSelectableChange',
  15. 'handleShallowSelectChange', 'handleSelect', 'handleIconDirectionChange'
  16. ].forEach(method => {
  17. this[method] = this[method].bind(this);
  18. });
  19. }
  20. handleMultipleChange() {
  21. this.setState({
  22. multiple: !this.state.multiple,
  23. selectedKeys: []
  24. });
  25. }
  26. handleIconDirectionChange() {
  27. this.setState({
  28. isSelectIconRight: !this.state.isSelectIconRight,
  29. selectedKeys: []
  30. });
  31. }
  32. handleSubMenuSelectableChange() {
  33. this.setState({
  34. subMenuSelectable: !this.state.subMenuSelectable,
  35. selectedKeys: []
  36. });
  37. }
  38. handleShallowSelectChange() {
  39. this.setState({
  40. shallowSelect: !this.state.shallowSelect,
  41. selectedKeys: []
  42. });
  43. }
  44. handleSelect(selectedKeys, ...others) {
  45. this.setState({
  46. selectedKeys
  47. });
  48. console.log(selectedKeys, ...others);
  49. }
  50. render() {
  51. const { multiple, subMenuSelectable, shallowSelect, selectedKeys, isSelectIconRight } = this.state;
  52. const selectMode = multiple ? 'multiple' : 'single';
  53. return (
  54. <div>
  55. <div>
  56. <span className="my-switch-label">Multiple </span>
  57. <Switch value={multiple} onChange={this.handleMultipleChange} />
  58. </div>
  59. <div>
  60. <span className="my-switch-label">isSelectIconRight </span>
  61. <Switch value={multiple} onChange={this.handleIconDirectionChange} />
  62. </div>
  63. <div>
  64. <span className="my-switch-label">Label of submenu selectable </span>
  65. <Switch value={multiple} onChange={this.handleSubMenuSelectableChange} />
  66. </div>
  67. <div>
  68. <span className="my-switch-label">Only first level selectable </span>
  69. <Switch value={multiple} onChange={this.handleShallowSelectChange} />
  70. </div>
  71. <Menu isSelectIconRight={isSelectIconRight} className="my-select-menu" defaultOpenKeys={['sub']} selectMode={selectMode} selectedKeys={selectedKeys} shallowSelect={shallowSelect} onSelect={this.handleSelect}>
  72. <Item key="1">Option 1</Item>
  73. <Item disabled key="2">Disabled option 2</Item>
  74. <SubMenu key="sub" label="Sub menu" selectable={subMenuSelectable}>
  75. <Item key="sub-1">Sub option 1</Item>
  76. <Item key="sub-2">Sub option 2</Item>
  77. </SubMenu>
  78. <Item key="3">Option 3</Item>
  79. </Menu>
  80. </div>
  81. );
  82. }
  83. }
  84. ReactDOM.render(<Demo />, mountNode);
  1. .my-switch-label {
  2. vertical-align: super;
  3. }
  4. .my-select-menu {
  5. margin-top: 10px;
  6. width: 200px;
  7. }

自定义菜单项选择

展示自定义组合菜单项可选的用法。

Menu 菜单 - 图8

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { CheckboxItem, RadioItem, Divider } = Menu;
  3. const sexs = ['male', 'female'];
  4. const balls = ['football', 'basketball', 'volleyball'];
  5. class Demo extends React.Component {
  6. constructor(props) {
  7. super(props);
  8. this.state = {
  9. sex: 'male',
  10. balls: []
  11. };
  12. this.handleSexCheck = this.handleSexCheck.bind(this);
  13. this.handleBallCheck = this.handleBallCheck.bind(this);
  14. }
  15. handleSexCheck(key) {
  16. this.setState({
  17. sex: key
  18. });
  19. }
  20. handleBallCheck(key, check) {
  21. let newKeys;
  22. const index = this.state.balls.indexOf(key);
  23. if (check && index === -1) {
  24. newKeys = this.state.balls.concat(key);
  25. } else if (!check && index > -1) {
  26. newKeys = [
  27. ...this.state.balls.slice(0, index),
  28. ...this.state.balls.slice(index + 1)
  29. ];
  30. }
  31. if (newKeys) {
  32. this.setState({
  33. balls: newKeys
  34. });
  35. }
  36. }
  37. render() {
  38. return (
  39. <Menu className="my-menu">
  40. {sexs.map(sex => (
  41. <RadioItem key={sex} checked={this.state.sex === sex} onChange={this.handleSexCheck.bind(this, sex)}>
  42. {sex}
  43. </RadioItem>
  44. ))}
  45. <Divider key="divider" />
  46. {balls.map(ball => (
  47. <CheckboxItem key={ball} checked={this.state.balls.indexOf(ball) > -1} onChange={this.handleBallCheck.bind(this, ball)}>
  48. {ball}
  49. </CheckboxItem>
  50. ))}
  51. </Menu>
  52. );
  53. }
  54. }
  55. ReactDOM.render(<Demo />, mountNode);
  1. .my-menu {
  2. width: 200px;
  3. }

横向菜单条

展示横向导航菜单条的用法。

Menu 菜单 - 图9

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item } = Menu;
  3. ReactDOM.render(
  4. <Menu hozInLine direction="hoz" mode="popup" className="my-hoz-menu" popupClassName="my-hoz-menu" popupAutoWidth>
  5. <Item key="1">First</Item>
  6. <Item key="2">Second</Item>
  7. <SubMenu label="Sub Nav">
  8. <Item key="sub-12">Sub option 1</Item>
  9. <Item key="sub-22">Sub option 2</Item>
  10. <SubMenu label="Sub Sub Nav">
  11. <Item key="sub-sub-122">Sub sub option 1</Item>
  12. <Item key="sub-sub-222">Sub sub option 2</Item>
  13. </SubMenu>
  14. </SubMenu>
  15. <SubMenu label="Sub Nav">
  16. <Item key="sub-1">Sub option 1</Item>
  17. <Item key="sub-2">Sub option 2</Item>
  18. <SubMenu label="Sub Sub Nav">
  19. <Item key="sub-sub-1">Sub sub option 1</Item>
  20. <Item key="sub-sub-2">Sub sub option 2</Item>
  21. </SubMenu>
  22. </SubMenu>
  23. <Item key="3">Third</Item>
  24. </Menu>
  25. , mountNode);
  1. .my-hoz-menu .next-menu-item {
  2. width: 160px;
  3. }
  4. .my-hoz-menu .next-menu-item.next-menu-more {
  5. width: 40px;
  6. }

创建上下文菜单

展示如何创建自定义的上下文菜单。

Menu 菜单 - 图10

查看源码在线预览

  1. import { Menu } from '@alifd/next';
  2. const { SubMenu, Item, Divider } = Menu;
  3. class Demo extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.state = {
  7. selectedKeys: []
  8. };
  9. this.handleSelect = this.handleSelect.bind(this);
  10. }
  11. handleSelect(selectedKeys) {
  12. selectedKeys = selectedKeys.filter(key => {
  13. return ['sub-1', 'sub-2'].indexOf(key) > -1;
  14. });
  15. this.setState({
  16. selectedKeys
  17. });
  18. }
  19. createContextMenu = e => {
  20. e.preventDefault();
  21. const target = e.target;
  22. const { top, left } = target.getBoundingClientRect();
  23. Menu.create({
  24. target: e.target,
  25. offset: [e.clientX - left, e.clientY - top],
  26. className: 'context-menu',
  27. popupClassName: 'context-menu',
  28. onItemClick: console.log,
  29. selectedKeys: this.state.selectedKeys,
  30. selectMode: 'multiple',
  31. onSelect: this.handleSelect,
  32. children: [
  33. <Item key="1">Option 1</Item>,
  34. <Item key="2">Option 2</Item>,
  35. <Item key="3">Option 3</Item>,
  36. <Divider key="divider-1" />,
  37. <SubMenu key="sub-menu" label="Sub menu">
  38. <Item key="sub-1">Sub option 1</Item>
  39. <Item key="sub-2">Sub option 2</Item>
  40. </SubMenu>,
  41. <Item key="4">Option 4</Item>,
  42. <Divider key="divider-2" />,
  43. <Item key="5">Option 5</Item>
  44. ]
  45. });
  46. };
  47. render() {
  48. return (
  49. <div className="context-demo" onContextMenu={this.createContextMenu}>
  50. Right click here to see the context menu!
  51. </div>
  52. );
  53. }
  54. }
  55. ReactDOM.render(<Demo />, mountNode);
  1. .context-demo {
  2. width: 500px;
  3. height: 200px;
  4. line-height: 200px;
  5. text-align: center;
  6. background: #DDD;
  7. border: 1px solid black;
  8. }
  9. .context-menu {
  10. width: 120px;
  11. }

相关区块

Menu 菜单 - 图11

暂无相关区块