first commit
This commit is contained in:
150
docs/ui/react/react-ui/src/components/data-table/data-table.md
Normal file
150
docs/ui/react/react-ui/src/components/data-table/data-table.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# DataTable
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - data-table.variants — `dataTableVariants`, `dataTab…
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
|
||||
- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
|
||||
- data-table.variants — `dataTableVariants`, `dataTab…
|
||||
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
- [data-table.variants](./data-table.variants.md) — `dataTableVariants`, `dataTablePaginationVariants`, and `dataTableSortHeaderVariants` drive the chassis `<DataTable>` o…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<DataTable>` is the chassis headless data-table built on `@tanstack/react-table` (v8) and the chassis `<Table>` family. It is a **suaveplan-only addition** — shadcn does not ship `data-table` as a registry item upstream (per Wave 3a's reconciliation finding #7); shadcn's docs treat data-tables as recipes consumers build on top of the `<Table>` primitive. The chassis exposes `<DataTable>` as a discrete component so consumers do not hand-roll sorting / pagination / selection / empty-state treatment per surface.
|
||||
|
||||
The component is generic over `TData` (the row shape) and `TValue` (the column value union, defaults to `unknown`). Consumers build column definitions with the re-exported `createColumnHelper` from `@tanstack/react-table`. Every chassis-rendered string (no-results, pagination, rows-per-page, selection summary) flows through the active i18n via `t()` calls keyed on bundled `react-ui.dataTable.*` translation keys. The `selected` key uses ICU plural so the count agrees with the active locale's plural rules — Welsh's six-form scale is bundled.
|
||||
|
||||
## Features
|
||||
|
||||
- Built on TanStack Table v8 — the de-facto headless table for React.
|
||||
- Sortable columns — click any sortable column header to toggle asc / desc / none; the header button shows a chevron icon indicating the active sort direction.
|
||||
- Pagination — `pageSize` defaults to 10 rows; previous / next buttons disable at the boundaries.
|
||||
- Row selection (opt-in via `enableRowSelection`) — chassis renders a selection summary using ICU plural translation.
|
||||
- Empty state — when no rows match, the chassis renders the bundled `react-ui.dataTable.noResults` message in a single full-width row.
|
||||
- Stable row keys via `getRowId` (recommended) — without it, TanStack uses array-index keys which collide on re-order.
|
||||
- `data-state="selected"` on selected rows so chassis `<TableRow>` hover / selected styling activates automatically.
|
||||
- Re-exports `createColumnHelper` so consumers don't need to add `@tanstack/react-table` as a direct dep.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { createColumnHelper, DataTable } from "@suaveplan/react-ui";
|
||||
|
||||
interface Invoice {
|
||||
readonly id: string;
|
||||
readonly invoice: string;
|
||||
readonly status: string;
|
||||
readonly amount: number;
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<Invoice>();
|
||||
|
||||
const COLUMNS = [
|
||||
columnHelper.accessor("invoice", { header: "Invoice" }),
|
||||
columnHelper.accessor("status", { header: "Status" }),
|
||||
columnHelper.accessor("amount", {
|
||||
header: "Amount",
|
||||
cell: (info) => `$${info.getValue().toFixed(2)}`,
|
||||
}),
|
||||
];
|
||||
|
||||
export function InvoiceDataTable({ data }: { data: Invoice[] }) {
|
||||
return (
|
||||
<DataTable<Invoice>
|
||||
columns={COLUMNS}
|
||||
data={data}
|
||||
getRowId={(row) => row.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { createColumnHelper, DataTable } from "@suaveplan/react-ui";
|
||||
|
||||
interface User {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly email: string;
|
||||
readonly role: "admin" | "member";
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<User>();
|
||||
|
||||
const COLUMNS = [
|
||||
columnHelper.display({
|
||||
id: "select",
|
||||
header: "",
|
||||
cell: ({ row }) => (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
aria-label="select-row"
|
||||
/>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("name", { header: "Name", enableSorting: true }),
|
||||
columnHelper.accessor("email", { header: "Email", enableSorting: false }),
|
||||
columnHelper.accessor("role", { header: "Role", enableSorting: true }),
|
||||
];
|
||||
|
||||
export function UserDataTable({ users }: { users: User[] }) {
|
||||
return (
|
||||
<DataTable<User>
|
||||
columns={COLUMNS}
|
||||
data={users}
|
||||
getRowId={(row) => row.id}
|
||||
enableRowSelection
|
||||
pageSize={5}
|
||||
initialSorting={[{ id: "name", desc: false }]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<DataTable<TData, TValue = unknown>>`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `columns` | `ReadonlyArray<ColumnDef<TData, TValue>>` | — | Column definitions; build with `createColumnHelper<TData>()`. |
|
||||
| `data` | `ReadonlyArray<TData>` | — | The rows to render. Must be stable identity-wise (use `useMemo` on the consumer side). |
|
||||
| `getRowId` | `(row: TData, index: number) => string` | _undefined_ | Stable row id for selection / keying. Recommended. |
|
||||
| `enableRowSelection` | `boolean` | `false` | When true, renders a selection summary using ICU plural translation. |
|
||||
| `pageSize` | `number` | `10` | Rows per page. |
|
||||
| `initialSorting` | `SortingState` | `[]` | Initial sort state. |
|
||||
| `noResultsKey` | `TranslationKey` | `react-ui.dataTable.noResults` | Empty-state message. |
|
||||
| `rowsPerPageKey` | `TranslationKey` | `react-ui.dataTable.rowsPerPage` | Pagination footer label. |
|
||||
| `selectedKey` | `TranslationKey` | `react-ui.dataTable.selected` | ICU-plural selection summary; receives `{ count }`. |
|
||||
| `previousKey` | `TranslationKey` | `react-ui.dataTable.previous` | Previous-page button label. |
|
||||
| `nextKey` | `TranslationKey` | `react-ui.dataTable.next` | Next-page button label. |
|
||||
|
||||
### `createColumnHelper<TData>()`
|
||||
|
||||
Re-exported from `@tanstack/react-table`. Returns a builder with `accessor`, `display`, and `group` methods for typed column definitions.
|
||||
|
||||
#### Errors
|
||||
|
||||
The DataTable component calls `useTranslation()` to resolve the chassis-rendered translation keys. It MUST render inside a `<TranslationProvider>` / `<ReactUiProvider>`; otherwise `useTranslation` throws.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The component is generic over `TData` and `TValue` so consumers get full type-safety on column definitions and cell renderers. The cast to `ColumnDef<TData, TValue>[]` inside `useReactTable` is a TanStack ergonomics workaround — TanStack's `data` accepts `TData[]` but its `columns` slot accepts a value-erased shape, so the chassis re-asserts the typed shape at the boundary.
|
||||
- The `selected` plural translation includes the Welsh six-form scale (`zero | one | two | few | many | other`) so the count agrees with the active locale's plural rules. Other locales (`en-GB`, `zh-CN`, `ar-AE`, `ru-RU`) use their respective plural categories from the bundled JSON.
|
||||
- `getRowId` is strongly recommended even when `data` items contain an `id` field — without a stable getter, TanStack uses array index keys, which collide when consumers reorder the data array. The chassis does NOT default `getRowId` to `(row) => row.id` because not every `TData` shape has an `id` field.
|
||||
- The pagination controls are intentionally bare `<button>` elements with `border px-2 py-1` — this keeps the data-table self-contained without forcing a `<Button>` import. Consumers wanting fancier pagination compose their own footer instead of using the built-in one (read pagination state from `table.getState().pagination` and call `table.setPageIndex` / `table.setPageSize` directly).
|
||||
- The sort indicator chevron icon is `aria-hidden="true"` because the column header text already conveys the sort target; screen-readers should not double-announce.
|
||||
- Empty state spans `colSpan={columns.length}` so the no-results message centres across the full table width regardless of column count.
|
||||
Reference in New Issue
Block a user