组件

菜单栏

桌面应用程序中常见的视觉上持久存在的菜单,提供对一致命令集的快速访问。

import React from 'react';
import * as Menubar from '@radix-ui/react-menubar';
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from '@radix-ui/react-icons';
import './styles.css';
const RADIO_ITEMS = ['Andy', 'Benoît', 'Luis'];
const CHECK_ITEMS = ['Always Show Bookmarks Bar', 'Always Show Full URLs'];
const MenubarDemo = () => {
const [checkedSelection, setCheckedSelection] = React.useState([CHECK_ITEMS[1]]);
const [radioSelection, setRadioSelection] = React.useState(RADIO_ITEMS[2]);
return (
<Menubar.Root className="MenubarRoot">
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">File</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" align="start" sideOffset={5} alignOffset={-3}>
<Menubar.Item className="MenubarItem">
New Tab <div className="RightSlot">⌘ T</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem">
New Window <div className="RightSlot">⌘ N</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem" disabled>
New Incognito Window
</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Sub>
<Menubar.SubTrigger className="MenubarSubTrigger">
Share
<div className="RightSlot">
<ChevronRightIcon />
</div>
</Menubar.SubTrigger>
<Menubar.Portal>
<Menubar.SubContent className="MenubarSubContent" alignOffset={-5}>
<Menubar.Item className="MenubarItem">Email Link</Menubar.Item>
<Menubar.Item className="MenubarItem">Messages</Menubar.Item>
<Menubar.Item className="MenubarItem">Notes</Menubar.Item>
</Menubar.SubContent>
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem">
Print… <div className="RightSlot">⌘ P</div>
</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">Edit</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" align="start" sideOffset={5} alignOffset={-3}>
<Menubar.Item className="MenubarItem">
Undo <div className="RightSlot">⌘ Z</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem">
Redo <div className="RightSlot">⇧ ⌘ Z</div>
</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Sub>
<Menubar.SubTrigger className="MenubarSubTrigger">
Find
<div className="RightSlot">
<ChevronRightIcon />
</div>
</Menubar.SubTrigger>
<Menubar.Portal>
<Menubar.SubContent className="MenubarSubContent" alignOffset={-5}>
<Menubar.Item className="MenubarItem">Search the web…</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem">Find…</Menubar.Item>
<Menubar.Item className="MenubarItem">Find Next</Menubar.Item>
<Menubar.Item className="MenubarItem">Find Previous</Menubar.Item>
</Menubar.SubContent>
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem">Cut</Menubar.Item>
<Menubar.Item className="MenubarItem">Copy</Menubar.Item>
<Menubar.Item className="MenubarItem">Paste</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">View</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" align="start" sideOffset={5} alignOffset={-14} >
{CHECK_ITEMS.map((item) => (
<Menubar.CheckboxItem className="MenubarCheckboxItem inset" key={item} checked={checkedSelection.includes(item)} onCheckedChange={() => setCheckedSelection((current) => current.includes(item) ? current.filter((el) => el !== item) : current.concat(item) ) } >
<Menubar.ItemIndicator className="MenubarItemIndicator">
<CheckIcon />
</Menubar.ItemIndicator>
{item}
</Menubar.CheckboxItem>
))}
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">
Reload <div className="RightSlot">⌘ R</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem inset" disabled>
Force Reload <div className="RightSlot">⇧ ⌘ R</div>
</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Toggle Fullscreen</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Hide Sidebar</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">Profiles</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" align="start" sideOffset={5} alignOffset={-14} >
<Menubar.RadioGroup value={radioSelection} onValueChange={setRadioSelection}>
{RADIO_ITEMS.map((item) => (
<Menubar.RadioItem className="MenubarRadioItem inset" key={item} value={item}>
<Menubar.ItemIndicator className="MenubarItemIndicator">
<DotFilledIcon />
</Menubar.ItemIndicator>
{item}
</Menubar.RadioItem>
))}
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Edit…</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Add Profile…</Menubar.Item>
</Menubar.RadioGroup>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
};
export default MenubarDemo;

功能

    可以是受控的或不受控的。

    支持具有可配置阅读方向的子菜单。

    支持项目、标签和项目组。

    支持可选中项目(单个或多个)。

    自定义侧边、对齐方式、偏移量和碰撞处理。

    可以选择渲染指向箭头。

    焦点得到完全管理。

    完整的键盘导航。

    支持类型提示。

安装

从命令行安装组件。

npm install @radix-ui/react-menubar

结构

导入所有部分并将其组合在一起。

import * as Menubar from '@radix-ui/react-menubar';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger />
<Menubar.Portal>
<Menubar.Content>
<Menubar.Label />
<Menubar.Item />
<Menubar.Group>
<Menubar.Item />
</Menubar.Group>
<Menubar.CheckboxItem>
<Menubar.ItemIndicator />
</Menubar.CheckboxItem>
<Menubar.RadioGroup>
<Menubar.RadioItem>
<Menubar.ItemIndicator />
</Menubar.RadioItem>
</Menubar.RadioGroup>
<Menubar.Sub>
<Menubar.SubTrigger />
<Menubar.Portal>
<Menubar.SubContent />
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator />
<Menubar.Arrow />
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);

