Table 表格

按行列组织的静态数据表。

收录的部分作品
作品品牌发售日
ATRI -My Dear Moments-ANIPLEX.EXE2020-06-19
Summer PocketsKey2018-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>
  )
}
tsx

用法

import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@hina-ui/react'
ts

表格由六个组件组成,与 HTML 表格的结构一一对应:Table 是根,TableHeader 与 TableBody 是表头区和数据区,TableRow 是行,TableHead 是表头单元格,TableCell 是数据单元格。除 Table、TableHead 和 TableCell 之外的组件只接受 className。

Table 自带横向滚动容器,表格宽度超出时在容器内滚动,不会撑破所在布局。

卷次页数发售日
第一卷3282021-03-10
第二卷3042021-09-10
第三卷3522022-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>
  )
}
tsx

本组件只负责呈现。需要列定义、排序、筛选、分页或行选择时,使用 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>
  )
}
tsx

对齐

align 控制单元格内容的对齐方向,可选 start、center 和 end,默认为 start。数值列通常右对齐,便于按位比较;表头与数据列的对齐方向应当一致。

表格已启用等宽数字,同列数字的字符宽度相同,不会因字形差异而参差。

卷次章节数评分
第一卷128.6
第二卷99.1
第三卷148.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>
  )
}
tsx

表格标题

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>
  )
}
tsx

行悬停

数据行默认在悬停时浮现一层浅色状态层,用于在宽表中跟住视线。若表格用于陈列固定信息、行本身不可交互,可将 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>
  )
}
tsx

行表头

当每一行的首列是该行的名称而非数据时,用 TableHead 并设 scope="row" 代替 TableCell。屏幕阅读器据此把该单元格作为整行的标题读出,读到后续单元格时会先播报它。

平台发售日定价
Windows2020-06-193,278 円
Nintendo Switch2022-03-244,180 円
PlayStation 42022-03-244,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>
  )
}
tsx

吸附首列

列数较多时,在首列的 TableHead 与 TableCell 上加 sticky,横向滚动时该列固定在起始边,其余列从它下方滑过。固定列自带实底与右侧分界线,滚动时不会与后面的内容重叠。

同一列的表头与数据单元格都要加 sticky,只加一处会导致该列在滚动时错位。

作品品牌发售日平台语言时长评分
ATRI -My Dear Moments-ANIPLEX.EXE2020-06-19Windows日文 / 简体中文约 20 小时8.9
Summer PocketsKey2018-06-29Windows日文约 40 小时8.5
サクラノ詩枕2015-10-23Windows日文约 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>
  )
}
tsx

密度

行高取自密度变量,在容器上设置 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>
  )
}
tsx

无障碍

  • 结构使用原生表格元素,屏幕阅读器可按行列关系导航,并播报所在的行列位置。
  • TableHead 的 scope 默认为 col,声明该单元格是所在列的表头;行表头需显式设为 row。
  • caption 是表格的可访问名。表格前后若已有标题文字,可用它复述,避免辅助技术只读到一个无名表格。
  • 对齐方式与行悬停都不改变朗读顺序。

API

Table

属性
类型
默认值
说明
variant
'primary' | 'secondary'
'primary'
形态
hover
boolean
true
数据行是否在悬停时浮现状态层
stickyHeader
boolean
false
表头是否在纵向滚动时固定于顶缘
caption
string
—
表格标题,同时作为可访问名
className
string
—
追加到滚动容器上的类
属性
说明
children
表格内容,通常是 TableHeader 与 TableBody

TableCell

属性
类型
默认值
说明
align
'start' | 'center' | 'end'
'start'
内容对齐方向
sticky
boolean
false
横向滚动时是否固定在起始边
className
string
—
追加到根元素的类

TableHeader、TableBody 与 TableRow

三者分别渲染 thead、tbody 和 tr,只接受 className,内容通过 children 传入。