Components

下拉菜单

向用户展示菜单——例如一组操作或功能——由按钮触发。

import * as React from "react";
import { DropdownMenu } from "radix-ui";
import { HamburgerMenuIcon, DotFilledIcon, CheckIcon, ChevronRightIcon, } from "@radix-ui/react-icons";
import "./styles.css";
const DropdownMenuDemo = () => {
const [bookmarksChecked, setBookmarksChecked] = React.useState(true);
const [urlsChecked, setUrlsChecked] = React.useState(false);
const [person, setPerson] = React.useState("pedro");
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="IconButton" aria-label="Customise options">
<HamburgerMenuIcon />
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content className="DropdownMenuContent" sideOffset={5}>
<DropdownMenu.Item className="DropdownMenuItem">
New Tab <div className="RightSlot">⌘+T</div>
</DropdownMenu.Item>
<DropdownMenu.Item className="DropdownMenuItem">
New Window <div className="RightSlot">⌘+N</div>
</DropdownMenu.Item>
<DropdownMenu.Item className="DropdownMenuItem" disabled>
New Private Window <div className="RightSlot">⇧+⌘+N</div>
</DropdownMenu.Item>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger className="DropdownMenuSubTrigger">
More Tools
<div className="RightSlot">
<ChevronRightIcon />
</div>
</DropdownMenu.SubTrigger>
<DropdownMenu.Portal>
<DropdownMenu.SubContent className="DropdownMenuSubContent" sideOffset={2} alignOffset={-5} >
<DropdownMenu.Item className="DropdownMenuItem">
Save Page As… <div className="RightSlot">⌘+S</div>
</DropdownMenu.Item>
<DropdownMenu.Item className="DropdownMenuItem">
Create Shortcut…
</DropdownMenu.Item>
<DropdownMenu.Item className="DropdownMenuItem">
Name Window…
</DropdownMenu.Item>
<DropdownMenu.Separator className="DropdownMenu.Separator" />
<DropdownMenu.Item className="DropdownMenuItem">
Developer Tools
</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Portal>
</DropdownMenu.Sub>
<DropdownMenu.Separator className="DropdownMenuSeparator" />
<DropdownMenu.CheckboxItem className="DropdownMenuCheckboxItem" checked={bookmarksChecked} onCheckedChange={setBookmarksChecked} >
<DropdownMenu.ItemIndicator className="DropdownMenuItemIndicator">
<CheckIcon />
</DropdownMenu.ItemIndicator>
Show Bookmarks <div className="RightSlot">⌘+B</div>
</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem className="DropdownMenuCheckboxItem" checked={urlsChecked} onCheckedChange={setUrlsChecked} >
<DropdownMenu.ItemIndicator className="DropdownMenuItemIndicator">
<CheckIcon />
</DropdownMenu.ItemIndicator>
Show Full URLs
</DropdownMenu.CheckboxItem>
<DropdownMenu.Separator className="DropdownMenuSeparator" />
<DropdownMenu.Label className="DropdownMenuLabel">
People
</DropdownMenu.Label>
<DropdownMenu.RadioGroup value={person} onValueChange={setPerson}>
<DropdownMenu.RadioItem className="DropdownMenuRadioItem" value="pedro" >
<DropdownMenu.ItemIndicator className="DropdownMenuItemIndicator">
<DotFilledIcon />
</DropdownMenu.ItemIndicator>
Pedro Duarte
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem className="DropdownMenuRadioItem" value="colm" >
<DropdownMenu.ItemIndicator className="DropdownMenuItemIndicator">
<DotFilledIcon />
</DropdownMenu.ItemIndicator>
Colm Tuite
</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
<DropdownMenu.Arrow className="DropdownMenuArrow" />
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
};
export default DropdownMenuDemo;

Features

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

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

    支持项、标签、项组。

    支持可选的单选或多选可勾选项,具有可选的不确定状态。

    支持模态和非模态模式。

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

    可选渲染指向箭头。

    焦点完全管理。

    完整的键盘导航。

    支持前置输入。

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

安装

从命令行安装组件。

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

解剖

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

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

API 参考

包含下拉菜单的所有部分。

PropTypeDefault
defaultOpen
boolean
No default value
open
boolean
No default value
onOpenChange
function
No default value
modal
boolean
true
dir
enum
No default value

触发器

切换下拉菜单的按钮。默认情况下,DropdownMenu.Content 将相对于触发器定位。

PropTypeDefault
asChild
boolean
false
Data attributeValues
[data-state]"open" | "closed"
[data-disabled]

当禁用时存在

门户

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

PropTypeDefault
forceMount
boolean
No default value
container
HTMLElement
document.body

内容

下拉菜单打开时弹出的组件。

