DropdownMenu
- 组件说明: 下拉菜单组件,用于在触发元素下方展示一组操作项,支持 Default/Danger 语义变体、图标插槽和级联子菜单。
- 尺寸基线: Content min-width 160px, padding-y 8px, radius 5px; Item padding 16px × 8px, font 14px/20px。
- 实现约定: 标准用法通过
items声明式配置,级联子菜单也通过subItems描述;仅在 CheckboxItem / RadioItem 等高级场景下再使用组合式 API。ui/dropdown-menu保留结构骨架和动画,design 层通过unstyledVisual接管视觉。 - Figma 规范
基础用法
通过 items 声明式配置菜单项,children 放触发元素。
结果
Loading...
实时编辑器
render( <DropdownMenu items={[{ label: '个人资料' }, { label: '账户设置' }, { type: 'separator' }, { label: '退出登录' }]} > <Button variant="secondary">打开菜单</Button> </DropdownMenu>, )
变体
通过 variant 区分操作语义。
结果
Loading...
实时编辑器
render( <DropdownMenu items={[ { label: '编辑' }, { label: '复制' }, { type: 'separator' }, { label: '删除', variant: 'danger' }, ]} > <Button variant="secondary">操作</Button> </DropdownMenu>, )
状态
结果
Loading...
实时编辑器
render( <DropdownMenu items={[ { label: '正常项' }, { label: '禁用项', disabled: true }, { type: 'separator' }, { label: '危险项', variant: 'danger' }, { label: '禁用危险项', variant: 'danger', disabled: true }, ]} > <Button variant="secondary">状态展示</Button> </DropdownMenu>, )
带图标
通过 icon 为菜单项添加前置图标。
结果
Loading...
实时编辑器
const EditIcon = () => ( <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M10.5 1.75L12.25 3.5L10.5 5.25M1.75 12.25H3.5L10.5 5.25L8.75 3.5L1.75 10.5V12.25Z" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"/> </svg> ) const TrashIcon = () => ( <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M1.75 3.5H12.25M5.25 6.125V10.375M8.75 6.125V10.375M2.625 3.5L3.5 11.375C3.5 11.8582 3.89175 12.25 4.375 12.25H9.625C10.1082 12.25 10.5 11.8582 10.5 11.375L11.375 3.5M4.375 3.5V2.625C4.375 2.14175 4.76675 1.75 5.25 1.75H8.75C9.23325 1.75 9.625 2.14175 9.625 2.625V3.5" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"/> </svg> ) const ArrowIcon = ({ direction }) => { const rotationMap = { up: 'rotate(-180deg)', right: 'rotate(-90deg)', down: 'rotate(0deg)', left: 'rotate(90deg)', } return ( <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ transform: rotationMap[direction] }} > <path d="M3.5 5.25L7 8.75L10.5 5.25" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" /> </svg> ) } render( <DropdownMenu items={[ { label: '编辑', icon: <EditIcon /> }, { label: '向上箭头', icon: <ArrowIcon direction="up" /> }, { label: '向右箭头', icon: <ArrowIcon direction="right" /> }, { label: '向下箭头', icon: <ArrowIcon direction="down" /> }, { label: '向左箭头', icon: <ArrowIcon direction="left" /> }, { type: 'separator' }, { label: '删除', variant: 'danger', icon: <TrashIcon /> }, ]} > <Button variant="secondary">带图标</Button> </DropdownMenu>, )
带标签分组
结果
Loading...
实时编辑器
render( <DropdownMenu items={[ { type: 'label', label: '我的账户' }, { label: '个人资料' }, { label: '账户设置' }, { type: 'separator' }, { type: 'label', label: '团队' }, { label: '团队设置' }, { label: '邀请成员' }, ]} > <Button variant="secondary">分组菜单</Button> </DropdownMenu>, )
级联子菜单
结果
Loading...
实时编辑器
render( <DropdownMenu items={[ { label: '新建文件' }, { label: '更多选项', subItems: [{ label: '导入' }, { label: '导出' }], }, { type: 'separator' }, { label: '删除', variant: 'danger' }, ]} > <Button variant="secondary">级联菜单</Button> </DropdownMenu>, )
尺寸规范
Content 容器
| 属性 | Token | 值 |
|---|---|---|
| 最小宽度 | — | 160px |
| 纵向内边距 | Spacing_8 | 8px |
| 圆角 | Radius_5 | 5px |
| 阴影 | Effects/Shadow/Default | 0 0 32px rgba(0,0,0,0.1) |
Item 项
| 属性 | Token | 值 |
|---|---|---|
| 水平内边距 | Spacing_16 | 16px |
| 纵向内边距 | Spacing_8 | 8px |
| 字号 | Font-Size/Body | 14px |
| 行高 | Line-Height/Body | 22px |
| 图标容器 | — | 20×20px |
颜色 Token
| 状态 | 文本色 | Hover 背景 |
|---|---|---|
| Default | Labels/Secondary#3d3d3d | Grays/Gray-1#ebebeb |
| Danger | Labels/Error#cc3325 | Grays/Gray-1#ebebeb |
| Disabled | Labels/Disabled#a3a3a3 | — |
Props
DropdownMenu(便捷 API)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| children | ReactNode | - | 触发元素 |
| items | DropdownMenuItemEntry[] | - | 声明式菜单项配置 |
| asChild | boolean | true | 是否将 children 作为 Trigger 本体 |
| open | boolean | - | 受控模式开关 |
| onOpenChange | (open: boolean) => void | - | 开关变化回调 |
| contentClassName | string | - | Content 自定义类名 |
| side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | 弹出方向 |
| align | 'start' | 'center' | 'end' | 'start' | 对齐方式 |
DropdownMenuItemEntry
| 类型 | type 值 | 必填属性 | 说明 |
|---|---|---|---|
| 菜单项 | 'item'(可省略) | label | 支持 variant、icon、disabled、onSelect、subItems、itemProps |
| 分隔线 | 'separator' | - | 渲染为 DropdownMenuSeparator |
| 分组标题 | 'label' | label | 渲染为 DropdownMenuLabel |
DropdownMenuItem
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| children | ReactNode | - | 菜单项内容 |
| variant | 'default' | 'danger' | 'default' | 语义变体 |
| icon | ReactNode | - | 前置图标 |
| disabled | boolean | false | 禁用状态 |
| onSelect | (event: Event) => void | - | 选中回调 |
| className | string | - | 自定义类名 |
高级用法
默认优先使用 items / subItems。只有在需要 CheckboxItem、RadioItem 或完全自定义内容组合时,再切换到 DropdownMenu + DropdownMenuTrigger + DropdownMenuContent。