Listbox
Listbox 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 v3 ListBox 文档。本指南只关注从 HeroUI v2 的迁移。
结构变化
v2:独立组件
在 v2 中,Listbox 使用彼此独立的组件:
import { Listbox, ListboxItem, ListboxSection } from "@lenso/ui";
export default function App() { return ( <Listbox> <ListboxItem key="1">Item 1</ListboxItem> </Listbox> );}v3:复合组件
在 v3 中,ListBox 使用复合组件:
import { ListBox, Label } from "@lenso/ui";
export default function App() { return ( <ListBox> <ListBox.Item id="1" textValue="Item 1"> <Label>Item 1</Label> </ListBox.Item> </ListBox> );}主要变化
1. 组件命名
v2: Listbox、ListboxItem、ListboxSection
v3: ListBox、ListBox.Item、ListBox.Section
2. 条目标识
v2: React 的 key 同时用于列表调和与条目身份(选择、焦点)。
v3: 使用 id 管理状态与焦点,使用 textValue 提供无障碍信息(当内容不是纯文本时);列表中仍可为各项保留 React 的 key。
3. Prop 变更
| v2 prop | v3 位置 | 说明 |
|---|---|---|
key(用于状态) | ListBox.Item | 条目身份(状态)请使用 id |
| — | textValue(在 ListBox.Item 上) | 用于无障碍(输入预判) |
variant、color | ListBox 或 ListBox.Item 上的 variant | 精简为 "default" | "danger"(不再有单独的 color prop) |
onAction | ListBox | 按下条目时触发,签名为 (key: Key) => void |
disabledKeys | ListBox | 与 v2 相同——一组键,对应应为非交互的条目 |
startContent、endContent | — | 请在条目内容中手动放置图标 |
description | Description | 使用 Description 组件 |
title(在 Section 上) | Header | 使用 Header 组件 |
topContent、bottomContent | — | 已移除(请自行处理) |
itemClasses、classNames | — | 在各部分上使用 className |
hideSelectedIcon | — | 不渲染 ListBox.ItemIndicator |
disableAnimation | — | 已移除 |
isVirtualized、virtualization | React Aria Virtualizer | 使用 React Aria 的 <Virtualizer> 包装(见下方示例) |
selectedKeys | ListBox | 与 v2 相同(使用 Selection 类型的 Set) |
迁移示例
选择
import { useState } from "react";
{/* Single selection */}const [singleSelected, setSingleSelected] = useState(new Set(["text"]));
<Listbox selectedKeys={singleSelected} selectionMode="single" onSelectionChange={setSingleSelected}> <ListboxItem key="text">Text</ListboxItem> <ListboxItem key="number">Number</ListboxItem></Listbox>
{/* Multiple selection */}const [multiSelected, setMultiSelected] = useState(new Set(["text"]));
<Listbox selectedKeys={multiSelected} selectionMode="multiple" onSelectionChange={setMultiSelected}> <ListboxItem key="text">Text</ListboxItem> <ListboxItem key="number">Number</ListboxItem></Listbox>含描述
<ListboxItem key="new" description="Create a new file"> New file</ListboxItem>含图标
<ListboxItem key="new" startContent={<AddIcon />}> New file</ListboxItem>含分组
<Listbox> <ListboxSection title="Actions"> <ListboxItem key="new">New file</ListboxItem> <ListboxItem key="edit">Edit file</ListboxItem> </ListboxSection> <ListboxSection title="Danger zone"> <ListboxItem key="delete">Delete</ListboxItem> </ListboxSection></Listbox>自定义选中指示器
<ListboxItem key="1" selectedIcon={<CustomCheckIcon />}> Item 1</ListboxItem>variant prop
在 v3 中,ListBox 与 ListBox.Item 都接受 variant prop,取值为 "default"(默认)或 "danger"。在根级 ListBox 上设置 variant 会作用于所有条目;在单个 ListBox.Item 上设置会覆盖该条目的根级值。
{/* Root-level variant — all items inherit "danger" styling */}<ListBox variant="danger"> <ListBox.Item id="delete" textValue="Delete"> <Label>Delete</Label> </ListBox.Item></ListBox>
{/* Per-item variant */}<ListBox> <ListBox.Item id="edit" textValue="Edit"> <Label>Edit</Label> </ListBox.Item> <ListBox.Item id="delete" textValue="Delete" variant="danger"> <Label>Delete</Label> </ListBox.Item></ListBox>onAction 事件处理函数
onAction 在条目被按下(点击或 Enter)时触发,参数为该条目的 id(类型为 Key)。
<ListBox onAction={(key) => alert(`Action on ${key}`)}> <ListBox.Item id="copy" textValue="Copy"> <Label>Copy</Label> </ListBox.Item> <ListBox.Item id="paste" textValue="Paste"> <Label>Paste</Label> </ListBox.Item></ListBox>disabledKeys
使用 disabledKeys 将特定条目设为非交互:
<ListBox disabledKeys={new Set(["paste"])}> <ListBox.Item id="copy" textValue="Copy"> <Label>Copy</Label> </ListBox.Item> <ListBox.Item id="paste" textValue="Paste"> <Label>Paste</Label> </ListBox.Item></ListBox>ListBox.Item 的渲染 prop
ListBox.Item 支持渲染 prop,可读取当前交互状态。可用字段包括 isSelected、isFocused、isDisabled、isPressed:
<ListBox selectionMode="single"> <ListBox.Item id="item1" textValue="Item 1"> {({isSelected, isFocused, isDisabled, isPressed}) => ( <> <Label className={isSelected ? "font-bold" : ""}>Item 1</Label> {isSelected && <ListBox.ItemIndicator />} </> )} </ListBox.Item></ListBox>虚拟化
v3 仍通过 React Aria 的 <Virtualizer> 支持虚拟化。用 Virtualizer 包裹 ListBox 的条目,以高效渲染长列表:
import {Virtualizer} from "react-aria-components";
<ListBox aria-label="Large list" items={items} selectionMode="multiple"> <Virtualizer> {(item) => ( <ListBox.Item id={item.id} textValue={item.name}> <Label>{item.name}</Label> </ListBox.Item> )} </Virtualizer></ListBox>组件剖析
v3 ListBox 的结构如下:
ListBox (Root) ├── ListBox.Item │ ├── Icon (optional, manual placement) │ ├── Label (required) │ ├── Description (optional) │ └── ListBox.ItemIndicator (optional) └── ListBox.Section (optional) ├── Header (optional) └── ListBox.Item总结
- 组件命名:
Listbox→ListBox,ListboxItem→ListBox.Item,ListboxSection→ListBox.Section。 - 条目结构:必须使用
Label、Description、ListBox.ItemIndicator等组件。 - 图标:不再使用
startContent/endContentprop,改为手动排版。 - 分组:使用
Header组件,不再使用 Section 的titleprop。 variantprop:variant与color合并为单一的variant("default"|"danger"),可设在ListBox与ListBox.Item上。onAction:ListBox上新增onAction,用于处理条目按下。disabledKeys:仍在ListBox上支持,用于禁用特定条目。- 渲染 prop:
ListBox.Item通过渲染 prop 提供isSelected、isFocused、isDisabled、isPressed。 - 已移除的内容 props:
topContent、bottomContent——请自行组织布局。 - 虚拟化:仍通过 React Aria
<Virtualizer>支持(替代isVirtualizedprop)。 - 选择类型:使用
Selection类型(Set),而不是数组。