| 作品 | 品牌 | 发售日 |
|---|---|---|
| ATRI -My Dear Moments- | ANIPLEX.EXE | 2020-06-19 |
| Summer Pockets | Key | 2018-06-29 |
| サクラノ詩 | 枕 | 2015-10-23 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
const games = [
{ title: 'ATRI -My Dear Moments-', brand: 'ANIPLEX.EXE', date: '2020-06-19' },
{ title: 'Summer Pockets', brand: 'Key', date: '2018-06-29' },
{ title: 'サクラノ詩', brand: '枕', date: '2015-10-23' },
]
export default function Demo() {
return (
<Table caption="收录的部分作品" className="w-full max-w-xl">
<TableHeader>
<TableRow>
<TableHead>作品</TableHead>
<TableHead>品牌</TableHead>
<TableHead align="end">发售日</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{games.map(game => (
<TableRow key={game.title}>
<TableCell>{game.title}</TableCell>
<TableCell>{game.brand}</TableCell>
<TableCell align="end">{game.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
用法
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
表格由六个组件组成,与 HTML 表格的结构一一对应:Table 是根,TableHeader 与 TableBody 是表头区和数据区,TableRow 是行,TableHead 是表头单元格,TableCell 是数据单元格。除 Table、TableHead 和 TableCell 之外的组件只接受 className。
Table 自带横向滚动容器,表格宽度超出时在容器内滚动,不会撑破所在布局。
| 卷次 | 页数 | 发售日 |
|---|---|---|
| 第一卷 | 328 | 2021-03-10 |
| 第二卷 | 304 | 2021-09-10 |
| 第三卷 | 352 | 2022-02-10 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
export default function Demo() {
return (
<Table className="w-full max-w-lg">
<TableHeader>
<TableRow>
<TableHead>卷次</TableHead>
<TableHead>页数</TableHead>
<TableHead>发售日</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>第一卷</TableCell>
<TableCell>328</TableCell>
<TableCell>2021-03-10</TableCell>
</TableRow>
<TableRow>
<TableCell>第二卷</TableCell>
<TableCell>304</TableCell>
<TableCell>2021-09-10</TableCell>
</TableRow>
<TableRow>
<TableCell>第三卷</TableCell>
<TableCell>352</TableCell>
<TableCell>2022-02-10</TableCell>
</TableRow>
</TableBody>
</Table>
)
}
本组件只负责呈现。需要列定义、排序、筛选、分页或行选择时,使用 DataTable。
示例
形态
primary 是带底色、边框与投影的面板,适合独立成块的表格;secondary 去掉边框与投影,只保留表头底色,适合已经位于卡片或面板内、不需要再嵌一层容器的场景。默认为 primary。
primary
| 角色 | 声优 |
|---|---|
| 夏目 佳月 | 藤咲ウサ |
| 斑鳩 千鹤 | 御苑生メイ |
secondary
| 角色 | 声优 |
|---|---|
| 夏目 佳月 | 藤咲ウサ |
| 斑鳩 千鹤 | 御苑生メイ |
import {
Stack,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Text,
} from '@hina-ui/react'
const rows = [
{ name: '夏目 佳月', cv: '藤咲ウサ' },
{ name: '斑鳩 千鹤', cv: '御苑生メイ' },
]
export default function Demo() {
return (
<Stack gap="lg" className="w-full max-w-lg">
<Stack gap="sm">
<Text size="sm" tone="faint">
primary
</Text>
<Table variant="primary">
<TableHeader>
<TableRow>
<TableHead>角色</TableHead>
<TableHead>声优</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.cv}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Stack>
<Stack gap="sm">
<Text size="sm" tone="faint">
secondary
</Text>
<Table variant="secondary">
<TableHeader>
<TableRow>
<TableHead>角色</TableHead>
<TableHead>声优</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.cv}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Stack>
</Stack>
)
}
对齐
align 控制单元格内容的对齐方向,可选 start、center 和 end,默认为 start。数值列通常右对齐,便于按位比较;表头与数据列的对齐方向应当一致。
表格已启用等宽数字,同列数字的字符宽度相同,不会因字形差异而参差。
| 卷次 | 章节数 | 评分 |
|---|---|---|
| 第一卷 | 12 | 8.6 |
| 第二卷 | 9 | 9.1 |
| 第三卷 | 14 | 8.8 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
const volumes = [
{ name: '第一卷', chapters: 12, rating: '8.6' },
{ name: '第二卷', chapters: 9, rating: '9.1' },
{ name: '第三卷', chapters: 14, rating: '8.8' },
]
export default function Demo() {
return (
<Table className="w-full max-w-lg">
<TableHeader>
<TableRow>
<TableHead>卷次</TableHead>
<TableHead align="center">章节数</TableHead>
<TableHead align="end">评分</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{volumes.map(volume => (
<TableRow key={volume.name}>
<TableCell>{volume.name}</TableCell>
<TableCell align="center">{volume.chapters}</TableCell>
<TableCell align="end">{volume.rating}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
表格标题
caption 在表格上方显示一行说明文字,同时作为表格的可访问名被辅助技术读出。横向滚动时它保持在起始边可见。
| 卷次 | 发售日 |
|---|---|
| 第一卷 | 2021-03-10 |
| 第二卷 | 2021-09-10 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
export default function Demo() {
return (
<Table caption="单行本发售一览" className="w-full max-w-lg">
<TableHeader>
<TableRow>
<TableHead>卷次</TableHead>
<TableHead>发售日</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>第一卷</TableCell>
<TableCell>2021-03-10</TableCell>
</TableRow>
<TableRow>
<TableCell>第二卷</TableCell>
<TableCell>2021-09-10</TableCell>
</TableRow>
</TableBody>
</Table>
)
}
行悬停
数据行默认在悬停时浮现一层浅色状态层,用于在宽表中跟住视线。若表格用于陈列固定信息、行本身不可交互,可将 hover 设为 false 关闭。
| 项目 | 内容 |
|---|---|
| 发行方式 | DL 版 / 实体版 |
| 游戏时长 | 约 20 小时 |
| 语言 | 日文 / 简体中文 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
const specs = [
{ key: '发行方式', value: 'DL 版 / 实体版' },
{ key: '游戏时长', value: '约 20 小时' },
{ key: '语言', value: '日文 / 简体中文' },
]
export default function Demo() {
return (
<Table hover={false} className="w-full max-w-lg">
<TableHeader>
<TableRow>
<TableHead>项目</TableHead>
<TableHead>内容</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{specs.map(spec => (
<TableRow key={spec.key}>
<TableCell>{spec.key}</TableCell>
<TableCell>{spec.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
行表头
当每一行的首列是该行的名称而非数据时,用 TableHead 并设 scope="row" 代替 TableCell。屏幕阅读器据此把该单元格作为整行的标题读出,读到后续单元格时会先播报它。
| 平台 | 发售日 | 定价 |
|---|---|---|
| Windows | 2020-06-19 | 3,278 円 |
| Nintendo Switch | 2022-03-24 | 4,180 円 |
| PlayStation 4 | 2022-03-24 | 4,180 円 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
const rows = [
{ platform: 'Windows', release: '2020-06-19', price: '3,278 円' },
{ platform: 'Nintendo Switch', release: '2022-03-24', price: '4,180 円' },
{ platform: 'PlayStation 4', release: '2022-03-24', price: '4,180 円' },
]
export default function Demo() {
return (
<Table className="w-full max-w-xl">
<TableHeader>
<TableRow>
<TableHead>平台</TableHead>
<TableHead>发售日</TableHead>
<TableHead align="end">定价</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(row => (
<TableRow key={row.platform}>
<TableHead scope="row">{row.platform}</TableHead>
<TableCell>{row.release}</TableCell>
<TableCell align="end">{row.price}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
吸顶表头
stickyHeader 让表头在纵向滚动时固定在容器顶缘。它需要配合高度限制使用,否则表格不会产生纵向滚动。开启后滚动容器同时接管纵横两个方向。
| 话数 | 标题 | 更新日 |
|---|---|---|
| 1 | 第 1 话 | 2024-01-15 |
| 2 | 第 2 话 | 2024-01-15 |
| 3 | 第 3 话 | 2024-01-15 |
| 4 | 第 4 话 | 2024-01-15 |
| 5 | 第 5 话 | 2024-02-15 |
| 6 | 第 6 话 | 2024-02-15 |
| 7 | 第 7 话 | 2024-02-15 |
| 8 | 第 8 话 | 2024-02-15 |
| 9 | 第 9 话 | 2024-03-15 |
| 10 | 第 10 话 | 2024-03-15 |
| 11 | 第 11 话 | 2024-03-15 |
| 12 | 第 12 话 | 2024-03-15 |
| 13 | 第 13 话 | 2024-04-15 |
| 14 | 第 14 话 | 2024-04-15 |
| 15 | 第 15 话 | 2024-04-15 |
| 16 | 第 16 话 | 2024-04-15 |
| 17 | 第 17 话 | 2024-05-15 |
| 18 | 第 18 话 | 2024-05-15 |
| 19 | 第 19 话 | 2024-05-15 |
| 20 | 第 20 话 | 2024-05-15 |
| 21 | 第 21 话 | 2024-06-15 |
| 22 | 第 22 话 | 2024-06-15 |
| 23 | 第 23 话 | 2024-06-15 |
| 24 | 第 24 话 | 2024-06-15 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
const episodes = Array.from({ length: 24 }, (_, index) => ({
no: index + 1,
title: `第 ${index + 1} 话`,
date: `2024-${String(Math.floor(index / 4) + 1).padStart(2, '0')}-15`,
}))
export default function Demo() {
return (
<Table stickyHeader className="max-h-72 w-full max-w-lg">
<TableHeader>
<TableRow>
<TableHead>话数</TableHead>
<TableHead>标题</TableHead>
<TableHead align="end">更新日</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{episodes.map(episode => (
<TableRow key={episode.no}>
<TableCell>{episode.no}</TableCell>
<TableCell>{episode.title}</TableCell>
<TableCell align="end">{episode.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
吸附首列
列数较多时,在首列的 TableHead 与 TableCell 上加 sticky,横向滚动时该列固定在起始边,其余列从它下方滑过。固定列自带实底与右侧分界线,滚动时不会与后面的内容重叠。
同一列的表头与数据单元格都要加 sticky,只加一处会导致该列在滚动时错位。
| 作品 | 品牌 | 发售日 | 平台 | 语言 | 时长 | 评分 |
|---|---|---|---|---|---|---|
| ATRI -My Dear Moments- | ANIPLEX.EXE | 2020-06-19 | Windows | 日文 / 简体中文 | 约 20 小时 | 8.9 |
| Summer Pockets | Key | 2018-06-29 | Windows | 日文 | 约 40 小时 | 8.5 |
| サクラノ詩 | 枕 | 2015-10-23 | Windows | 日文 | 约 50 小时 | 9.0 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
const works = [
{
title: 'ATRI -My Dear Moments-',
brand: 'ANIPLEX.EXE',
date: '2020-06-19',
platform: 'Windows',
language: '日文 / 简体中文',
length: '约 20 小时',
rating: '8.9',
},
{
title: 'Summer Pockets',
brand: 'Key',
date: '2018-06-29',
platform: 'Windows',
language: '日文',
length: '约 40 小时',
rating: '8.5',
},
{
title: 'サクラノ詩',
brand: '枕',
date: '2015-10-23',
platform: 'Windows',
language: '日文',
length: '约 50 小时',
rating: '9.0',
},
]
export default function Demo() {
return (
<Table className="w-full max-w-lg [&_td]:whitespace-nowrap [&_th]:whitespace-nowrap">
<TableHeader>
<TableRow>
<TableHead sticky>作品</TableHead>
<TableHead>品牌</TableHead>
<TableHead>发售日</TableHead>
<TableHead>平台</TableHead>
<TableHead>语言</TableHead>
<TableHead>时长</TableHead>
<TableHead align="end">评分</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{works.map(work => (
<TableRow key={work.title}>
<TableCell sticky>{work.title}</TableCell>
<TableCell>{work.brand}</TableCell>
<TableCell>{work.date}</TableCell>
<TableCell>{work.platform}</TableCell>
<TableCell>{work.language}</TableCell>
<TableCell>{work.length}</TableCell>
<TableCell align="end">{work.rating}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
密度
行高取自密度变量,在容器上设置 data-density="compact" 即可整体压缩,表格本身不需要改动。用于信息密集的列表页。
comfortable(默认)
| 卷次 | 发售日 |
|---|---|
| 第一卷 | 2021-03-10 |
| 第二卷 | 2021-09-10 |
| 第三卷 | 2022-02-10 |
compact
| 卷次 | 发售日 |
|---|---|
| 第一卷 | 2021-03-10 |
| 第二卷 | 2021-09-10 |
| 第三卷 | 2022-02-10 |
import {
Stack,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Text,
} from '@hina-ui/react'
const rows = [
{ name: '第一卷', date: '2021-03-10' },
{ name: '第二卷', date: '2021-09-10' },
{ name: '第三卷', date: '2022-02-10' },
]
export default function Demo() {
return (
<Stack gap="lg" className="w-full max-w-lg">
<Stack gap="sm">
<Text size="sm" tone="faint">
comfortable(默认)
</Text>
<Table>
<TableHeader>
<TableRow>
<TableHead>卷次</TableHead>
<TableHead align="end">发售日</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell align="end">{row.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Stack>
<Stack gap="sm" data-density="compact">
<Text size="sm" tone="faint">
compact
</Text>
<Table>
<TableHeader>
<TableRow>
<TableHead>卷次</TableHead>
<TableHead align="end">发售日</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell align="end">{row.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Stack>
</Stack>
)
}
无障碍
- 结构使用原生表格元素,屏幕阅读器可按行列关系导航,并播报所在的行列位置。
TableHead的scope默认为col,声明该单元格是所在列的表头;行表头需显式设为row。caption是表格的可访问名。表格前后若已有标题文字,可用它复述,避免辅助技术只读到一个无名表格。- 对齐方式与行悬停都不改变朗读顺序。
API
Table
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'primary' | 形态 |
hover | boolean | true | 数据行是否在悬停时浮现状态层 |
stickyHeader | boolean | false | 表头是否在纵向滚动时固定于顶缘 |
caption | string | — | 表格标题,同时作为可访问名 |
className | string | — | 追加到滚动容器上的类 |
| 属性 | 说明 |
|---|---|
children | 表格内容,通常是 TableHeader 与 TableBody |
TableHead
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
scope | 'col' | 'row' | 'colgroup' | 'rowgroup' | 'col' | 该表头单元格描述的是列还是行 |
align | 'start' | 'center' | 'end' | 'start' | 内容对齐方向 |
sticky | boolean | false | 横向滚动时是否固定在起始边 |
className | string | — | 追加到根元素的类 |
TableCell
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
align | 'start' | 'center' | 'end' | 'start' | 内容对齐方向 |
sticky | boolean | false | 横向滚动时是否固定在起始边 |
className | string | — | 追加到根元素的类 |
TableHeader、TableBody 与 TableRow
三者分别渲染 thead、tbody 和 tr,只接受 className,内容通过 children 传入。