组件

上下文菜单

显示一个位于指针位置的菜单,通过右键单击或长按触发。

import * as React from "react";
import { ContextMenu } from "radix-ui";
import { DotFilledIcon, CheckIcon, ChevronRightIcon, } from "@radix-ui/react-icons";
import "./styles.css";
const ContextMenuDemo = () => {
const [bookmarksChecked, setBookmarksChecked] = React.useState(true);
const [urlsChecked, setUrlsChecked] = React.useState(false);
const [person, setPerson] = React.useState("pedro");
return (
<ContextMenu.Root>
<ContextMenu.Trigger className="ContextMenuTrigger">
Right-click here.
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent" sideOffset={5} align="end" >
<ContextMenu.Item className="ContextMenuItem">
Back <div className="RightSlot">⌘+[</div>
</ContextMenu.Item>
<ContextMenu.Item className="ContextMenuItem" disabled>
Forward <div className="RightSlot">⌘+]</div>
</ContextMenu.Item>
<ContextMenu.Item className="ContextMenuItem">
Reload <div className="RightSlot">⌘+R</div>
</ContextMenu.Item>
<ContextMenu.Sub>
<ContextMenu.SubTrigger className="ContextMenuSubTrigger">
More Tools
<div className="RightSlot">
<ChevronRightIcon />
</div>
</ContextMenu.SubTrigger>
<ContextMenu.Portal>
<ContextMenu.SubContent className="ContextMenuSubContent" sideOffset={2} alignOffset={-5} >
<ContextMenu.Item className="ContextMenuItem">
Save Page As… <div className="RightSlot">⌘+S</div>
</ContextMenu.Item>
<ContextMenu.Item className="ContextMenuItem">
Create Shortcut…
</ContextMenu.Item>
<ContextMenu.Item className="ContextMenuItem">
Name Window…
</ContextMenu.Item>
<ContextMenu.Separator className="ContextMenuSeparator" />
<ContextMenu.Item className="ContextMenuItem">
Developer Tools
</ContextMenu.Item>
</ContextMenu.SubContent>
</ContextMenu.Portal>
</ContextMenu.Sub>
<ContextMenu.Separator className="ContextMenuSeparator" />
<ContextMenu.CheckboxItem className="ContextMenuCheckboxItem" checked={bookmarksChecked} onCheckedChange={setBookmarksChecked} >
<ContextMenu.ItemIndicator className="ContextMenuItemIndicator">
<CheckIcon />
</ContextMenu.ItemIndicator>
Show Bookmarks <div className="RightSlot">⌘+B</div>
</ContextMenu.CheckboxItem>
<ContextMenu.CheckboxItem className="ContextMenuCheckboxItem" checked={urlsChecked} onCheckedChange={setUrlsChecked} >
<ContextMenu.ItemIndicator className="ContextMenuItemIndicator">
<CheckIcon />
</ContextMenu.ItemIndicator>
Show Full URLs
</ContextMenu.CheckboxItem>
<ContextMenu.Separator className="ContextMenuSeparator" />
<ContextMenu.Label className="ContextMenuLabel">
People
</ContextMenu.Label>
<ContextMenu.RadioGroup value={person} onValueChange={setPerson}>
<ContextMenu.RadioItem className="ContextMenuRadioItem" value="pedro" >
<ContextMenu.ItemIndicator className="ContextMenuItemIndicator">
<DotFilledIcon />
</ContextMenu.ItemIndicator>
Pedro Duarte
</ContextMenu.RadioItem>
<ContextMenu.RadioItem className="ContextMenuRadioItem" value="colm" >
<ContextMenu.ItemIndicator className="ContextMenuItemIndicator">
<DotFilledIcon />
</ContextMenu.ItemIndicator>
Colm Tuite
</ContextMenu.RadioItem>
</ContextMenu.RadioGroup>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
};
export default ContextMenuDemo;

特性

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

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

    支持可勾选的项目(单选或多选),并可选择不确定状态。

    支持模态和非模态模式。

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

    焦点完全受控。

    完整的键盘导航。

    Typeahead 支持。

    关闭和分层行为高度可定制。

    在触摸设备上通过长按触发

安装

从命令行安装组件。

npm install @radix-ui/react-context-menu

解剖

导入所有部件并将它们组装在一起。

import { ContextMenu } from "radix-ui";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger />
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Label />
<ContextMenu.Item />
<ContextMenu.Group>
<ContextMenu.Item />
</ContextMenu.Group>
<ContextMenu.CheckboxItem>
<ContextMenu.ItemIndicator />
</ContextMenu.CheckboxItem>
<ContextMenu.RadioGroup>
<ContextMenu.RadioItem>
<ContextMenu.ItemIndicator />
</ContextMenu.RadioItem>
</ContextMenu.RadioGroup>
<ContextMenu.Sub>
<ContextMenu.SubTrigger />
<ContextMenu.Portal>
<ContextMenu.SubContent />
</ContextMenu.Portal>
</ContextMenu.Sub>
<ContextMenu.Separator />
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);