PropTypeDefault
asChild
boolean
false
loop
boolean
false
onCloseAutoFocus
function
No default value
onEscapeKeyDown
function
No default value
onPointerDownOutside
function
No default value
onFocusOutside
function
No default value
onInteractOutside
function
No default value
forceMount
boolean
No default value
side
enum
"bottom"
sideOffset
number
0
align
enum
"center"
alignOffset
number
0
avoidCollisions
boolean
true
collisionBoundary
Boundary
[]
collisionPadding
number | Padding
0
arrowPadding
number
0
sticky
enum
"partial"
hideWhenDetached
boolean
false
Data attributeValues
[data-state]"open" | "closed"
[data-side]"left" | "right" | "bottom" | "top"
[data-align]"start" | "end" | "center"
[data-orientation]"vertical" | "horizontal"
CSS VariableDescription
--radix-dropdown-menu-content-transform-origin从内容和箭头位置/偏移量计算的 transform-origin
--radix-dropdown-menu-content-available-width触发器和边界边缘之间的剩余宽度
--radix-dropdown-menu-content-available-height触发器和边界边缘之间的剩余高度
--radix-dropdown-menu-trigger-width触发器的宽度
--radix-dropdown-menu-trigger-height触发器的高度

箭头

一个可选的箭头元素,用于渲染在下拉菜单旁边。可以用来帮助视觉链接触发器与 DropdownMenu.Content。必须在 DropdownMenu.Content 内部渲染。

PropTypeDefault
asChild
boolean
false
width
number
10
height
number
5

包含下拉菜单项的组件。

PropTypeDefault
asChild
boolean
false
disabled
boolean
No default value
onSelect
function
No default value
textValue
string
No default value
Data attributeValues
[data-orientation]"vertical" | "horizontal"
[data-highlighted]

当选中时存在

[data-disabled]

当禁用时存在

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

PropTypeDefault
asChild
boolean
false

标签

用于渲染标签。不能通过箭头键聚焦。

PropTypeDefault
asChild
boolean
false

CheckboxItem

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

PropTypeDefault
asChild
boolean
false
checked
boolean | 'indeterminate'
No default value
onCheckedChange
function
No default value
disabled
boolean
No default value
onSelect
function
No default value
textValue
string
No default value
Data attributeValues
[data-state]"checked" | "unchecked" | "indeterminate"
[data-highlighted]

当选中时存在

[data-disabled]

当禁用时存在

RadioGroup

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

PropTypeDefault
asChild
boolean
false
value
string
No default value
onValueChange
function
No default value

RadioItem

一个可以控制和渲染为单选框的项。

PropTypeDefault
asChild
boolean
false
value*
string
No default value
disabled
boolean
No default value
onSelect
function
No default value
textValue
string
No default value
Data attributeValues
[data-state]"checked" | "unchecked" | "indeterminate"
[data-highlighted]

当选中时存在

[data-disabled]

当禁用时存在

项指示器

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

PropTypeDefault
asChild
boolean
false
forceMount
boolean
No default value
Data attributeValues
[data-state]"checked" | "unchecked" | "indeterminate"

分隔符

用于在下拉菜单中视觉上分隔项。

PropTypeDefault
asChild
boolean
false

子菜单

包含所有子菜单的部分。

PropTypeDefault
defaultOpen
boolean
No default value
open
boolean
No default value
onOpenChange
function
No default value

子触发器

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

PropTypeDefault
asChild
boolean
false
disabled
boolean
No default value
textValue
string
No default value
Data attributeValues
[data-state]"open" | "closed"
[data-highlighted]

当选中时存在

[data-disabled]

当禁用时存在

CSS VariableDescription
--radix-dropdown-menu-content-transform-origin从内容和箭头位置/偏移量计算的 transform-origin
--radix-dropdown-menu-content-available-width触发器和边界边缘之间的剩余宽度
--radix-dropdown-menu-content-available-height触发器和边界边缘之间的剩余高度
--radix-dropdown-menu-trigger-width触发器的宽度
--radix-dropdown-menu-trigger-height触发器的高度

示例

带子菜单的示例

您可以通过组合使用 DropdownMenu.Sub 以及其部分来创建子菜单。

<DropdownMenu.Root>
<DropdownMenu.Trigger></DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content>
<DropdownMenu.Item></DropdownMenu.Item>
<DropdownMenu.Item></DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>子菜单 →</DropdownMenu.SubTrigger>
<DropdownMenu.Portal>
<DropdownMenu.SubContent>
<DropdownMenu.Item>子菜单项</DropdownMenu.Item>
<DropdownMenu.Item>子菜单项</DropdownMenu.Item>
<DropdownMenu.Arrow />
</DropdownMenu.SubContent>
</DropdownMenu.Portal>
</DropdownMenu.Sub>
<DropdownMenu.Separator />
<DropdownMenu.Item></DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>

带禁用项的示例

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

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

带分隔符的示例

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

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

带标签的示例

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

<DropdownMenu.Root>
<DropdownMenu.Trigger></DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content>
<DropdownMenu.Label>标签</DropdownMenu.Label>
<DropdownMenu.Item></DropdownMenu.Item>
<DropdownMenu.Item></DropdownMenu.Item>
<DropdownMenu.Item></DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>

