ListBox 列表框
列表框展示一组选项,并允许用户选择一个或多个。
用法
import { ListBox } from '@lenso/ui';此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";
// Adapted from HeroUI v3.2.6 list-box-default (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { descriptionStyles } from "@lenso/tokens/description";import { labelStyles } from "@lenso/tokens/label";import { Avatar, ListBox, ListBoxItem } from "@lenso/ui";
const users = [ { key: "1", textValue: "Bob", color: "blue" }, { key: "2", textValue: "Fred", color: "green" }, { key: "3", textValue: "Martha", color: "purple" },] as const;const styles = stylex.create({ root: { width: 220 }, details: { display: "flex", flexDirection: "column" },});
export function Default() { return ( <ListBox aria-label="Users" xstyle={styles.root} selectionMode="single"> {users.map((user) => ( <ListBoxItem key={user.key} itemKey={user.key} textValue={user.textValue}> <Avatar size="sm"> <Avatar.Image alt={user.textValue} src={`https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/${user.color}.jpg`} /> <Avatar.Fallback>{user.textValue[0]}</Avatar.Fallback> </Avatar> <div {...stylex.props(styles.details)}> <span {...stylex.props(labelStyles.label)}>{user.textValue}</span> <span {...stylex.props(descriptionStyles.description)}> {user.textValue.toLowerCase()}@heroui.com </span> </div> <ListBoxItem.Indicator /> </ListBoxItem> ))} </ListBox> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
组件结构
import { ListBox, Label, Description, Header } from '@lenso/ui';
export default () => ( <ListBox> <ListBox.Item> <Label /> <Description /> <ListBox.ItemIndicator /> </ListBox.Item> <ListBox.Section> <Header /> <ListBox.Item> <Label /> </ListBox.Item> </ListBox.Section> </ListBox>)示例
含禁用项
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 with-disabled-items adaptation (Apache-2.0).import { Actions } from "./actions";export function WithDisabledItems() { return <Actions disabled />;}Local adaptation source above. Derived from HeroUI v3.2.6 source.
分组选项
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 with-sections adaptation (Apache-2.0).import { Actions } from "./actions";export function WithSections() { return <Actions />;}Local adaptation source above. Derived from HeroUI v3.2.6 source.
多选
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 multi-select adaptation (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { UsersList, styles } from "./users";export function MultiSelect() { return ( <div {...stylex.props(styles.surface)}> <UsersList selectionMode="multiple" /> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
受控组件
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 controlled adaptation (Apache-2.0).import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { UsersList, styles } from "./users";export function Controlled() { const [selected, setSelected] = useState<Set<React.Key>>(new Set(["1"])); return ( <div {...stylex.props(styles.stack)}> <div {...stylex.props(styles.surface)}> <UsersList selectionMode="multiple" selectedKeys={selected} onSelectionChange={setSelected} customCheck /> </div> <p {...stylex.props(styles.muted)}> Selected: {selected.size ? [...selected].join(", ") : "None"} </p> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
虚拟滚动
ListBox 通过 Virtualizer 支持虚拟化,仅渲染视口内可见行,从而高效展示大数据集。
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 virtualization adaptation (Apache-2.0); native fixed-row window.import * as stylex from "@stylexjs/stylex";import { ListBox, ListBoxItem } from "@lenso/ui";import { Description, Label } from "./text";const first = [ "Emma", "Liam", "Olivia", "Noah", "Ava", "James", "Sophia", "Oliver", "Isabella", "Lucas", "Mia", "Ethan", "Charlotte", "Mason", "Amelia", "Logan", "Harper", "Alexander", "Ella", "Benjamin",];const last = [ "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Anderson", "Taylor", "Thomas", "Jackson", "White", "Harris", "Clark", "Lewis", "Robinson", "Walker",];const users = Array.from({ length: 1000 }, (_, index) => ({ key: index + 1, textValue: `${first[index % 20]} ${last[Math.floor(index / 20) % 20]}`, email: `${first[index % 20]!.toLowerCase()}.${last[Math.floor(index / 20) % 20]!.toLowerCase()}@acme.com`,}));const styles = stylex.create({ list: { width: 300 }, detail: { display: "flex", flexDirection: "column" },});export function Virtualization() { return ( <ListBox aria-label="Virtualized list with 1000 items" xstyle={styles.list} items={users} virtualized={{ rowHeight: 50, height: 400 }} > {(item) => ( <ListBoxItem itemKey={item.key} textValue={item.textValue}> <div {...stylex.props(styles.detail)}> <Label>{item.textValue}</Label> <Description>{users[Number(item.key) - 1]!.email}</Description> </div> <ListBoxItem.Indicator /> </ListBoxItem> )} </ListBox> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
自定义勾选图标
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 custom-check-icon adaptation (Apache-2.0).import * as stylex from "@stylexjs/stylex";import { UsersList, styles } from "./users";export function CustomCheckIcon() { return ( <div {...stylex.props(styles.surface)}> <UsersList selectionMode="multiple" customCheck /> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
渲染函数
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 render-function adaptation (Apache-2.0).import { UsersList, styles } from "./users";export function RenderFunction() { return ( <UsersList xstyle={styles.list} selectionMode="single" render={(props) => <div {...props} data-custom="true" />} renderItems /> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
自定义样式
Tailwind CSS
此预览复用英文版适配,不代表中文源示例已完成本地实现。
"use client";// HeroUI v3.2.6 custom-styles adaptation (Apache-2.0).import { UsersList, styles } from "./users";export function CustomStyles() { return ( <UsersList aria-label="Assignee" xstyle={styles.custom} selectionMode="single" customStyles count={2} /> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
全局 CSS
若要自定义组件类,可使用 @layer components 指令。了解更多。
@layer components { .list-box { @apply rounded-lg border border-border bg-surface p-2; }
.list-box-item { @apply rounded px-2 py-1 cursor-pointer; }
.list-box-item--danger { @apply text-danger; }
.list-box-item__indicator { @apply text-accent; }}样式参考
HeroUI 遵循 BEM 方法论,确保组件变体与状态可复用且易于自定义。
CSS 类
ListBox 组件使用以下 CSS 类(查看源码样式):
基础类 [!toc]
.list-box- ListBox 根容器.list-box-item- 单个列表项.list-box-item__indicator- 选中指示图标.list-box-section- 用于分组的区块容器
变体类 [!toc]
.list-box--default- 默认变体样式.list-box--danger- 危险变体样式.list-box-item--default- 列表项默认变体.list-box-item--danger- 列表项危险变体
状态类 [!toc]
.list-box-item[data-selected="true"]- 选中状态.list-box-item[data-focus-visible="true"]- 聚焦状态.list-box-item[data-disabled="true"]- 禁用状态.list-box-item__indicator[data-visible="true"]- 指示器可见状态
交互状态
该组件同时支持 CSS 伪类与 data 属性:
- 悬停:列表项上
:hover或[data-hovered="true"] - 聚焦:列表项上
:focus-visible或[data-focus-visible="true"] - 已选中:列表项上
[data-selected="true"] - 禁用:列表项上
:disabled或[data-disabled="true"]
API 参考
ListBox
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
aria-label | string | - | ListBox 的无障碍标签。 |
aria-labelledby | string | - | 标注 ListBox 的元素 id。 |
selectionMode | "none" | "single" | "multiple" | "single" | 选择行为。 |
selectedKeys | Selection | - | 受控的选中 key。 |
defaultSelectedKeys | Selection | - | 初始选中 key。 |
onSelectionChange | (keys: Selection) => void | - | 选中变化时调用的事件处理函数。 |
disabledKeys | Iterable<Key> | - | 禁用项的 key。 |
onAction | (key: Key) => void | - | 激活某项时调用的事件处理函数。 |
variant | "default" | "danger" | "default" | 视觉变体。 |
className | string | - | 额外的 Tailwind CSS 类。 |
children | ReactNode | - | ListBox 项与分组。 |
render | DOMRenderFunction<keyof React.JSX.IntrinsicElements, ListBoxRenderProps> | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
ListBox.Item
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
id | Key | - | 列表项唯一标识。 |
textValue | string | - | 用于无障碍与首字母导航的文本值。 |
isDisabled | boolean | false | 是否禁用该项。 |
variant | "default" | "danger" | "default" | 视觉变体。 |
className | string | - | 额外的 Tailwind CSS 类。 |
children | ReactNode | RenderFunction | - | 列表项内容或渲染函数。 |
render | (props: DetailedHTMLProps<LinkWithRequiredHref, HTMLAnchorElement> | React.JSX.IntrinsicElements[keyof React.JSX.IntrinsicElements], renderProps: ListBoxItemRenderProps) => ReactElement | - | 使用自定义渲染函数覆盖默认 DOM 元素。 |
ListBox.ItemIndicator
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
className | string | - | 额外的 Tailwind CSS 类。 |
children | ReactNode | RenderFunction | - | 自定义指示器内容或渲染函数。 |
ListBox.Section
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
className | string | - | 额外的 Tailwind CSS 类。 |
children | ReactNode | - | 分组内容,包含 Header 与列表项。 |
RenderProps
在 ListBox.Item 或 ListBox.ItemIndicator 中使用渲染函数时,会传入以下值:
| Prop | 类型 | 描述 |
|---|---|---|
isSelected | boolean | 该项是否选中。 |
isFocused | boolean | 该项是否聚焦。 |
isDisabled | boolean | 该项是否禁用。 |
isPressed | boolean | 该项是否处于按下状态。 |
ListLayout
| Name | 类型 | 默认值 | 描述 |
|---|---|---|---|
rowHeight | number | undefined | 48 | 行固定高度(px)。 |
estimatedRowHeight | number | undefined | — | 行高可变时的估算高度。 |
headingHeight | number | undefined | 48 | 分组标题固定高度(px)。 |
estimatedHeadingHeight | number | undefined | — | 标题高度可变时的估算高度。 |
loaderHeight | number | undefined | 48 | 加载器元素固定高度(px)。该加载器用于在根级或嵌套行/分组中渲染「加载更多」等内容。 |
dropIndicatorThickness | number | undefined | 2 | 放置指示线厚度。 |
gap | number | undefined | 0 | 项之间的间距。 |
padding | number | undefined | 0 | 列表内边距。 |
示例
基本用法
import { ListBox, Label, Description } from '@lenso/ui';
<ListBox aria-label="Users" selectionMode="single"> <ListBox.Item id="1" textValue="Bob"> <Label>Bob</Label> <Description>[email protected]</Description> </ListBox.Item> <ListBox.Item id="2" textValue="Alice"> <Label>Alice</Label> <Description>[email protected]</Description> </ListBox.Item></ListBox>分组选项
import { ListBox, Header, Separator } from '@lenso/ui';
<ListBox aria-label="Actions" selectionMode="none" onAction={(key) => console.log(key)}> <ListBox.Section> <Header>Actions</Header> <ListBox.Item id="new" textValue="New file">New file</ListBox.Item> <ListBox.Item id="edit" textValue="Edit file">Edit file</ListBox.Item> </ListBox.Section> <Separator /> <ListBox.Section> <Header>Danger zone</Header> <ListBox.Item id="delete" textValue="Delete" variant="danger">Delete</ListBox.Item> </ListBox.Section></ListBox>受控选择
import { ListBox, Selection } from '@lenso/ui';import { useState } from 'react';
function ControlledListBox() { const [selected, setSelected] = useState<Selection>(new Set(["1"]));
return ( <ListBox aria-label="Options" selectedKeys={selected} selectionMode="multiple" onSelectionChange={setSelected} > <ListBox.Item id="1" textValue="Option 1">Option 1</ListBox.Item> <ListBox.Item id="2" textValue="Option 2">Option 2</ListBox.Item> <ListBox.Item id="3" textValue="Option 3">Option 3</ListBox.Item> </ListBox> );}自定义指示器
import { ListBox, ListBoxItemIndicator } from '@lenso/ui';import { Icon } from '@iconify/react';
<ListBox aria-label="Options" selectionMode="multiple"> <ListBox.Item id="1" textValue="Option 1"> Option 1 <ListBox.ItemIndicator> {({isSelected}) => isSelected ? <Icon icon="gravity-ui:check" /> : null } </ListBox.ItemIndicator> </ListBox.Item></ListBox>无障碍
ListBox 组件实现 ARIA listbox 模式,并提供:
- 完整键盘导航支持
- 屏幕阅读器对选中变化的播报
- 合理的焦点管理
- 禁用状态支持
- 首字母导航(typeahead)搜索能力
更多信息见 React Aria ListBox 文档。