Dropdown
Dropdown 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 v3 Dropdown 文档。本指南只关注从 HeroUI v2 的迁移。
结构变化
在 v2 中,Dropdown 使用相互独立的组件:DropdownTrigger、DropdownMenu、DropdownItem、DropdownSection:
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@lenso/ui";
export default function App() { return ( <Dropdown> <DropdownTrigger> <Button>Open Menu</Button> </DropdownTrigger> <DropdownMenu> <DropdownItem key="new">New file</DropdownItem> <DropdownItem key="copy">Copy link</DropdownItem> </DropdownMenu> </Dropdown> );}在 v3 中,Dropdown 采用复合组件模式,并提供显式的子组件结构:
import { Dropdown, Button, Label } from "@lenso/ui";
export default function App() { return ( <Dropdown> <Button>Open Menu</Button> <Dropdown.Popover> <Dropdown.Menu> <Dropdown.Item id="new" textValue="New file"> <Label>New file</Label> </Dropdown.Item> <Dropdown.Item id="copy" textValue="Copy link"> <Label>Copy link</Label> </Dropdown.Item> </Dropdown.Menu> </Dropdown.Popover> </Dropdown> );}主要变化
1. 组件结构
v2: 相互独立的组件:DropdownTrigger、DropdownMenu、DropdownItem、DropdownSection
v3: 复合组件:Dropdown.Trigger、Dropdown.Popover、Dropdown.Menu、Dropdown.Item、Dropdown.Section
2. 组件名称变更
| v2 组件 | v3 组件 | 说明 |
|---|---|---|
DropdownTrigger | Dropdown.Trigger | 功能相同 |
DropdownMenu | Dropdown.Menu | 需包裹在 Dropdown.Popover 内 |
DropdownItem | Dropdown.Item | 使用 id 与 textValue;列表项保留 key |
DropdownSection | Dropdown.Section | 功能相同 |
| — | Dropdown.Popover | 新增包裹组件(必填) |
3. 菜单项标识
v2: 菜单项内容通过 children 传递;React 的 key 同时用于列表调和与菜单项标识(选择、焦点)。
v3: 菜单项的可见文本必须使用 Label 组件。为每个菜单项提供 id(状态/焦点)与 textValue(当内容不是纯文本时用于无障碍)。列表中的菜单项仍应保留 React 的 key。
4. Prop 变更
| v2 prop | v3 位置 | 说明 |
|---|---|---|
variant、color(在 DropdownMenu 上) | — | 已移除(菜单不再有 variant/color) |
classNames、itemClasses(在 DropdownMenu 上) | — | 在 Menu 与菜单项上使用 className |
color(在 DropdownItem 上) | Dropdown.Item | 使用 variant="danger" 表示危险操作 |
title | — | 使用 Label 子组件 |
description | — | 使用 Description 子组件 |
shortcut | — | 使用 Kbd 子组件 |
startContent、endContent | — | 将图标或组件作为 children 放置 |
selectedIcon | — | 使用 Dropdown.ItemIndicator |
showDivider | — | 在菜单项之间使用 Separator |
classNames(在 DropdownItem 上) | — | 在菜单项上使用 className |
isSelected | Dropdown.Menu | 在 Menu 上使用 selectedKeys |
isDisabled | Dropdown.Menu | 在 Menu 上使用 disabledKeys |
trigger(在 Dropdown 上) | Dropdown | 仍支持:"press"(默认)或 "longPress" |
5. 新增子组件
Dropdown.Popover:Dropdown.Menu的必填外层包裹Dropdown.ItemIndicator:用于选择指示(对勾/圆点等)Dropdown.SubmenuTrigger:子菜单触发结构Dropdown.SubmenuIndicator:子菜单 chevron 指示
迁移示例
使用 onAction
<DropdownMenu onAction={(key) => alert(key)}> <DropdownItem key="new">New file</DropdownItem></DropdownMenu>菜单项内容
{/* With icon */}<DropdownItem key="new" startContent={<AddNoteIcon />}> New file</DropdownItem>
{/* With description */}<DropdownItem key="edit" description="Edit the file"> Edit file</DropdownItem>
{/* With shortcut */}<DropdownItem key="copy" shortcut="⌘C"> Copy</DropdownItem>危险菜单项
<DropdownItem key="delete" className="text-danger" color="danger"> Delete file</DropdownItem>使用分组(Section)
<DropdownMenu> <DropdownSection showDivider title="Actions"> <DropdownItem key="new">New file</DropdownItem> <DropdownItem key="edit">Edit file</DropdownItem> </DropdownSection> <DropdownSection title="Danger zone"> <DropdownItem key="delete" color="danger">Delete file</DropdownItem> </DropdownSection></DropdownMenu>选择
import { useState } from "react";
{/* Single selection */}const [singleSelected, setSingleSelected] = useState(new Set(["text"]));<DropdownMenu selectedKeys={singleSelected} selectionMode="single" onSelectionChange={setSingleSelected}> <DropdownItem key="text">Text</DropdownItem> <DropdownItem key="number">Number</DropdownItem></DropdownMenu>
{/* Multiple selection */}const [multiSelected, setMultiSelected] = useState(new Set(["bold"]));<DropdownMenu selectedKeys={multiSelected} selectionMode="multiple" onSelectionChange={setMultiSelected}> <DropdownItem key="bold">Bold</DropdownItem> <DropdownItem key="italic">Italic</DropdownItem></DropdownMenu>键盘快捷键
<DropdownItem key="copy" shortcut="⌘C"> Copy</DropdownItem>子菜单
{/* v2 did not have built-in submenu support */}长按触发
<Dropdown> <DropdownTrigger> <Button>Long press me</Button> </DropdownTrigger> <DropdownMenu> <DropdownItem key="cut">Cut</DropdownItem> <DropdownItem key="copy">Copy</DropdownItem> </DropdownMenu></Dropdown>组件结构(Anatomy)
v3 的 Dropdown 结构如下:
Dropdown (Root) — accepts trigger="press" | "longPress" ├── Dropdown.Trigger (optional, defaults to first child) ├── Dropdown.Popover (required wrapper) │ └── Dropdown.Menu │ ├── Dropdown.Item │ │ ├── Icon (optional, first child) │ │ ├── Label (required for text) │ │ ├── Description (optional) │ │ ├── Kbd slot="keyboard" (optional, for shortcuts) │ │ └── Dropdown.ItemIndicator (optional, for selection) │ ├── Separator (for dividers) │ ├── Dropdown.Section │ │ ├── Header (optional) │ │ └── Dropdown.Item │ └── Dropdown.SubmenuTrigger │ ├── Dropdown.Item │ │ ├── Label │ │ └── Dropdown.SubmenuIndicator (chevron icon) │ └── Dropdown.Popover │ └── Dropdown.Menu │ └── Dropdown.Item总结
- 组件结构:必须使用复合组件(
Dropdown.Trigger、Dropdown.Popover、Dropdown.Menu等)。 Dropdown.Popover为必填:Dropdown.Menu必须包裹在Dropdown.Popover内。Label组件:菜单项文本必须使用Label。Description组件:用Description替代descriptionprop。Kbd组件:用带slot="keyboard"的Kbd替代shortcutprop;slot 用于把快捷键放到菜单项末尾。- 图标作为 children:图标作为第一个 child 放置,不再使用
startContentprop。 Separator组件:用Separator替代showDividerprop。ItemIndicator组件:用Dropdown.ItemIndicator表示选择状态。- 用 variant 表示危险:用
variant="danger"替代color="danger"。 - 菜单样式 prop 移除:
Dropdown.Menu不再支持variant或colorprop。 classNames移除:在各自子组件上使用className。- 子菜单:用
Dropdown.SubmenuTrigger包裹会打开嵌套菜单的项,并在项内使用Dropdown.SubmenuIndicator。 triggerprop:在根Dropdown上使用trigger="longPress"以长按打开菜单(默认为按压打开)。