Table
Table 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 v3 Table 文档。本指南只关注从 HeroUI v2 的迁移。
结构变化
在 v2 中,Table 对每个部分使用独立的命名导入:
import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from "@lenso/ui";
export default function App() { return ( <Table aria-label="Example table"> <TableHeader> <TableColumn>Name</TableColumn> <TableColumn>Role</TableColumn> </TableHeader> <TableBody> <TableRow key="1"> <TableCell>Kate Moore</TableCell> <TableCell>CEO</TableCell> </TableRow> </TableBody> </Table> );}在 v3 中,Table 使用点语法的复合组件,并新增 Table.ScrollContainer 与 Table.Content:
import { Table } from "@lenso/ui";
export default function App() { return ( <Table> <Table.ScrollContainer> <Table.Content aria-label="Example table"> <Table.Header> <Table.Column>Name</Table.Column> <Table.Column>Role</Table.Column> </Table.Header> <Table.Body> <Table.Row> <Table.Cell>Kate Moore</Table.Cell> <Table.Cell>CEO</Table.Cell> </Table.Row> </Table.Body> </Table.Content> </Table.ScrollContainer> </Table> );}主要变化
1. 组件结构
v2: 独立的命名导入(Table、TableHeader、TableColumn、TableBody、TableRow、TableCell)
v3: 单一 Table 导入配合点语法:Table、Table.ScrollContainer、Table.Content、Table.Header、Table.Column、Table.Body、Table.Row、Table.Cell、Table.Footer
2. 新增包裹层
Table:根容器(样式外层)Table.ScrollContainer:横向滚动与自定义滚动条Table.Content:实际的<table>元素(aria-label、selectionMode、sortDescriptor等放在这里)Table.Footer:替代bottomContent,用于分页等底部内容
3. Prop 变更
| v2 prop | v3 对应位置 | 说明 |
|---|---|---|
aria-label | Table.Content 的 aria-label | 移到 Table.Content |
selectionMode | Table.Content | 移到 Table.Content |
selectedKeys | Table.Content | 移到 Table.Content |
defaultSelectedKeys | Table.Content | 移到 Table.Content |
onSelectionChange | Table.Content | 移到 Table.Content |
sortDescriptor | Table.Content | 移到 Table.Content |
onSortChange | Table.Content | 移到 Table.Content |
disabledKeys | Table.Content | 移到 Table.Content |
disallowEmptySelection | Table.Content | 移到 Table.Content |
selectionBehavior | Table.Content | 移到 Table.Content |
disabledBehavior | Table.Content | 移到 Table.Content |
onRowAction | Table.Content | 移到 Table.Content |
onCellAction | Table.Content | 移到 Table.Content |
topContent | — | 放在 Table 内、Table.ScrollContainer 之前 |
bottomContent | — | 使用 Table.Footer |
topContentPlacement | — | 已移除(直接组合布局) |
bottomContentPlacement | — | 已移除(直接组合布局) |
color | — | 已移除(请用 Tailwind CSS) |
variant | Table 的 variant | 变为 "primary"(卡片式,默认)或 "secondary"(扁平) |
layout | — | 已移除 |
radius | — | 已移除(请用 Tailwind CSS) |
shadow | — | 已移除(请用 Tailwind CSS) |
isStriped | — | 已移除(请用 Tailwind CSS) |
isCompact | — | 已移除(请用 Tailwind CSS) |
isHeaderSticky | — | 已移除(请用 Tailwind CSS,例如 sticky top-0) |
fullWidth | — | 已移除(默认全宽) |
removeWrapper | — | 已移除(直接组合布局) |
hideHeader | — | 已移除(省略 Table.Header 或用 CSS) |
isVirtualized | — | 使用 React Aria 的 Virtualizer 包裹 |
maxTableHeight | — | 使用 CSS 或 Virtualizer |
rowHeight | — | 与 Virtualizer 一起使用 TableLayout |
isKeyboardNavigationDisabled | — | 已移除 |
disableAnimation | — | 已移除 |
classNames | — | 在各复合子组件上使用 className |
4. 使用 Checkbox 的选择
v2: 设置 selectionMode 后由表格自动渲染 Checkbox。
v3: 在列与行中显式使用带 slot="selection" 的 Checkbox。
5. 加载与空状态
v2: TableBody 上的 loadingState、loadingContent、emptyContent。
v3: Table.Body 上的 renderEmptyState;无限滚动加载用 Table.LoadMore。
6. 分页
v2: Table 上的 bottomContent。
v3: Table.Footer 复合组件。
7. 列宽调整
v2: 非内置能力。
v3: Table.ResizableContainer + Table.ColumnResizer 复合组件。
迁移示例
基础表格
import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from "@lenso/ui";
<Table aria-label="Users"> <TableHeader> <TableColumn>Name</TableColumn> <TableColumn>Role</TableColumn> <TableColumn>Status</TableColumn> </TableHeader> <TableBody> <TableRow key="1"> <TableCell>Kate Moore</TableCell> <TableCell>CEO</TableCell> <TableCell>Active</TableCell> </TableRow> <TableRow key="2"> <TableCell>John Doe</TableCell> <TableCell>Developer</TableCell> <TableCell>Active</TableCell> </TableRow> </TableBody></Table>动态行
const columns = [ { key: "name", label: "Name" }, { key: "role", label: "Role" },];const rows = [ { key: "1", name: "Kate", role: "CEO" }, { key: "2", name: "John", role: "Developer" },];
<Table aria-label="Users"> <TableHeader columns={columns}> {(column) => <TableColumn key={column.key}>{column.label}</TableColumn>} </TableHeader> <TableBody items={rows}> {(item) => ( <TableRow key={item.key}> {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} </TableRow> )} </TableBody></Table>选择
const [selectedKeys, setSelectedKeys] = useState(new Set(["1"]));
<Table aria-label="Users" selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys}> <TableHeader> <TableColumn>Name</TableColumn> <TableColumn>Role</TableColumn> </TableHeader> <TableBody> <TableRow key="1"> <TableCell>Kate</TableCell> <TableCell>CEO</TableCell> </TableRow> <TableRow key="2"> <TableCell>John</TableCell> <TableCell>Developer</TableCell> </TableRow> </TableBody></Table>排序
const [sortDescriptor, setSortDescriptor] = useState({ column: "name", direction: "ascending",});
<Table aria-label="Users" sortDescriptor={sortDescriptor} onSortChange={setSortDescriptor}> <TableHeader> <TableColumn key="name" allowsSorting>Name</TableColumn> <TableColumn key="role" allowsSorting>Role</TableColumn> </TableHeader> <TableBody items={sortedItems}> {(item) => ( <TableRow key={item.key}> {(columnKey) => <TableCell>{item[columnKey]}</TableCell>} </TableRow> )} </TableBody></Table>分页(底部内容)
<Table aria-label="Users" bottomContent={ <Pagination total={10} page={page} onChange={setPage} /> } bottomContentPlacement="outside"> {/* ... */}</Table>空状态
<TableBody emptyContent="No rows to display."> {[]}</TableBody>样式相关变化
v2:classNames prop
<Table classNames={{ base: "custom-base", wrapper: "custom-wrapper", table: "custom-table", thead: "custom-header", tbody: "custom-body", tr: "custom-row", th: "custom-column", td: "custom-cell", }}/>v3:直接使用 className
<Table className="custom-base"> <Table.ScrollContainer className="custom-wrapper"> <Table.Content aria-label="Table" className="custom-table"> <Table.Header className="custom-header"> <Table.Column className="custom-column">Name</Table.Column> </Table.Header> <Table.Body className="custom-body"> <Table.Row className="custom-row"> <Table.Cell className="custom-cell">Value</Table.Cell> </Table.Row> </Table.Body> </Table.Content> </Table.ScrollContainer></Table>组件结构(Anatomy)
v3 的 Table 结构如下:
Table (Root container) ├── Table.ScrollContainer (horizontal scroll) │ └── Table.Content (<table> element, aria-label, selectionMode, etc.) │ ├── Table.Header (<thead>) │ │ └── Table.Column (<th>, allowsSorting, etc.) │ │ └── Table.ColumnResizer (optional) │ └── Table.Body (<tbody>, items, renderEmptyState) │ ├── Table.Row (<tr>) │ │ └── Table.Cell (<td>) │ └── Table.LoadMore (optional, infinite scroll) │ └── Table.LoadMoreContent └── Table.Footer (optional, pagination, etc.)数据项标识
v2: React 的 key 同时用于列表调和与选择状态。
v3: 在 Table.Row 与 Table.Column 上使用 id 承载选择/排序状态;列表渲染仍保留 React 的 key。
总结
- 导入方式:由多个命名导入 → 单一
Table导入配合点语法。 - 新增包裹层:
Table.ScrollContainer与Table.Content包裹表格结构。 - prop 迁移:
aria-label、selectionMode、sortDescriptor等从Table移到Table.Content。 - 底部区域:
bottomContent→Table.Footer。 - 顶部区域:
topContent→ 放在Table内、Table.ScrollContainer之前。 - 选择用 Checkbox:由自动渲染 → 显式使用
slot="selection"的Checkbox。 - 空状态:
emptyContent→Table.Body的renderEmptyState。 - 加载:
loadingState/loadingContent→ 无限滚动场景用Table.LoadMore。 - 列宽调整:新增
Table.ResizableContainer与Table.ColumnResizer。 - 标识字段:行/列由
key表达业务标识 → 使用id。 - 样式类 prop 移除:
color、radius、shadow、isStriped、isCompact等 → 请用 Tailwind CSS。 classNames移除:在各复合子组件上使用className。