反应-数据表
故事书: anthony-liddle.github.io/反应数据表。每个功能都是交互式演示.
npm install @anthony-liddle/react-data-table
用于管理和报告屏幕的数据密集反应表。 取自出品应用软件并被作为独立包出版.
特征
- 排序:单击一个页眉来循环向上 ~ 下 ~ 关闭,选择使用 Shift+ 点击或 Shift+ Enter 的多列排序 。 排序页眉为键盘操作按钮并报告
aria-sort。 。 。 . - 分组列:父头横跨子列,并有两行头.
- 行选择: 复选框, 选择全部, 任意点击, 以及无法选择的行 .
- 拖动选中区域:按一行并拖曳以选择范围.
- 栏管理:通过鼠标或键盘,显示、隐藏并重排来自头板的列.
- 倾斜:有范围文字和可选"Page N of M"的页脚,外加用于偏移和光标API的钩子.
- 持久性: 列布局,排序,并被写入到 URL (可共享) 和 localStorage (生存重载) 的过滤器.
- 另外: 有子菜单的行动作菜单,装入骨架,可扩展行,和可压倒性的标签.
只有无害环境管理,严格的类型脚本,反应18或19。 不需要"尾风":包装自有CSS.
从早期版本升级吗 ? 移民.md 列出每笔折叠的更改以及该做什么.
设置
一次导入样式表, 例如在根版式中 :
import '@anthony-liddle/react-data-table/styles.css';
这就是整个设置。 桌子不需要尾风 也不需要自己的阶梯.
利用尾风,或任何级联层 你自己的? 在命令分层的行后, 从您的 CSS 条目中导入分层复制 。 在尾风之间 base 财务报告和审定财务报表 utilities,飞行前离开桌子 和一个工具通过 className 赢 :
@layer theme, base, react-data-table, components, utilities;
@import "tailwindcss";
@import "@anthony-liddle/react-data-table/styles.layer.css";
在此导入,而不是从JavaScript导入,所以命令行总是先来. 在它被命令之前使用的一层会往下走,在Tailwind的飞行前,桌子会失去它的垫和边框. 与无上层 styles.css,Tailwind的公用设备(它坐在一层)不能覆盖表格,所以分层的拷贝是用Tailwind使用的.
styles.layer.css 规则与 styles.css 内部 @layer react-data-table。见 样式 以便他们超越一切.
快开始
import { DataTable, type TableColumn } from '@anthony-liddle/react-data-table';
interface Member {
id: string;
name: string;
email: string;
team: string;
}
const columns: TableColumn<Member>[] = [
{ id: 'name', header: 'Name', sortKey: 'name', cell: ({ row }) => row.name },
{ id: 'email', header: 'Email', cell: ({ row }) => row.email },
{ id: 'team', header: 'Team', sortKey: 'team', cell: ({ row }) => row.team, hideByDefault: true },
];
export function Members({ members }: { members: Member[] }) {
return <DataTable columns={columns} data={members} persistKey="members" />;
}
桌子已经控制住了 您拥有数据,类型,选择,和页面,下面的钩子管理通常的大小写. 见 专题故事 每条线都会结束.
DataTable 道具
| Prop | 类型 | 默认 | |
|---|---|---|---|
columns | TableColumn<T>[] | 所需经费 | 栏定义(见下文) |
data | T[] | 所需经费 | 以行为相. 每个人需要一个独特的 id。 。 。 . |
sort | TableSort | – 说吧 – | { sortBy, onSort }:按优先排序的列,并单击处理器. useTableSort 返回它. |
selection | TableSelection<T> | – 说吧 – | 选择状态和处理者; useTableSelection 返回它。 启用复选框并拖曳选择; Shift+单击复选框, 或Shift+ 空间在一个,设置一个范围. |
selectOnRowClick | boolean | false | 点击一行来切换它, 除了它内部的控件( 见 onRowClick(中文(简体) ) |
pagination | TablePagination | – 说吧 – | 显示页脚 |
showPageCount | boolean | false | 在页脚上添加“ Page N of M” 和边框控制 |
actionMenu | TableActionMenuItem<T>[] | – 说吧 – | 每行菜单:链接、回调、一级子菜单、每行 hidden 编号 : disabled |
persistKey | string | – 说吧 – | 在本地Storage中保存列能见度和顺序; 给钩子相同的密钥( 见 持久性(中文(简体) ) |
hideTableSettings | boolean | false | 隐藏列设置面板。 每根柱子被钉住时,也会被漏出(如:alwaysVisible(中文(简体) ) |
onColumnReorder | (order: string[]) => void | – 说吧 – | 用新列编号顺序调用 |
onVisibleColumnsChange | (columns: VisibleColumn[]) => void | – 说吧 – | 当列显示或隐藏时调用 |
loading | boolean | false | 显示骨架行和套装 aria-busy |
maxLoadingRows | number | 10 | 斯凯莱通行数 |
onRowClick | (row: T) => void | – 说吧 – | 行点击回调。 点击行内的控件(链接、按钮、窗体控制、标签、, [role=button], (中文(简体) ), [role=checkbox], (中文(简体) ), [role=menuitem] 或可编辑的内容),或从单元格中打开的门户中,不要计数. 在行主单元格中为键盘用户设置链接对等(参见 无障碍(中文(简体) ) |
renderExpandedRow | (row: T) => ReactNode | – 说吧 – | 一行的扩大面板的内容, 下方的一行全宽. 添加每行有扩展按钮的首列。 仅用于扩展行 |
expansion | TableExpansion<T> | – 说吧 – | 扩大哪些行; useTableExpansion 返回它。 没有它,表保持状态本身 |
expandOnRowClick | boolean | false | 点击一行来切换它的面板, 除了其中的控制器 |
rowClassName | (row: T) => string | – 说吧 – | 每类额外 <tr> |
getRowTestId | (row: T) => string | undefined | – 说吧 – | 每行数 data-testid 用于端到端测试 |
getRowLabel | (row: T) => string | – 说吧 – | 命名辅助技术的每行:它的复选框为"Select QQ labelXX",它的菜单为"Actions for QQ labelXX",它的扩展按钮为"Details for QQ labelX". 没有它,行名由 labels.rowLabel,"行地","行地". |
labels | Partial<DataTableLabels> | 联合国 | 覆盖表格制作或宣布的任何字符串(见 标签(中文(简体) ) |
linkComponent | ComponentType | 'a' | 'a' | 渲染器 href 菜单项目,例如路由器 Link。它必须将它不使用的道具分散到主机上,并转发其参考文献,而菜单需要这些参考文献来聚焦键盘 |
列数
interface TableColumn<T> {
id: string; // required, unique in the table, and stable across header text and translations
header: string; // header text
sortKey?: string; // makes the column sortable
cell?: ({ row }) => ReactNode;
loadingCell?: () => ReactNode;
headerIcon?: ReactNode;
headerClassName?: string; // header cell classes
cellClassName?: string; // body cell classes
subColumns?: TableColumn<T>[]; // makes this a grouped parent column
hideByDefault?: boolean; // starts hidden; can be shown from the panel
alwaysVisible?: boolean; // pinned: can't be hidden or moved, and stays out of the column panel
}
钩子
| 钩子 | 回返 | 使用 |
|---|---|---|
useTableSort({ sortableColumns, defaultSortBy, multiSort, persistKey, onChange }) | { sortBy, onSort, setSortBy, reset, isRestored } | 递给 sort 道具是; 发送 sortBy 您的查询 |
useTableSelection(rows, { isRowDisabled }) | { selectedRows, onToggleRow, onToggleAll, clearSelection, … } | 递给 selection 道具的原样 |
useTableExpansion({ isRowExpandable, initialExpandedIds }) | { isRowExpanded, onToggleRow, expandedIds, setExpandedIds, collapseAll, … } | 递给 expansion 道具,保持行开 横穿一个回放或打开一个从外面 |
usePagination({ pageSize, persistKey }) | { offset, limit, pageIndex, getPagination, reset, isRestored, … } | 偏移或页码 API 和数组: 发送 offset 编号 : limit,通过 getPagination(totalCount) 页:1 pagination 道具 |
useCursorPagination({ pageSize }) | { variables, pageIndex, getPagination, reset } | 图QL 连接: 发送 variables (单位:千美元){ first, after })与查询,通过 getPagination(pageInfo, totalCount?) 页:1 pagination 道具,带有页面信息 variables 请求 |
usePersistedFilters(key, filterDefs, { persistToStorage }) | { filters, setFilter, reset, isRestored } | 任何过滤状态, 像表格一样持续 |
sortBy 按优先顺序列出排序的列,每个 { key, direction } 小写 'asc' 或者说 'desc'; 未排序为空列表 。 如果您的API想要大写语句, 请在呼叫中转换 . reset() 返回到 defaultSortBy,以及 setSortBy([]) 清除了类型.
与 multiSort: true,在标题上单击 Shift+(或Shift+Enter或Shift+) 焦点上的空格)添加了该列作为下个排序的密钥,再翻转,再去掉,将其它的留入原位. 简单的点击仍然使列成为唯一的类型。 每个被排序的一栏显示一个带有位置的小数字,而两个或两个以上被排序. 只有在您的 API 可以按多个字段排序时才会打开 .
将选择保留在自己的状态而不是 useTableSelection,通过 isRowSelected, (中文(简体) ), onToggleRow 财务报告和审定财务报表 onToggleAll 页:1 selection 贴出你自己。 Shift 范围或拖动调用 onToggleRow 每行都会改变一次,在一个单一的事件中,所以从上一个值更新状态(setSelected(prev => ...)) (中文(简体) ). 否则,只有最后一行棍子.
usePagination 手拿回来 offset 财务报告和审定财务报表 limit 用于当前页面。 总行数通常随该页而来,所以表格的 pagination 道具是在制取时从道具上建造的:
const { offset, limit, getPagination, reset } = usePagination({ pageSize: 25 });
const { items, total } = useMembers({ offset, limit }); // your fetch, or rows.slice(offset, offset + limit)
<DataTable columns={columns} data={items} pagination={getPagination(total)} />;
打电话 reset() 当排序或过滤器改变时,表格会回到第一页. useTableSort是因为 onChange 运行在每一个类型的变化 除了恢复一个持续, 所以它是的地方: useTableSort({ onChange: () => reset() })。 。 。 .
TablePagination.totalCount 是可选的,因为光标 API 往往没有总计。 没有它,页脚读作"Showing 11 -20",以页行数结束("Showing 11 -20")DataTable 供给它;一个独立者 Pagination 带 pageRowCount),它降低了"Page N of M",接下来是 hasNextPage。负或非无限总数,如未知的API's -1,算作无. usePagination 工作出来 hasNextPage 从总数,所以它需要真正的。 总数不详, 使用 useCursorPagination 用于光标 API,或构建 pagination 用你的API自己来做 hasNextPage。没有行的一页读作“显示0-0”.
标签
表格中的每个字符串都来自 labels,所以一个翻译也覆盖了屏幕阅读器的名称. 将任何子集传递到 DataTable 或者说 Pagination;其余则保留其英语默认值,即 defaultLabels 出口.
| 密钥 | 默认 |
|---|---|
noResults | 找不到结果 |
showingRangeOf({ start, end, total }) | 显示 { start} -{end} 的结果 |
selectAll | 全部选择 |
selectRow({ rowLabel }) | 选择 {rowLabel} |
rowLabel({ id }) | 行 {id} (当使用时) getRowLabel 未设定) |
rowActions({ rowLabel }) | {rowLabel} 的动作 |
columnSettings | 列设置 |
columnsPanel | 列数 |
reorderColumn({ column }) | 重排 {栏} |
previousPage 编号 : nextPage | 上页 / 下页 |
showingRange({ start, end }) | 显示 { start}-{end} (当总数未知时) |
pageOf({ page, pageCount }) | 页面 {page} 的 {pageCount} |
expandColumn | 细节( 扩展列隐藏标题文本) |
expandRow({ rowLabel }) | {rowLabel} 的细节 |
sortPriority({ priority, direction }) | 排序优先 {优先 }, 递升/ 递降 |
一个独立的 TableActionMenu 将其触发点命名为“行动”,或 triggerLabel。 。 。 .
持久性
把桌子和钩子都给我 persistKey。值得共享的内容在 URL 中进行,列布局则留在浏览器中:
| 状态 | 拥有者 | 網址 | 本地图集 |
|---|---|---|---|
| 排序 | useTableSort | {key}.sort=name,-team | rdt:1:{key}:sort |
| 页次 | usePagination | {key}.page=3 | 无 |
| 过滤器 | usePersistedFilters | {key}.{param}=… | rdt:1:{key}:filters |
| 列可见度和顺序 | DataTable | 无 | rdt:1:{key}:columns |
- 一个链接就是整个观点. 当一个页面在表格的命名空间中打开它自己没有写入的参数时,这些参数单独设定了视图:一个链接被删除的状态会占用它的默认值,保存的状态不会被读取. 否则,每个州都会读取自己的parm,然后是localStorage,然后是默认的. 列布局从来不是链接的一部分,所以它总是来自本地Storage.
- URL显示当前视图. 恢复后,每变一次, 州写回
history.replaceState,保持你的其他参数,散列和history.state。该类型总是写成,如{key}.sort=当未分类时, 地址栏会增加表的参数值. - 可读取, 一次检查一个参数 . 排序是按优先顺序排列的逗号列表,
-标记下降,页面基于1,不在1页。 一个不解析的参数, 或者一个命名外部一列的排序sortableColumns,以默认值取而代之。 保留了末尾的一页:表格显示其空出状态和前作状态。 存储密钥带有格式版本( S)rdt:1:),因此后期格式忽略了旧条目. - 名称. 排序密钥不能包含逗号或起始
-过滤器param名称不能包含.。在一页上给出每个表格自己的密钥,并使用一个usePersistedFilters每把钥匙. - 过
sortableColumns带有persistKey。 。 。 . 链接可以命名任意排序的密钥,没有列表,它会直接进入您的查询中;有了它,一个未知的密钥会回到默认中去. - 服务器渲染. 服务器渲染,以及客户端渲染出水合物,使用默认状态,所以持续状态不会造成水合不匹配;它直接在渲染后到达. 由它开始的只有客户的渲染 每个钩子返回
isRestored只有在第一次出现时才有假 这样你就可以抓到持续状态.
样式
表格中的每个元素都有一个有文件记录的类,运行时状态都生活在属性上,所以你用普通的 CSS 来重塑它. 每一基本规则 styles.css 是一个单类,一个修饰者或状态最多会再增加一个类,属性或伪类. 因此,您的一个类,在样式表之后加载, 覆盖一个基底样式, 同一状态的同一类覆盖一个状态。 没用 !important,样式表只重排它自己的元素,绝不在单元格中标记.
主题
颜色来自Shadcn风格的符号, 读作全彩值 : --background, (中文(简体) ), --foreground, (中文(简体) ), --card, (中文(简体) ), --popover, (中文(简体) ), --popover-foreground, (中文(简体) ), --primary, (中文(简体) ), --primary-foreground, (中文(简体) ), --muted, (中文(简体) ), --muted-foreground, (中文(简体) ), --accent, (中文(简体) ), --accent-foreground, (中文(简体) ), --border 财务报告和审定财务报表 --ring. 包裹从未宣布它们。 它每个都读取一个内置默认, 所以一个主题你宣布在任何地方获胜, 即使在一个级联层 .dark 在任何祖先上,或在一个组件本身的元素上,切换默认值。 菜单和列面板在末尾渲染 <body>所以当他们的桌子坐在 .dark 他们抄来的 .dark 在他们自己。 当前 shadcn/ ui 主题按原样适用 .
更古老的阴影主题写出赤裸的 HSL 三进制(--card: 0 0% 100%并读为 hsl(var(--card))。要在此使用一个,请同时更改:将每个值写成全色(--card: hsl(0 0% 100%)),并读作 var(--card) 在你自己的CSS,包括一尾风 @theme 测绘. 只更改给出的值 hsl(hsl(...)),则无效.
自定义属性
把这些放在 :root,在桌子的任何祖先,或单行穿行 rowClassName。 。 。 .
| 财产 | 默认 | |
|---|---|---|
--rdt-radius | 0.5rem | 表容器的角半径 |
--rdt-font-size | 0.875rem | 文本正文 |
--rdt-header-font-size | 0.75rem | 页眉文本 |
--rdt-cell-padding-x | 0.75rem | 水平单元格粘贴 |
--rdt-cell-padding-y | 0.5rem | 垂直单元格粘贴 |
--rdt-row-selected | 未设定 | 选定行的背景锡。 选中行总是显示 3px 栏 --primary 在他们的第一个单元格; 锡是额外的。 将您的单元格文本保留在可读处:用Shadcn默认的变音文本,一个淡锡必须保持非常苍白,才能达到4.5:1. |
--rdt-skeleton | 石头、光和暗 | 正在装入占位符 |
--rdt-drag-selection | hsl(216 100% 58.4%) | 拖行区域旁的栏 |
类别和状态
| 块 | 要件和修改 | 状态 |
|---|---|---|
rdt-table | __scroll, (中文(简体) ), __table, (中文(简体) ), __head, (中文(简体) ), __header-row, (中文(简体) ), __header-cell (单位:千美元)--group, (中文(简体) ), --expand, (中文(简体) ), --select, (中文(简体) ), --settings, (中文(简体) ), --actions, (中文(简体) ), --sticky, (中文(简体) ), __header-label, (中文(简体) ), __select-all, (中文(简体) ), __body, (中文(简体) ), __row (单位:千美元)--clickable, (中文(简体) ), __cell (单位:千美元)--expand, (中文(简体) ), --select, (中文(简体) ), --actions, (中文(简体) ), --settings, (中文(简体) ), --sticky, (中文(简体) ), __empty-row, (中文(简体) ), __empty, (中文(简体) ), __expanded-row, (中文(简体) ), __expanded-cell | aria-busy 财务报告和审定财务报表 data-dragging 打开 __table· ; data-selected, (中文(简体) ), data-drag-range, (中文(简体) ), data-drag-start 财务报告和审定财务报表 data-drag-end 打开 __row |
rdt-sort-header (单位:千美元)--sticky(中文(简体) ) | __button (打出细胞), __content (跟随标题) text-align, (中文(简体) ), __label, (中文(简体) ), __icon, (中文(简体) ), __arrow, (中文(简体) ), __priority (一个被排序的列的位置,而两个或更多被排序) | aria-sort 在标题上; data-direction="asc" 或者说 "desc" 打开 __icon |
rdt-checkbox | __indicator, (中文(简体) ), __icon | data-state, (中文(简体) ), data-disabled |
rdt-column-panel | __trigger, (中文(简体) ), __trigger-icon, (中文(简体) ), __list, (中文(简体) ), __item, (中文(简体) ), __label, (中文(简体) ), __handle, (中文(简体) ), __handle-icon | data-dragging 打开 __item |
rdt-menu (单位:千美元)--sub(中文(简体) ) | __item, (中文(简体) ), __sub-trigger, (中文(简体) ), __chevron | data-highlighted, (中文(简体) ), data-disabled, (中文(简体) ), data-state |
rdt-action-menu | __trigger, (中文(简体) ), __trigger-icon, (中文(简体) ), __link, (中文(简体) ), __content, (中文(简体) ), __icon (单位:千美元)--link(中文(简体) ) | |
rdt-pagination (单位:千美元)--bordered(中文(简体) ) | __range, (中文(简体) ), __controls, (中文(简体) ), __page-count, (中文(简体) ), __button, (中文(简体) ), __icon | |
rdt-button (单位:千美元)--ghost, (中文(简体) ), --outline(中文(简体) ) | :disabled | |
rdt-skeleton (单位:千美元)--select, (中文(简体) ), --action, (中文(简体) ), --cell(中文(简体) ) | ||
rdt-expand-button | __icon (chevron,打开时转出) | aria-expanded |
rdt-visually-hidden | 只供屏幕阅读器使用的文本 |
内部 DataTable,可分类标题既是 rdt-table__header-cell 财务报告和审定财务报表 rdt-sort-header。列面板和行菜单在端端的门户中渲染 <body>所以用他们自己的班级,而不是通过 .rdt-table。 。 。 .
示例
/* Denser rows and square corners for every table on the page */
:root {
--rdt-cell-padding-y: 0.25rem;
--rdt-radius: 0;
}
/* Tint selected rows as well, in both themes. These keep shadcn's
default muted text at 4.5:1. */
:root {
--rdt-row-selected: oklch(0.985 0.012 250);
}
.dark {
--rdt-row-selected: oklch(0.26 0.03 255);
}
/* Bold the sorted column's header */
.rdt-table__header-cell[aria-sort] {
font-weight: 600;
}
无障碍
在每个公关上,每个故事都用无头铬制成并用斧头检查(@storybook/addon-a11y) (中文(简体) ).
- 可点击行:行不集中,因为焦点
<tr>不能宣布它有什么作用。 对于打开某种东西的行,在主单元格中放一个真正的链接(或按钮),供键盘和屏幕阅读器用户使用,并使用onRowClick作为该行其余部分的鼠标快捷键。 点击链接时不计为行点击,所以没有放两次火. 可点击行的故事显示了它. - 可扩展行有一个真实的按钮,取名为"QQ labelQQ"的详情",带有
aria-expanded和,虽然开放,aria-controls指着面板. - 排序头是按钮 。 主分类栏载
aria-sort;虽然对两个或两个以上的列进行了排序,但每个被排序的页眉也会宣布其位置和方向("sort preservation 2, downsing"). - 使用页眉单元格
scope="col",并使用组合父标题scope="colgroup"。 。 。 . - 复选框,页码按钮("上页"/"下页"),列设置触发,列拖动手柄("重排QQ栏")都具有可访问的名称.
- 行复选框和行菜单在您通过时以行名命名( “ 选择 QQ label ”) , “ 动作 QQ label QQ ”
getRowLabel,例如行名. 没有它,它们就回到"""("row")",所以每当行有人能读取的名字时,就通过它. - 列设置面板是一个对话框: Tab 到达每个复选框和控点, Space 切换出一列, 并用 Space, 箭头键和 Space 重排焦点控点 .
- 行菜单是带有箭键导航的Radix倒放菜单.
已知差距 :
- 拖动选择并点击到选择行是只使用鼠标的 。 复选框是键盘路径, Shift+ 在焦点复选框上的空间设置了最后一行切换的每行 .
已知问题
目前无. 一号报告 GitHub 图像。 。 。 .
发展
pnpm install
pnpm storybook # http://localhost:6006
pnpm test # unit tests (Vitest + happy-dom)
pnpm test:storybook # every story in Chromium: play functions + axe
pnpm lint # Biome
pnpm typecheck
pnpm build # dist/
pnpm lint:package # publint + are-the-types-wrong
pnpm smoke # install the packed tarball and use it
pnpm test:storybook 需要铬一次: pnpm exec playwright install chromium。 。 。 .
当一个 GitHub 发布时,通过 npm 信任的来源发布, 发布从 CI 发布 。 见 捐款额。 。 。 .
许可证
麻省理工学院 安东尼·利多