API 参考

包含菜单栏的所有部分。

属性类型默认值
asChild
布尔值
false
defaultValue
字符串
无默认值
value
字符串
无默认值
onValueChange
函数
无默认值
dir
枚举
无默认值
loop
布尔值
false

顶级菜单项,包含触发器和内容组合。

属性类型默认值
asChild
布尔值
false
value
字符串
无默认值

触发器

切换内容的按钮。默认情况下,Menubar.Content 将自身定位在触发器旁边。

属性类型默认值
asChild
布尔值
false
数据属性
[data-state]"open" |"closed"
[data-highlighted]

突出显示时出现

[data-disabled]

禁用时出现

传送门

使用时,将内容部分传送至 body

属性类型默认值
forceMount
布尔值
无默认值
container
HTMLElement
document.body

内容

菜单打开时弹出的组件。

属性类型默认值
asChild
布尔值
false
loop
布尔值
false
onCloseAutoFocus
函数
无默认值
onEscapeKeyDown
函数
无默认值
onPointerDownOutside
函数
无默认值
onFocusOutside
函数
无默认值
onInteractOutside
函数
无默认值
forceMount
布尔值
无默认值
side
枚举
"bottom"
sideOffset
数字
0
align
枚举
"center"
alignOffset
数字
0
avoidCollisions
布尔值
true
collisionBoundary
边界
[]
collisionPadding
数字 | 填充
0
arrowPadding
数字
0
sticky
枚举
"partial"
hideWhenDetached
布尔值
false
数据属性
[data-state]"open" |"closed"
[data-side]"left" |"right" |"bottom" |"top"
[data-align]"start" |"end" |"center"
CSS 变量描述
--radix-menubar-content-transform-origin从内容和箭头位置/偏移量计算出的 transform-origin
--radix-menubar-content-available-width触发器和边界边缘之间的剩余宽度
--radix-menubar-content-available-height触发器和边界边缘之间的剩余高度
--radix-menubar-trigger-width触发器的宽度
--radix-menubar-trigger-height触发器的高度

箭头

一个可选的箭头元素,用于与菜单栏菜单一起渲染。这可以用来帮助视觉上将触发器与 Menubar.Content 关联起来。必须在 Menubar.Content 内部渲染。

属性类型默认值
asChild
布尔值
false
width
数字
10
height
数字
5

项目

包含菜单栏项目的组件。

属性类型默认值
asChild
布尔值
false
disabled
布尔值
无默认值
onSelect
函数
无默认值
textValue

字符串
无默认值
数据属性
[data-highlighted]

突出显示时出现

[data-disabled]

禁用时出现

分组

用于对多个 Menubar.Item 进行分组。

属性类型默认值
asChild
布尔值
false

标签

用于渲染标签。它无法使用方向键获得焦点。

属性类型默认值
asChild
布尔值
false

复选框项

可以像复选框一样控制和渲染的项目。

属性类型默认值
asChild
布尔值
false
选中状态
布尔值 | 'indeterminate'
无默认值
选中状态更改事件
函数
无默认值
disabled
布尔值
无默认值
onSelect
函数
无默认值
textValue