API 参考

遵循 Menu WAI-ARIA 设计模式 并使用 roving tabindex 来管理菜单项之间的焦点移动。

Root(根元素)

包含上下文菜单的所有部件。

Prop(属性)Type(类型)Default(默认值)
dir
enum(枚举)
无默认值
onOpenChange
function(函数)
无默认值
modal(模态)
boolean(布尔值)
true

Trigger(触发器)

打开上下文菜单的区域。将其包裹在你希望右键单击(或使用相关的键盘快捷键)时打开上下文菜单的目标元素周围。

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
disabled(禁用)
boolean(布尔值)
false
Data attribute(数据属性)Values(值)
[data-state]"open" |"closed"

Portal(传送门)

使用时,将内容部分传送到 body 中。

Prop(属性)Type(类型)Default(默认值)
forceMount
boolean(布尔值)
无默认值
container(容器)
HTMLElement
document.body

Content(内容)

在打开的上下文菜单中弹出的组件。

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
loop(循环)
boolean(布尔值)
false
onCloseAutoFocus
function(函数)
无默认值
onEscapeKeyDown
function(函数)
无默认值
onPointerDownOutside
function(函数)
无默认值
onFocusOutside
function(函数)
无默认值
onInteractOutside
function(函数)
无默认值
forceMount
boolean(布尔值)
无默认值
alignOffset
number(数字)
0
avoidCollisions(避免碰撞)
boolean(布尔值)
true
collisionBoundary(碰撞边界)
Boundary(边界)
[]
collisionPadding(碰撞内边距)
number | Padding(数字 | 内边距)
0
sticky(粘性)
enum(枚举)
"partial"
hideWhenDetached(分离时隐藏)
boolean(布尔值)
false
Data attribute(数据属性)Values(值)
[data-state]"open" |"closed"
[data-side]"left" |"right" |"bottom" |"top"
[data-align]"start" |"end" |"center"
CSS Variable(CSS 变量)Description(描述)
--radix-context-menu-content-transform-origin从内容和箭头位置/偏移量计算出的 transform-origin
--radix-context-menu-content-available-width触发器和边界边缘之间剩余的宽度
--radix-context-menu-content-available-height触发器和边界边缘之间剩余的高度
--radix-context-menu-trigger-width触发器的宽度
--radix-context-menu-trigger-height触发器的高度

Arrow(箭头)

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

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
width(宽度)
number(数字)
10
height(高度)
number(数字)
5

Item(项目)

包含上下文菜单项的组件。

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
disabled(禁用)
boolean(布尔值)
无默认值
onSelect
function(函数)
无默认值
textValue
string(字符串)
无默认值
Data attribute(数据属性)Values(值)
[data-highlighted]

高亮显示时呈现

[data-disabled]

禁用时呈现

Group(组)

用于分组多个 ContextMenu.Item

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false

Label(标签)

用于渲染标签。它不会使用箭头键获得焦点。

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false

CheckboxItem(复选框项目)

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

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
checked(已选中)
boolean | 'indeterminate'(布尔值 | '不确定')
无默认值
onCheckedChange
function(函数)
无默认值
disabled(禁用)
boolean(布尔值)
无默认值
onSelect
function(函数)
无默认值
textValue
string(字符串)
无默认值
Data attribute(数据属性)Values(值)
[data-state]"checked" |"unchecked" |"indeterminate"
[data-highlighted]

高亮显示时呈现

[data-disabled]

禁用时呈现

RadioGroup(单选按钮组)

用于分组多个 ContextMenu.RadioItem

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
value(值)
string(字符串)
无默认值
onValueChange
function(函数)
无默认值

RadioItem(单选按钮项目)

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

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
value(值)*
string(字符串)
无默认值
disabled(禁用)
boolean(布尔值)
无默认值
onSelect
function(函数)
无默认值
textValue
string(字符串)
无默认值
Data attribute(数据属性)Values(值)
[data-state]"checked" |"unchecked" |"indeterminate"
[data-highlighted]

高亮显示时呈现

[data-disabled]

禁用时呈现

ItemIndicator(项目指示器)

当父级 ContextMenu.CheckboxItemContextMenu.RadioItem 被选中时渲染。你可以直接设置此元素的样式,或者你可以将其用作包装器来放入图标,或者两者都做。

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
forceMount
boolean(布尔值)
无默认值
Data attribute(数据属性)Values(值)
[data-state]"checked" |"unchecked" |"indeterminate"

Separator(分隔符)

用于在上下文菜单中可视化地分隔项目。

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false

