DropdownMenu
下拉菜单:menu / menuitem 语义,方向键与 Home / End 导航、typeahead 跳转,开合时管理焦点。支持普通 / 勾选 / 单选 / 分组 / 子菜单,以及图标与快捷键提示。
安装
添加组件
pnpm dlx @elyri/cli@latest add dropdown-menu示例
基础用法
包含分组标题、可选菜单项、分隔线与禁用项,选中项显示在下方。
Selected: Nothing yet
import { useState } from 'react';
import { DropdownMenu } from './components/elyri/DropdownMenu';
export function Example() {
const [selected, setSelected] = useState('Nothing yet');
return (
<div className="demo-ui-row">
<DropdownMenu>
<DropdownMenu.Trigger>Actions</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Label>Edit</DropdownMenu.Label>
<DropdownMenu.Item onSelect={() => setSelected('Duplicate')}>Duplicate</DropdownMenu.Item>
<DropdownMenu.Item onSelect={() => setSelected('Rename')}>Rename</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item onSelect={() => setSelected('Archive')}>Archive</DropdownMenu.Item>
<DropdownMenu.Item disabled>Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
<span className="demo-ui-label">Selected: {selected}</span>
</div>
);
}勾选与单选
CheckboxItem 与 RadioGroup / RadioItem 切换后保持打开,方便连续选择。
Sort: name · Compact: on
import { useState } from 'react';
import { DropdownMenu } from './components/elyri/DropdownMenu';
export function Example() {
const [sort, setSort] = useState('name');
const [compact, setCompact] = useState(true);
return (
<div className="demo-ui-row">
<DropdownMenu>
<DropdownMenu.Trigger>View</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Label>Sort by</DropdownMenu.Label>
<DropdownMenu.RadioGroup value={sort} onValueChange={setSort}>
<DropdownMenu.RadioItem value="name">Name</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="date">Date modified</DropdownMenu.RadioItem>
</DropdownMenu.RadioGroup>
<DropdownMenu.Separator />
<DropdownMenu.CheckboxItem checked={compact} onCheckedChange={setCompact}>
Compact density
</DropdownMenu.CheckboxItem>
</DropdownMenu.Content>
</DropdownMenu>
<span className="demo-ui-label">Sort: {sort} · Compact: {compact ? 'on' : 'off'}</span>
</div>
);
}子菜单
Sub / SubTrigger / SubContent 逐级展开,可用方向键或右键定位;也可让某项保持打开。
import { DropdownMenu } from './components/elyri/DropdownMenu';
export function Example() {
return (
<div className="demo-ui-row">
<DropdownMenu>
<DropdownMenu.Trigger>Share</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Copy link</DropdownMenu.Item>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Invite people</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item>Email</DropdownMenu.Item>
<DropdownMenu.Item>Slack</DropdownMenu.Item>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>More…</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item>Teams</DropdownMenu.Item>
<DropdownMenu.Item>Discord</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
<DropdownMenu.Separator />
{/* onSelect 里 preventDefault 让菜单保持打开 */}
<DropdownMenu.Item onSelect={(event) => event.preventDefault()} danger>
Revoke access
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</div>
);
}图标、快捷键与 asChild
菜单项可带图标与右侧快捷键提示;Trigger 传 asChild 可复用 Button 等自定义触发元素。
Selected: Nothing yet
import { useState } from 'react';
import { Button } from './components/elyri/Button';
import { DropdownMenu } from './components/elyri/DropdownMenu';
export function Example() {
const [selected, setSelected] = useState('Nothing yet');
const pick = (label: string) => () => setSelected(label);
return (
<div className="demo-ui-row">
<DropdownMenu>
<DropdownMenu.Trigger asChild>
<Button variant="secondary">File</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item onSelect={pick('New')}>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
aria-hidden="true"
>
<path d="M12 5v14M5 12h14" />
</svg>
New
<DropdownMenu.Shortcut>⌘N</DropdownMenu.Shortcut>
</DropdownMenu.Item>
<DropdownMenu.Item onSelect={pick('Save')}>
Save
<DropdownMenu.Shortcut>⌘S</DropdownMenu.Shortcut>
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item danger onSelect={pick('Delete')}>
Delete
<DropdownMenu.Shortcut>⌫</DropdownMenu.Shortcut>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
<span className="demo-ui-label">Selected: {selected}</span>
</div>
);
}属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
open | boolean | — | 受控开关 |
defaultOpen | boolean | false | 非受控初始开关 |
onOpenChange | (open: boolean) => void | — | 开关变化回调 |
placement | 'top' | 'bottom' | 'left' | 'right' | 'bottom' | 期望方向:top / bottom / left / right |
align | 'start' | 'center' | 'end' | 'start' | 交叉轴对齐:start / center / end |
offset | number | 4 | 与触发元素的间距 |
DropdownMenu.Trigger | ButtonHTMLAttributes | — | 触发元素,方向键也可打开菜单;asChild 时合并到唯一子元素 |
Trigger.asChild | boolean | false | 不渲染自带按钮,把行为合并到唯一子元素上 |
DropdownMenu.Content | HTMLAttributes | — | 菜单容器,role="menu" |
DropdownMenu.Item | ButtonHTMLAttributes | — | 菜单项,role="menuitem",选中后默认关闭 |
onSelect | (event) => void | — | 选中回调;调用 event.preventDefault() 可让菜单保持打开 |
disabled | boolean | — | 禁用项,不参与键盘导航 |
danger | boolean | — | 危险操作(如删除)显示为警示色 |
DropdownMenu.CheckboxItem | ButtonHTMLAttributes | — | 可勾选菜单项,role="menuitemcheckbox",切换后保持打开 |
CheckboxItem.checked | boolean | — | 受控选中态 |
CheckboxItem.defaultChecked | boolean | false | 非受控初始选中态 |
CheckboxItem.onCheckedChange | (checked: boolean) => void | — | 选中态变化回调 |
DropdownMenu.RadioGroup | HTMLAttributes | — | 单选项分组,role="group" |
RadioGroup.value | string | — | 受控选中值 |
RadioGroup.defaultValue | string | — | 非受控初始选中值 |
RadioGroup.onValueChange | (value: string) => void | — | 选中值变化回调 |
DropdownMenu.RadioItem | ButtonHTMLAttributes | — | 单选菜单项,role="menuitemradio",选中后保持打开 |
RadioItem.value | string | — | 该项的取值,与 RadioGroup 的 value 对应 |
DropdownMenu.Sub | object | — | 子菜单根组件,管理自身的展开状态 |
DropdownMenu.SubTrigger | ButtonHTMLAttributes | — | 子菜单触发项,带 aria-haspopup,悬停或方向键展开 |
DropdownMenu.SubContent | HTMLAttributes | — | 子菜单容器,默认在右侧展开 |
DropdownMenu.Group | HTMLAttributes | — | 分组容器,role="group",可配 aria-label |
DropdownMenu.Label | HTMLAttributes | — | 分组标题 |
DropdownMenu.Separator | HTMLAttributes | — | 分隔线 |
DropdownMenu.Shortcut | HTMLAttributes | — | 右侧快捷键提示,如 ⌘K |