字符串
无默认值
数据属性
[data-state]"checked" |"unchecked"
[data-highlighted]

突出显示时出现

[data-disabled]

禁用时出现

单选组

用于对多个 Menubar.RadioItem 进行分组。

属性类型默认值
asChild
布尔值
false
value
字符串
无默认值
onValueChange
函数
无默认值

单选项

可以像单选按钮一样控制和渲染的项目。

属性类型默认值
asChild
布尔值
false
value*
字符串
无默认值
disabled
布尔值
无默认值
onSelect
函数
无默认值
textValue

字符串
无默认值
数据属性
[data-state]"checked" |"unchecked"
[data-highlighted]

突出显示时出现

[data-disabled]

禁用时出现

项目指示器

当父级 Menubar.CheckboxItemMenubar.RadioItem 被选中时渲染。您可以直接设置此元素的样式,或者将其用作包装器以放入图标,或者两者兼而有之。

属性类型默认值
asChild
布尔值
false
forceMount
布尔值
无默认值
数据属性
[data-state]"checked" |"unchecked"

分隔符

用于在菜单栏菜单中视觉上分隔项目。

属性类型默认值
asChild
布尔值
false

子菜单

包含子菜单的所有部分。

属性类型默认值
默认打开状态
布尔值
无默认值
打开状态
布尔值
无默认值
打开状态更改事件
函数
无默认值

子菜单触发器

打开子菜单的项目。必须渲染在 Menubar.Sub 内部。

属性类型默认值
asChild
布尔值
false
disabled
布尔值
无默认值
textValue

字符串
无默认值
数据属性
[data-state]"open" |"closed"
[data-highlighted]

突出显示时出现

[data-disabled]

禁用时出现

子菜单内容

子菜单打开时弹出的组件。必须渲染在 Menubar.Sub 内部。

属性类型默认值
asChild
布尔值
false
loop
布尔值
false
onEscapeKeyDown
函数
无默认值
onPointerDownOutside
函数
无默认值
onFocusOutside
函数
无默认值
onInteractOutside
函数
无默认值
forceMount
布尔值
无默认值
sideOffset
数字
0
alignOffset
数字
0
avoidCollisions
布尔值
true
collisionBoundary
边界
[]
collisionPadding
数字 | 填充
0
arrowPadding
数字
0
sticky
枚举
"partial"
hideWhenDetached
布尔值
false
数据属性
[data-state]"open" |"closed"
[data-side]"left" |"right" |"bottom" |"top"
[data-align]"start" |"end" |"center"
[方向]"垂直" |"水平"
CSS 变量描述
--radix-menubar-content-transform-origin从内容和箭头位置/偏移量计算出的 transform-origin
--radix-menubar-content-available-width触发器和边界边缘之间的剩余宽度
--radix-menubar-content-available-height触发器和边界边缘之间的剩余高度
--radix-menubar-trigger-width触发器的宽度
--radix-menubar-trigger-height触发器的高度

示例

带有子菜单

您可以通过结合使用 Menubar.Sub 及其各个部分来创建子菜单。

<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item></Menubar.Item>
<Menubar.Item></Menubar.Item>
<Menubar.Separator />
<Menubar.Sub>
<Menubar.SubTrigger>Sub menu →</Menubar.SubTrigger>
<Menubar.Portal>
<Menubar.SubContent>
<Menubar.Item>Sub menu item</Menubar.Item>
<Menubar.Item>Sub menu item</Menubar.Item>
<Menubar.Arrow />
</Menubar.SubContent>
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator />
<Menubar.Item></Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>

带有禁用项目

您可以通过 data-disabled 属性为禁用项目添加特殊样式。

// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item className="MenubarItem" disabled>
</Menubar.Item>
<Menubar.Item className="MenubarItem"></Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
/* styles.css */
.MenubarItem[data-disabled] {
color: gainsboro;
}

带有分隔符

使用 Separator 部分在项目之间添加分隔符。

<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item></Menubar.Item>
<Menubar.Separator />
<Menubar.Item></Menubar.Item>
<Menubar.Separator />
<Menubar.Item></Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>

带有标签

使用 Label 部分帮助标记某个区域。

<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Label>Label</Menubar.Label>
<Menubar.Item></Menubar.Item>
<Menubar.Item></Menubar.Item>
<Menubar.Item></Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>