带复选框项的示例

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

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

带单选项的示例

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

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

带复杂项的示例

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

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

限制内容/子内容的大小

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

我们公开了多个 CSS 自定义属性,如 --radix-dropdown-menu-trigger-width--radix-dropdown-menu-content-available-height 来支持此功能。使用它们来限制内容尺寸。

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

基于原点的动画

我们公开了 CSS 自定义属性 --radix-dropdown-menu-content-transform-origin。使用它从其计算的原点为内容动画,基于 sidesideOffsetalignalignOffset 和任何碰撞。

// index.jsx
import { DropdownMenu } from "radix-ui";
import "./styles.css";
export default () => (
<DropdownMenu.Root>
<DropdownMenu.Trigger></DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content className="DropdownMenuContent">
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
/* styles.css */
.DropdownMenuContent {
transform-origin: var(--radix-dropdown-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 { DropdownMenu } from "radix-ui";
import "./styles.css";
export default () => (
<DropdownMenu.Root>
<DropdownMenu.Trigger></DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content className="DropdownMenuContent">
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
/* styles.css */
.DropdownMenuContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.DropdownMenuContent[data-side="top"] {
animation-name: slideUp;
}
.DropdownMenuContent[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 设计模式 并使用 游走 tabindex 来管理菜单项间的焦点移动。

键盘交互

KeyDescription
Space
当焦点在 DropdownMenu.Trigger 上时,打开下拉菜单并将焦点设置在第一个项上。
当焦点在某项上时,激活该项。
Enter
当焦点在 DropdownMenu.Trigger 上时,打开下拉菜单并将焦点设置在第一个项上。
当焦点在某项上时,激活该项。
ArrowDown
当焦点在 DropdownMenu.Trigger 上时,打开下拉菜单。
当焦点在某项上时,焦点移动到下一项。
ArrowUp
当焦点在某项上时,焦点移动到上一项。
ArrowRightArrowLeft
当焦点在 DropdownMenu.SubTrigger 上时,根据阅读方向打开或关闭子菜单。
Esc
关闭下拉菜单并将焦点移到 DropdownMenu.Trigger 上。

自定义 API

通过将原始部件抽象为您自己的组件来创建自己的 API。

抽象箭头和项指示器

此示例抽象了 DropdownMenu.ArrowDropdownMenu.ItemIndicator 部件。它还封装了 CheckboxItemRadioItem 的实现细节。

用法

import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuGroup, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, } from "./your-dropdown-menu";
export default () => (
<DropdownMenu>
<DropdownMenuTrigger>下拉菜单触发器</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem></DropdownMenuItem>
<DropdownMenuLabel>标签</DropdownMenuLabel>
<DropdownMenuGroup></DropdownMenuGroup>
<DropdownMenuCheckboxItem>复选框项</DropdownMenuCheckboxItem>
<DropdownMenuSeparator>分隔符</DropdownMenuSeparator>
<DropdownMenuRadioGroup>
<DropdownMenuRadioItem>单选项</DropdownMenuRadioItem>
<DropdownMenuRadioItem>单选项</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
);

实现

// your-dropdown-menu.jsx
import * as React from "react";
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
import { CheckIcon, DividerHorizontalIcon } from "@radix-ui/react-icons";
export const DropdownMenu = DropdownMenuPrimitive.Root;
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
export const DropdownMenuContent = React.forwardRef(
({ children, ...props }, forwardedRef) => {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content {...props} ref={forwardedRef}>
{children}
<DropdownMenuPrimitive.Arrow />
</DropdownMenuPrimitive.Content>
</DropdownMenuPrimitive.Portal>
);
},
);
export const DropdownMenuLabel = DropdownMenuPrimitive.Label;
export const DropdownMenuItem = DropdownMenuPrimitive.Item;
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
export const DropdownMenuCheckboxItem = React.forwardRef(
({ children, ...props }, forwardedRef) => {
return (
<DropdownMenuPrimitive.CheckboxItem {...props} ref={forwardedRef}>
{children}
<DropdownMenuPrimitive.ItemIndicator>
{props.checked === "indeterminate" && <DividerHorizontalIcon />}
{props.checked === true && <CheckIcon />}
</DropdownMenuPrimitive.ItemIndicator>
</DropdownMenuPrimitive.CheckboxItem>
);
},
);
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
export const DropdownMenuRadioItem = React.forwardRef(
({ children, ...props }, forwardedRef) => {
return (
<DropdownMenuPrimitive.RadioItem {...props} ref={forwardedRef}>
{children}
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon />
</DropdownMenuPrimitive.ItemIndicator>
</DropdownMenuPrimitive.RadioItem>
);
},
);
export const DropdownMenuSeparator = DropdownMenuPrimitive.Separator;
下一篇表单 Form