Menu 菜单
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
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' 下生效,优先级高于 defaultOpenKeys | Boolean | false |
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 | 内连子菜单缩进距离 | Number | 20 |
popupAutoWidth | 是否自动让弹层的宽度和菜单项保持一致,如果弹层的宽度比菜单项小则和菜单项保持一致,如果宽度大于菜单项则不做处理 | Boolean | false |
popupAlign | 弹层的对齐方式可选值:'follow', 'outside' | Enum | 'follow' |
popupProps | 弹层自定义 props | Object/Function | {} |
popupClassName | 弹出子菜单自定义 className | String | - |
popupStyle | 弹出子菜单自定义 style | Object | - |
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 | 是否只能选择第一层菜单项(不能选择子菜单中的菜单项) | Boolean | false |
hasSelectedIcon | 是否显示选中图标,如果设置为 false 需配合配置平台设置选中时的背景色以示区分 | Boolean | true |
isSelectIconRight | 是否将选中图标居右,仅当 hasSelectedIcon 为true 时生效。注意:SubMenu 上的选中图标一直居左,不受此API控制 | Boolean | false |
direction | 菜单第一层展示方向可选值:'ver', 'hoz' | Enum | 'ver' |
hozAlign | 横向菜单条 item 和 footer 的对齐方向,在 direction 设置为 'hoz' 并且 header 存在时生效可选值:'left', 'right' | Enum | 'left' |
hozInLine | 横向菜单模式下,是否维持在一行,即超出一行折叠成 SubMenu 显示, 仅在 direction='hoz' mode='popup' 时生效 | Boolean | false |
header | 自定义菜单头部 | ReactNode | - |
footer | 自定义菜单尾部 | ReactNode | - |
autoFocus | 是否自动获得焦点 | Boolean | false |
focusedKey | 当前获得焦点的子菜单或菜单项 key 值 | String | - |
embeddable | 是否开启嵌入式模式,一般用于Layout的布局中,开启后没有默认背景、外层border、box-shadow,可以配合<Menu style={{lineHeight: '100px'}}> 自定义高度 | Boolean | false |
Menu.Item
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
disabled | 是否禁用 | Boolean | false |
helper | 帮助文本 | ReactNode | - |
children | 菜单项标签内容 | ReactNode | - |
Menu.SubMenu
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | 标签内容 | ReactNode | - |
selectable | 是否可选,该属性仅在设置 Menu 组件 selectMode 属性后生效 | Boolean | false |
mode | 子菜单打开方式,如果设置会覆盖 Menu 上的同名属性可选值:'inline', 'popup' | Enum | Menu 的 mode 属性值 |
children | 菜单项或下一级子菜单 | ReactNode | - |
Menu.PopupItem
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | 标签内容 | ReactNode | - |
children | 自定义弹层内容 | ReactNode | - |
Menu.CheckboxItem
该子组件选中情况不受 defaultSelectedKeys/selectedKeys 控制,请自行控制选中逻辑
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
checked | 是否选中 | Boolean | false |
indeterminate | 是否半选中 | Boolean | false |
disabled | 是否禁用 | Boolean | false |
onChange | 选中或取消选中触发的回调函数签名:Function(checked: Boolean, event: Object) => void参数:checked: {Boolean} 是否选中event: {Object} 选中事件对象 | Function | () => {} |
helper | 帮助文本 | ReactNode | - |
children | 标签内容 | ReactNode | - |
Menu.RadioItem
该子组件选中情况不受 defaultSelectedKeys/selectedKeys 控制,请自行控制选中逻辑
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
checked | 是否选中 | Boolean | false |
disabled | 是否禁用 | Boolean | false |
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 | 切换选中状态 |
代码示例
展示最基本的用法。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item, Group, Divider } = Menu;
ReactDOM.render(
<Menu className="my-menu" defaultOpenKeys="sub-menu">
<Item key="1">Option 1</Item>
<Item disabled key="2">Disabled option 2</Item>
<Divider key="divider" />
<Group label="Group">
<Item key="group-1">Group option 1</Item>
<Item key="group-2">Group option 2</Item>
</Group>
<Divider />
<SubMenu key="sub-menu" label="Sub menu">
<Item key="sub-1">Sub option 1</Item>
<Item key="sub-2">Sub option 2</Item>
<Item disabled key="sub-3">
<a href="https://www.taobao.com/" target="__blank">Disabled Option Link 3</a>
</Item>
<Item key="sub-4">
<a href="https://www.taobao.com/" target="__blank">Option Link 4</a>
</Item>
</SubMenu>
<Item key="3" helper="CTRL+P">Option 3</Item>
<Item disabled key="4">
<a href="https://www.taobao.com/" target="__blank">Disabled Option Link</a>
</Item>
<Item key="5">
<a href="https://www.taobao.com/" target="__blank">Option Link</a>
</Item>
</Menu>
, mountNode);
.my-menu {
width: 200px;
}
通过设置 openMode 为 'single',可以让菜单同时只能展开一个内连子菜单,默认为可以同时展开多个。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item } = Menu;
ReactDOM.render(
<Menu defaultOpenKeys="1" className="my-menu" openMode="single">
<SubMenu key="0" label="Sub menu 1">
<Item key="0-0">Sub option 1</Item>
<Item key="0-1">Sub option 2</Item>
<Item key="0-2">Sub option 3</Item>
</SubMenu>
<SubMenu key="1" label="Sub menu 2">
<Item key="1-0">Sub option 1</Item>
<Item key="1-1">Sub option 2</Item>
<Item key="1-2">Sub option 3</Item>
</SubMenu>
<SubMenu key="2" label="Sub menu 3">
<Item key="2-0">Sub option 1</Item>
<Item key="2-1">Sub option 2</Item>
<Item key="2-2">Sub option 3</Item>
</SubMenu>
<SubMenu key="3" label="Sub menu 4">
<Item key="3-0">Sub option 1</Item>
<Item key="3-1">Sub option 2</Item>
<Item key="3-2">Sub option 3</Item>
</SubMenu>
</Menu>
, mountNode);
.my-menu {
width: 200px;
}
展示弹出菜单的用法。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item, Divider } = Menu;
ReactDOM.render(
<Menu className="my-menu" mode="popup">
<Item key="1">Option 1</Item>
<Item key="2">Option 2</Item>
<Item key="3">Option 3</Item>
<Divider key="divider" />
<SubMenu key="sub-1" label="Popup menu 1">
<Item key="popup-1-1">Popup option 1</Item>
<Item key="popup-1-2">Popup option 2</Item>
</SubMenu>
<SubMenu key="sub-2" label="Popup menu 2">
<Item key="popup-2-1">Popup option 1</Item>
<Item key="popup-2-2">Popup option 2</Item>
</SubMenu>
</Menu>
, mountNode);
.my-menu {
width: 200px;
}
可以设置 triggerType 为 'hover',来 hover 打开子菜单,默认点击打开子菜单。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item, Divider } = Menu;
ReactDOM.render(
<Menu className="my-menu" mode="popup" triggerType="hover">
<Item key="1">Option 1</Item>
<Item key="2">Option 2</Item>
<Item key="3">Option 3</Item>
<Divider key="divider" />
<SubMenu key="sub-1" label="Popup menu 1">
<Item key="popup-1-1">Popup option 1</Item>
<Item key="popup-1-2">Popup option 2</Item>
</SubMenu>
<SubMenu key="sub-2" label="Popup menu 2">
<Item key="popup-2-1">Popup option 1</Item>
<Item key="popup-2-2">Popup option 2</Item>
</SubMenu>
</Menu>
, mountNode);
.my-menu {
width: 200px;
}
可以通过设置 popupAlign 为 'outside',使弹出菜单和父级菜单对齐。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item, Divider } = Menu;
ReactDOM.render(
<Menu className="my-menu" mode="popup" popupAlign="outside">
<Item key="1">Option 1</Item>
<Item key="2">Option 2</Item>
<Item key="3">Option 3</Item>
<Divider key="divider" />
<SubMenu key="sub-1" label="Popup menu 1">
<Item key="popup-1-1">Popup option 1</Item>
<Item key="popup-1-2">Popup option 2</Item>
</SubMenu>
<SubMenu key="sub-2" label="Popup menu 2">
<Item key="popup-2-1">Popup option 1</Item>
<Item key="popup-2-2">Popup option 2</Item>
</SubMenu>
</Menu>
, mountNode);
.my-menu {
width: 200px;
}
自定义菜单弹出内容。
查看源码在线预览
import { Menu } from '@alifd/next';
const { PopupItem } = Menu;
class Demo extends React.Component {
render() {
const popupProps = {
target: () => ReactDOM.findDOMNode(this),
offset: [-1, 0],
animation: false
};
return (
<Menu className="my-custom-menu" popupProps={popupProps}>
<PopupItem key="0" label="Popup item 1">
<div className="my-custom-content">Custom content 1</div>
</PopupItem>
<PopupItem key="1" label="Popup item 2">
<div className="my-custom-content">Custom content 2</div>
</PopupItem>
<PopupItem key="2" label="Popup item 3">
<div className="my-custom-content">Custom content 3</div>
</PopupItem>
<PopupItem key="3" label="Popup item 4">
<div className="my-custom-content">Custom content 4</div>
</PopupItem>
</Menu>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.my-custom-menu {
width: 200px;
border: 1px solid #ccc;
padding: 0;
box-shadow: none;
z-index: 1000;
}
.my-custom-content {
width: 400px;
height: 200px;
background: #fff;
border: 1px solid #ccc;
line-height: 200px;
text-align: center;
font-size: 20px;
}
展示菜单项选择用法。
查看源码在线预览
import { Switch, Menu } from '@alifd/next';
const { SubMenu, Item } = Menu;
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
multiple: false,
subMenuSelectable: false,
shallowSelect: false,
isSelectIconRight: false,
selectedKeys: ['1']
};
[
'handleMultipleChange', 'handleSubMenuSelectableChange',
'handleShallowSelectChange', 'handleSelect', 'handleIconDirectionChange'
].forEach(method => {
this[method] = this[method].bind(this);
});
}
handleMultipleChange() {
this.setState({
multiple: !this.state.multiple,
selectedKeys: []
});
}
handleIconDirectionChange() {
this.setState({
isSelectIconRight: !this.state.isSelectIconRight,
selectedKeys: []
});
}
handleSubMenuSelectableChange() {
this.setState({
subMenuSelectable: !this.state.subMenuSelectable,
selectedKeys: []
});
}
handleShallowSelectChange() {
this.setState({
shallowSelect: !this.state.shallowSelect,
selectedKeys: []
});
}
handleSelect(selectedKeys, ...others) {
this.setState({
selectedKeys
});
console.log(selectedKeys, ...others);
}
render() {
const { multiple, subMenuSelectable, shallowSelect, selectedKeys, isSelectIconRight } = this.state;
const selectMode = multiple ? 'multiple' : 'single';
return (
<div>
<div>
<span className="my-switch-label">Multiple </span>
<Switch value={multiple} onChange={this.handleMultipleChange} />
</div>
<div>
<span className="my-switch-label">isSelectIconRight </span>
<Switch value={multiple} onChange={this.handleIconDirectionChange} />
</div>
<div>
<span className="my-switch-label">Label of submenu selectable </span>
<Switch value={multiple} onChange={this.handleSubMenuSelectableChange} />
</div>
<div>
<span className="my-switch-label">Only first level selectable </span>
<Switch value={multiple} onChange={this.handleShallowSelectChange} />
</div>
<Menu isSelectIconRight={isSelectIconRight} className="my-select-menu" defaultOpenKeys={['sub']} selectMode={selectMode} selectedKeys={selectedKeys} shallowSelect={shallowSelect} onSelect={this.handleSelect}>
<Item key="1">Option 1</Item>
<Item disabled key="2">Disabled option 2</Item>
<SubMenu key="sub" label="Sub menu" selectable={subMenuSelectable}>
<Item key="sub-1">Sub option 1</Item>
<Item key="sub-2">Sub option 2</Item>
</SubMenu>
<Item key="3">Option 3</Item>
</Menu>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.my-switch-label {
vertical-align: super;
}
.my-select-menu {
margin-top: 10px;
width: 200px;
}
展示自定义组合菜单项可选的用法。
查看源码在线预览
import { Menu } from '@alifd/next';
const { CheckboxItem, RadioItem, Divider } = Menu;
const sexs = ['male', 'female'];
const balls = ['football', 'basketball', 'volleyball'];
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
sex: 'male',
balls: []
};
this.handleSexCheck = this.handleSexCheck.bind(this);
this.handleBallCheck = this.handleBallCheck.bind(this);
}
handleSexCheck(key) {
this.setState({
sex: key
});
}
handleBallCheck(key, check) {
let newKeys;
const index = this.state.balls.indexOf(key);
if (check && index === -1) {
newKeys = this.state.balls.concat(key);
} else if (!check && index > -1) {
newKeys = [
...this.state.balls.slice(0, index),
...this.state.balls.slice(index + 1)
];
}
if (newKeys) {
this.setState({
balls: newKeys
});
}
}
render() {
return (
<Menu className="my-menu">
{sexs.map(sex => (
<RadioItem key={sex} checked={this.state.sex === sex} onChange={this.handleSexCheck.bind(this, sex)}>
{sex}
</RadioItem>
))}
<Divider key="divider" />
{balls.map(ball => (
<CheckboxItem key={ball} checked={this.state.balls.indexOf(ball) > -1} onChange={this.handleBallCheck.bind(this, ball)}>
{ball}
</CheckboxItem>
))}
</Menu>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.my-menu {
width: 200px;
}
展示横向导航菜单条的用法。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item } = Menu;
ReactDOM.render(
<Menu hozInLine direction="hoz" mode="popup" className="my-hoz-menu" popupClassName="my-hoz-menu" popupAutoWidth>
<Item key="1">First</Item>
<Item key="2">Second</Item>
<SubMenu label="Sub Nav">
<Item key="sub-12">Sub option 1</Item>
<Item key="sub-22">Sub option 2</Item>
<SubMenu label="Sub Sub Nav">
<Item key="sub-sub-122">Sub sub option 1</Item>
<Item key="sub-sub-222">Sub sub option 2</Item>
</SubMenu>
</SubMenu>
<SubMenu label="Sub Nav">
<Item key="sub-1">Sub option 1</Item>
<Item key="sub-2">Sub option 2</Item>
<SubMenu label="Sub Sub Nav">
<Item key="sub-sub-1">Sub sub option 1</Item>
<Item key="sub-sub-2">Sub sub option 2</Item>
</SubMenu>
</SubMenu>
<Item key="3">Third</Item>
</Menu>
, mountNode);
.my-hoz-menu .next-menu-item {
width: 160px;
}
.my-hoz-menu .next-menu-item.next-menu-more {
width: 40px;
}
展示如何创建自定义的上下文菜单。
查看源码在线预览
import { Menu } from '@alifd/next';
const { SubMenu, Item, Divider } = Menu;
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedKeys: []
};
this.handleSelect = this.handleSelect.bind(this);
}
handleSelect(selectedKeys) {
selectedKeys = selectedKeys.filter(key => {
return ['sub-1', 'sub-2'].indexOf(key) > -1;
});
this.setState({
selectedKeys
});
}
createContextMenu = e => {
e.preventDefault();
const target = e.target;
const { top, left } = target.getBoundingClientRect();
Menu.create({
target: e.target,
offset: [e.clientX - left, e.clientY - top],
className: 'context-menu',
popupClassName: 'context-menu',
onItemClick: console.log,
selectedKeys: this.state.selectedKeys,
selectMode: 'multiple',
onSelect: this.handleSelect,
children: [
<Item key="1">Option 1</Item>,
<Item key="2">Option 2</Item>,
<Item key="3">Option 3</Item>,
<Divider key="divider-1" />,
<SubMenu key="sub-menu" label="Sub menu">
<Item key="sub-1">Sub option 1</Item>
<Item key="sub-2">Sub option 2</Item>
</SubMenu>,
<Item key="4">Option 4</Item>,
<Divider key="divider-2" />,
<Item key="5">Option 5</Item>
]
});
};
render() {
return (
<div className="context-demo" onContextMenu={this.createContextMenu}>
Right click here to see the context menu!
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.context-demo {
width: 500px;
height: 200px;
line-height: 200px;
text-align: center;
background: #DDD;
border: 1px solid black;
}
.context-menu {
width: 120px;
}