带有复选框项目

使用 CheckboxItem 部分添加可以选中的项目。

import React from 'react';
import { CheckIcon } from '@radix-ui/react-icons';
import * as Menubar from '@radix-ui/react-menubar';
export default () => {
const [checked, setChecked] = React.useState(true);
return (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item></Menubar.Item>
<Menubar.Item></Menubar.Item>
<Menubar.Separator />
<Menubar.CheckboxItem checked={checked} onCheckedChange={setChecked} >
<Menubar.ItemIndicator>
<CheckIcon />
</Menubar.ItemIndicator>
Checkbox item
</Menubar.CheckboxItem>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
};

带有单选项目

使用 RadioGroupRadioItem 部分添加可以在其他项目中选中的项目。

import React from 'react';
import { CheckIcon } from '@radix-ui/react-icons';
import * as Menubar from '@radix-ui/react-menubar';
export default () => {
const [color, setColor] = React.useState('blue');
return (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.RadioGroup value={color} onValueChange={setColor}>
<Menubar.RadioItem value="red">
<Menubar.ItemIndicator>
<CheckIcon />
</Menubar.ItemIndicator>
Red
</Menubar.RadioItem>
<Menubar.RadioItem value="blue">
<Menubar.ItemIndicator>
<CheckIcon />
</Menubar.ItemIndicator>
Blue
</Menubar.RadioItem>
</Menubar.RadioGroup>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
};

带有复杂项目

您可以在 Item 部分添加额外的装饰元素,例如图像。

import * as Menubar from '@radix-ui/react-menubar';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>
<img src="" />
Adolfo Hess
</Menubar.Item>
<Menubar.Item>
<img src="" />
Miyah Myles
</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);

约束内容/子内容大小

您可能希望约束内容(或子内容)的宽度,使其与触发器(或子触发器)的宽度匹配。您可能还想约束其高度,使其不超过视口。

我们公开了几个 CSS 自定义属性,例如 --radix-menubar-trigger-width--radix-menubar-content-available-height 来支持此功能。使用它们来约束内容尺寸。

// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" sideOffset={5}>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Root>
);
/* styles.css */
.MenubarContent {
width: var(--radix-menubar-trigger-width);
max-height: var(--radix-menubar-content-available-height);
}

基于原点的动画

我们公开了 CSS 自定义属性 --radix-menubar-content-transform-origin。使用它根据 sidesideOffsetalignalignOffset 和任何冲突,从其计算出的原点开始为内容设置动画。

// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent"></Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
/* styles.css */
.MenubarContent {
transform-origin: var(--radix-menubar-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}

基于冲突的动画

我们公开了 data-sidedata-align 属性。它们的值将在运行时更改以反映冲突。使用它们创建基于冲突和方向的动画。

// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger></Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent"></Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
/* styles.css */
.MenubarContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.MenubarContent[data-side='top'] {
animation-name: slideUp;
}
.MenubarContent[data-side='bottom'] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

无障碍性

遵循 菜单按钮 WAI-ARIA 设计模式 并使用 循环 Tab 键索引 来管理菜单项之间的焦点移动。

键盘交互

描述
空格
当焦点位于 Menubar.Trigger 上时,打开菜单栏并聚焦第一个项目。
当焦点位于某个项目上时,激活聚焦的项目。
回车
当焦点位于 Menubar.Trigger 上时,打开关联的菜单。
当焦点位于某个项目上时,激活聚焦的项目。
向下箭头
当焦点位于 Menubar.Trigger 上时,打开关联的菜单。
当焦点位于某个项目上时,将焦点移动到下一个项目。
向上箭头
当焦点位于某个项目上时,将焦点移动到上一个项目。
向右箭头向左箭头
当焦点位于 Menubar.Trigger 上时,将焦点移动到下一个或上一个项目。
当焦点位于 Menubar.SubTrigger 上时,根据阅读方向打开或关闭子菜单。
当焦点位于 Menubar.Content 内时,打开菜单栏中的下一个菜单。
Esc
关闭当前打开的菜单并将焦点移动到其 Menubar.Trigger.
上一个标签
下一个导航菜单