Sub(子菜单)

包含子菜单的所有部件。

Prop(属性)Type(类型)Default(默认值)
defaultOpen
boolean(布尔值)
无默认值
open
boolean(布尔值)
无默认值
onOpenChange
function(函数)
无默认值

SubTrigger(子菜单触发器)

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

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
disabled(禁用)
boolean(布尔值)
无默认值
textValue
string(字符串)
无默认值
Data attribute(数据属性)Values(值)
[data-state]"open" |"closed"
[data-highlighted]

高亮显示时呈现

[data-disabled]

禁用时呈现

SubContent(子菜单内容)

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

Prop(属性)Type(类型)Default(默认值)
asChild
boolean(布尔值)
false
loop(循环)
boolean(布尔值)
false
onEscapeKeyDown
function(函数)
无默认值
onPointerDownOutside
function(函数)
无默认值
onFocusOutside
function(函数)
无默认值
onInteractOutside
function(函数)
无默认值
forceMount
boolean(布尔值)
无默认值
sideOffset
number(数字)
0
alignOffset
number(数字)
0
avoidCollisions(避免碰撞)
boolean(布尔值)
true
collisionBoundary(碰撞边界)
Boundary(边界)
[]
collisionPadding(碰撞内边距)
number | Padding(数字 | 内边距)
0
arrowPadding
number(数字)
0
sticky(粘性)
enum(枚举)
"partial"
hideWhenDetached(分离时隐藏)
boolean(布尔值)
false
Data attribute(数据属性)Values(值)
[data-state]"open" |"closed"
[data-side]"left" |"right" |"bottom" |"top"
[data-align]"start" |"end" |"center"
CSS Variable(CSS 变量)Description(描述)
--radix-context-menu-content-transform-origin从内容和箭头位置/偏移量计算出的 transform-origin
--radix-context-menu-content-available-width触发器和边界边缘之间剩余的宽度
--radix-context-menu-content-available-height触发器和边界边缘之间剩余的高度
--radix-context-menu-trigger-width触发器的宽度
--radix-context-menu-trigger-height触发器的高度

示例

带有子菜单

你可以通过结合使用 ContextMenu.Sub 及其部件来创建子菜单。

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

带有禁用项目

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

// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item className="ContextMenuItem" disabled>
</ContextMenu.Item>
<ContextMenu.Item className="ContextMenuItem"></ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuItem[data-disabled] {
color: gainsboro;
}

带有分隔符

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

<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item></ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item></ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item></ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>

带有标签

使用 Label 部件来帮助标记一个部分。

<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Label>Label</ContextMenu.Label>
<ContextMenu.Item></ContextMenu.Item>
<ContextMenu.Item></ContextMenu.Item>
<ContextMenu.Item></ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>

带有复选框项目

使用 CheckboxItem 部件来添加一个可以被选中的项目。

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

带有单选按钮项目

使用 RadioGroupRadioItem 部件来添加一个可以在其他项中被选中的项。

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

带有复杂项目

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

import { ContextMenu } from "radix-ui";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content>
<ContextMenu.Item>
<img src="" />
Adolfo Hess
</ContextMenu.Item>
<ContextMenu.Item>
<img src="" />
Miyah Myles
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);

约束内容/子内容大小

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

我们公开了几个 CSS 自定义属性,例如 --radix-context-menu-trigger-width--radix-context-menu-content-available-height 以支持这一点。 使用它们来约束内容尺寸。

// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent">
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuContent {
width: var(--radix-context-menu-trigger-width);
max-height: var(--radix-context-menu-content-available-height);
}

感知原点的动画

我们公开了一个 CSS 自定义属性 --radix-context-menu-content-transform-origin。 使用它从其计算的原点(基于 sidesideOffsetalignalignOffset 和任何碰撞)为内容添加动画效果。

// index.jsx
import { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent">
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuContent {
transform-origin: var(--radix-context-menu-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 { ContextMenu } from "radix-ui";
import "./styles.css";
export default () => (
<ContextMenu.Root>
<ContextMenu.Trigger></ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content className="ContextMenuContent">
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
);
/* styles.css */
.ContextMenuContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.ContextMenuContent[data-side="top"] {
animation-name: slideUp;
}
.ContextMenuContent[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);
}
}

可访问性

使用 roving tabindex 来管理菜单项之间的焦点移动。

键盘交互

按键Description(描述)
Space
激活聚焦的项目。
Enter
激活聚焦的项目。
ArrowDown
将焦点移动到下一个项目。
ArrowUp
将焦点移动到上一个项目。
ArrowRightArrowLeft
当焦点在 ContextMenu.SubTrigger 上时,根据阅读方向打开或关闭子菜单。
Esc
关闭上下文菜单
上一个Collapsible
下一个Dialog