prime-ui-kit 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunks/{chunk-RZNGLELR.js → chunk-3PY52JP3.js} +1603 -1609
- package/dist/chunks/{chunk-ZTZ2ML2B.js → chunk-4KONMO7Z.js} +1 -1
- package/dist/chunks/{chunk-WYCTXD63.js → chunk-W6EGTNG6.js} +3553 -2882
- package/dist/color-picker.js +2 -2
- package/dist/components/data-table/ColumnsMenu.d.ts +34 -8
- package/dist/components/data-table/DataTable.d.ts +31 -5
- package/dist/components/data-table/DataTableColumnsMenu.d.ts +45 -0
- package/dist/components/data-table/Head.d.ts +14 -8
- package/dist/components/data-table/Row.d.ts +8 -8
- package/dist/components/data-table/StateRows.d.ts +5 -5
- package/dist/components/data-table/columnOrder.d.ts +14 -0
- package/dist/components/data-table/reorder.d.ts +15 -5
- package/dist/components/data-table/types.d.ts +5 -0
- package/dist/components/data-table/useFrozenColumns.d.ts +6 -2
- package/dist/components/datepicker/DatepickerPanel.d.ts +1 -1
- package/dist/components/dnd/Dnd.d.ts +9 -5
- package/dist/components/dnd/inPlaceRow.d.ts +7 -0
- package/dist/components/dnd/useDragSource.d.ts +2 -0
- package/dist/components/dnd/useSortableList.d.ts +5 -0
- package/dist/components/field-list/FieldList.d.ts +34 -12
- package/dist/components/gantt/Gantt.d.ts +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/scroll-container/ScrollContainer.d.ts +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +4 -2
- package/dist/internal/ToolRow.d.ts +23 -0
- package/dist/internal/fieldCell.d.ts +0 -6
- package/dist/layout/page/Page.d.ts +7 -7
- package/dist/skill/anti-slop.md +43 -12
- package/dist/skill/api-contract.md +1 -1
- package/dist/skill/cheatsheet.md +7 -3
- package/dist/skill/checklist.md +4 -1
- package/dist/skill/choosing.md +1 -0
- package/dist/skill/components.md +1 -1
- package/dist/skill/composition.md +137 -20
- package/dist/skill/reference/components/data-table/COMPONENT.md +101 -40
- package/dist/skill/reference/components/data-table/examples/column-order.tsx +152 -0
- package/dist/skill/reference/components/data-table/examples/columns-visibility.tsx +20 -11
- package/dist/skill/reference/components/data-table/examples/controlled.tsx +32 -17
- package/dist/skill/reference/components/data-table/examples/examples.module.css +14 -36
- package/dist/skill/reference/components/data-table/examples/narrow.tsx +24 -11
- package/dist/skill/reference/components/data-table/examples/reorder.tsx +24 -2
- package/dist/skill/reference/components/data-table/examples/selection.tsx +13 -18
- package/dist/skill/reference/components/data-table/examples/server.tsx +45 -31
- package/dist/skill/reference/components/data-table/examples/toolbar.tsx +5 -12
- package/dist/skill/reference/components/datepicker/COMPONENT.md +2 -2
- package/dist/skill/reference/components/dnd/COMPONENT.md +9 -4
- package/dist/skill/reference/components/dnd/examples/table-columns.tsx +75 -0
- package/dist/skill/reference/components/field-list/COMPONENT.md +70 -43
- package/dist/skill/reference/components/field-list/examples/narrow.tsx +108 -33
- package/dist/skill/reference/components/field-list/examples/reorder.tsx +1 -1
- package/dist/skill/reference/components/field-list/examples/row-menu.tsx +71 -0
- package/dist/skill/reference/components/field-list/examples/screen.tsx +2 -1
- package/dist/skill/reference/components/field-list/examples/variants.tsx +51 -0
- package/dist/skill/reference/components/gantt/COMPONENT.md +4 -4
- package/dist/skill/reference/components/scroll-container/COMPONENT.md +1 -1
- package/dist/skill/reference/components/smart-filter/COMPONENT.md +2 -2
- package/dist/skill/reference/layout/page/COMPONENT.md +8 -5
- package/dist/skill/responsive.md +10 -8
- package/dist/tokens/semantic.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/data-table/COMPONENT.md +101 -40
- package/src/components/data-table/examples/column-order.tsx +152 -0
- package/src/components/data-table/examples/columns-visibility.tsx +20 -11
- package/src/components/data-table/examples/controlled.tsx +32 -17
- package/src/components/data-table/examples/examples.module.css +14 -36
- package/src/components/data-table/examples/narrow.tsx +24 -11
- package/src/components/data-table/examples/reorder.tsx +24 -2
- package/src/components/data-table/examples/selection.tsx +13 -18
- package/src/components/data-table/examples/server.tsx +45 -31
- package/src/components/data-table/examples/toolbar.tsx +5 -12
- package/src/components/datepicker/COMPONENT.md +2 -2
- package/src/components/dnd/COMPONENT.md +9 -4
- package/src/components/dnd/examples/table-columns.tsx +75 -0
- package/src/components/field-list/COMPONENT.md +70 -43
- package/src/components/field-list/examples/narrow.tsx +108 -33
- package/src/components/field-list/examples/reorder.tsx +1 -1
- package/src/components/field-list/examples/row-menu.tsx +71 -0
- package/src/components/field-list/examples/screen.tsx +2 -1
- package/src/components/field-list/examples/variants.tsx +51 -0
- package/src/components/gantt/COMPONENT.md +4 -4
- package/src/components/scroll-container/COMPONENT.md +1 -1
- package/src/components/smart-filter/COMPONENT.md +2 -2
- package/src/layout/page/COMPONENT.md +8 -5
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
- Thousands of records the server sorts and pages (`source="server"` + `rowCount`): the table shows the one page the server returned.
|
|
12
12
|
- Rows that open a record: a page (`getRowHref`, a real link) or a drawer in place (`onRowClick`).
|
|
13
13
|
- Rows grouped by a key with a header and totals per group (`getRowGroup`).
|
|
14
|
-
- Records whose order is data the user sets: pipeline stages, a price list, priorities (`onReorder`).
|
|
14
|
+
- Records whose order is data the user sets: pipeline stages, a price list, priorities (`onReorder`; the grip ends the row, `reorderHandle` moves it).
|
|
15
|
+
- Tables people arrange for themselves: their own column order and set (`columnOrder` + `hiddenColumns`, saved by the app).
|
|
15
16
|
- Hierarchical rows (`getRowChildren`) or rows with a detail panel (`renderExpanded`).
|
|
16
17
|
- Wide numeric reports with a sticky head and first column.
|
|
17
|
-
- Wide records on narrow screens: let people choose the columns (`columnsMenu`) instead of dropping them.
|
|
18
|
+
- Wide records on narrow screens: let people choose the columns (`columnsMenu` over the row actions, else `DataTableColumnsMenu` beside the table's other tools) instead of dropping them.
|
|
19
|
+
- A table among other blocks (KPI cards, a chart): its search, filter, chips, column chooser and view stand in its own tools row (`toolbar` + `toolbarChips`) — a filter stands next to what it filters. The page's bar keeps the title, a period that drives several blocks and the primary action.
|
|
18
20
|
- A log or a register that takes the rest of the page height (`scrollHeight="fill"`).
|
|
19
21
|
|
|
20
22
|
## When not to use
|
|
@@ -28,24 +30,34 @@
|
|
|
28
30
|
|
|
29
31
|
## Import
|
|
30
32
|
```tsx
|
|
31
|
-
import { DataTable, type DataTableColumn } from "prime-ui-kit";
|
|
33
|
+
import { DataTable, type DataTableColumn, DataTableColumnsMenu } from "prime-ui-kit";
|
|
32
34
|
```
|
|
33
|
-
Exported types: `DataTableColumn<Row>`, `DataTableProps<Row>`, `DataTableSortState`, `DataTableOrder`, `DataTableGroup<Row>`, `DataTableRowClickEvent`, `DataTableLabels`.
|
|
35
|
+
Exported types: `DataTableColumn<Row>`, `DataTableProps<Row>`, `DataTableSortState`, `DataTableOrder`, `DataTableGroup<Row>`, `DataTableRowClickEvent`, `DataTableLabels`, `DataTableColumnsMenuProps<Row>`, `DataTableColumnsMenuLabels`.
|
|
34
36
|
|
|
35
37
|
## Anatomy
|
|
36
38
|
```
|
|
37
39
|
DataTable card-fill block, radius 12, clips its content
|
|
38
|
-
├── toolbar
|
|
40
|
+
├── toolbar the table's own tools row (ToolRow, as Page.Tools): search, filter, chooser, view, bulk actions
|
|
41
|
+
│ └── toolbarChips the applied filters (SmartFilter.Chips) on a line under it, gone while empty
|
|
39
42
|
├── scroll viewport ScrollContainer, both axes, in a frame that holds the edge shadows
|
|
40
43
|
│ └── table
|
|
41
|
-
│ ├── thead [
|
|
42
|
-
│ │ columnsMenu: an icon Button + Dropdown in the row-actions head cell
|
|
44
|
+
│ ├── thead [grip] [select all] [toggle] head cells [end grip cell]; sortable heads hold a button
|
|
45
|
+
│ │ columnsMenu: an icon Button + Dropdown in the row-actions head cell (the last
|
|
46
|
+
│ │ column, hideHeader); with columnOrder a Dnd.Sortable list inside
|
|
47
|
+
│ │ columnOrder: the head row is a Dnd.Sortable; a header moves alone (drag, Alt + ← / →)
|
|
43
48
|
│ └── tbody rows · sub-rows · detail rows · skeleton / empty / error row
|
|
44
49
|
│ (one per group) with getRowGroup: a <th scope="rowgroup"> header row, then its rows
|
|
45
|
-
│ with onReorder: a Dnd.Sortable tbody
|
|
50
|
+
│ with onReorder: a Dnd.Sortable tbody; a Dnd.Handle ends each row (reorderHandle
|
|
51
|
+
│ "end"), leads it ("start"), or the row itself is the grip ("row")
|
|
46
52
|
└── footer range «Показано 1–5 из 23» · Pagination · infinite-scroll status
|
|
47
53
|
```
|
|
48
|
-
|
|
54
|
+
```
|
|
55
|
+
DataTableColumnsMenu the column chooser outside the table, for the page's tools
|
|
56
|
+
├── soft Button view.columns icon · labels.columns · Badge with the count of shown columns
|
|
57
|
+
└── Dropdown a «Показывать» group of check items (with onColumnOrderChange a Dnd.Sortable list,
|
|
58
|
+
a grip ends each item) · «Показать все» · «Исходный порядок»
|
|
59
|
+
```
|
|
60
|
+
Columns are data (`DataTableColumn<Row>[]`), not child parts; the table has no parts. The column chooser has one place per table: the `columnsMenu` flag puts it in the head of the row-actions column; a table without one gets `DataTableColumnsMenu` beside the table's other tools — in its `toolbar` on a page that holds more than the table, in `Page.Tools` on a page that is the table — bound to the same `hiddenColumns` / `columnOrder` state; never alone in a `toolbar`.
|
|
49
61
|
|
|
50
62
|
## API
|
|
51
63
|
|
|
@@ -57,10 +69,13 @@ Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an
|
|
|
57
69
|
| Prop | Type | Default | Description |
|
|
58
70
|
|---|---|---|---|
|
|
59
71
|
| `columns` | `DataTableColumn<Row>[]` | — (required) | Column definitions, see `DataTableColumn<Row>`. |
|
|
60
|
-
| `hiddenColumns` | `string[]` | — | Ids of columns that are not rendered, controlled. A column with `hideable: false` stays. The column chooser (`columnsMenu`) changes it. |
|
|
72
|
+
| `hiddenColumns` | `string[]` | — | Ids of columns that are not rendered, controlled. A column with `hideable: false` stays. The column chooser (`columnsMenu`, `DataTableColumnsMenu`) changes it. |
|
|
61
73
|
| `defaultHiddenColumns` | `string[]` | `[]` | Initially hidden column ids, uncontrolled. |
|
|
62
74
|
| `onHiddenColumnsChange` | `(hiddenColumns: string[]) => void` | — | The hidden ids changed (`columnsMenu`). The kit stores nothing: keep the list (local storage, the user's settings) and pass it back to restore the choice. |
|
|
63
|
-
| `
|
|
75
|
+
| `columnOrder` | `string[]` | — | Column ids in screen order, hidden ones keeping their place, controlled. Any prop of this triad makes columns reorder: a header drag (the body stays until the drop), Alt + ← / → on a header, grips in the column chooser. |
|
|
76
|
+
| `defaultColumnOrder` | `string[]` | — | Initial column order, uncontrolled; `[]` — the order of `columns`, reorderable. |
|
|
77
|
+
| `onColumnOrderChange` | `(columnOrder: string[]) => void` | — | The order changed: a header dropped, a keyboard move, a chooser grip or «Исходный порядок». The kit stores nothing: keep it with `hiddenColumns` and pass it back. |
|
|
78
|
+
| `columnsMenu` | `boolean` | `false` | The column chooser in the head of the row-actions column (the last one, `hideHeader`); reorders with `columnOrder`. Without that column it renders nothing and warns: use `DataTableColumnsMenu` in the page's tools. |
|
|
64
79
|
| `rows` | `Row[]` | — (required) | Data; sorted in memory when a sort is set. With `source="server"` — the page (or the loaded batches) as the server returned it. |
|
|
65
80
|
| `source` | `"client" \| "server"` | `"client"` | Who sorts and pages the rows. `client`: the table sorts `rows` and slices pages. `server`: `rows` is one page, already sorted; `sort` / `page` changes are the request, `rowCount` drives Pagination. |
|
|
66
81
|
| `rowCount` | `number` | `rows.length` | Total rows on the server (`source="server"`): the page count and «Показано {from}–{to} из {total}». |
|
|
@@ -79,12 +94,14 @@ Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an
|
|
|
79
94
|
| `loadingRows` | `number` | `min(pageSize, 5)` | Number of skeleton rows. |
|
|
80
95
|
| `empty` | `ReactNode` | `labels.empty` | Empty-state content: a string goes into a compact EmptyPage, a node (e.g. a full `EmptyPage`) is rendered as is. |
|
|
81
96
|
| `loadError` | `ReactNode` | — | Load error: replaces the body with the message in `role="alert"`. |
|
|
82
|
-
| `toolbar` | `ReactNode` | — |
|
|
97
|
+
| `toolbar` | `ReactNode` | — | The table's own tools row — search, filter, `DataTableColumnsMenu`, a view, bulk actions; two or more, unwrapped. Laid out as `Page.Tools`: the search takes the free room, the rest keep their width at the end. |
|
|
98
|
+
| `toolbarChips` | `ReactNode` | — | The applied filters (`SmartFilter.Chips`) on a line under the `toolbar`, gone while empty; `SmartFilter.Root` wraps the DataTable. |
|
|
83
99
|
| `selectable` | `boolean` | `false` | Leading checkbox column with «select all», Shift range, press-and-drag and Space. |
|
|
84
100
|
| `selected` | `Key[]` | — | Selected row ids, controlled. |
|
|
85
101
|
| `defaultSelected` | `Key[]` | `[]` | Initially selected ids, uncontrolled. |
|
|
86
102
|
| `onSelectedChange` | `(selected: Key[]) => void` | — | Selection change. |
|
|
87
|
-
| `onReorder` | `(id: Key, beforeId: Key \| null) => void \| Promise<void>` | — | Rows reorder by a
|
|
103
|
+
| `onReorder` | `(id: Key, beforeId: Key \| null) => void \| Promise<void>` | — | Rows reorder (by a grip, see `reorderHandle`). Row `id` goes in front of `beforeId` (`null` = last); apply it with `moveBefore`, return a promise to save. Off while sorted by a column. |
|
|
104
|
+
| `reorderHandle` | `"start" \| "end" \| "row"` | `"end"` | Where a row is picked up (`onReorder`): `end` — a grip ends the row; `start` — a grip column leads it; `row` — no grip, the whole row is carried and takes Alt + ↑ / ↓ itself. |
|
|
88
105
|
| `getRowChildren` | `(row: Row) => Row[] \| undefined` | — | Sub-rows rendered under the parent with the same columns, indented by depth. |
|
|
89
106
|
| `renderExpanded` | `(row: Row) => ReactNode` | — | Full-width detail row under an expanded row. |
|
|
90
107
|
| `isRowExpandable` | `(row: Row) => boolean` | — | Which rows get an expand toggle; by default rows with sub-rows, or every row with `renderExpanded`. |
|
|
@@ -119,6 +136,20 @@ Generic `DataTable<Row>`; `ref` → `HTMLDivElement`. A card-fill block with an
|
|
|
119
136
|
| `className` | `string` | — | Class on the root `<div>`. |
|
|
120
137
|
| `…rest` | `Omit<HTMLAttributes<HTMLDivElement>, "children">` | — | The other attributes of the root `<div>`. |
|
|
121
138
|
|
|
139
|
+
### DataTableColumnsMenu
|
|
140
|
+
Generic `DataTableColumnsMenu<Row>`; `ref` → `HTMLButtonElement`. The column chooser outside the table, for the page's tools: a soft Button «Колонки» with the count of shown columns, opening the same menu as `columnsMenu`. Bound to the table's state.
|
|
141
|
+
|
|
142
|
+
| Prop | Type | Default | Description |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| `columns` | `DataTableColumn<Row>[]` | — (required) | The table's `columns`, in the order of the code. |
|
|
145
|
+
| `hiddenColumns` | `string[]` | — (required) | The ids the table hides: the same list its `hiddenColumns` reads. |
|
|
146
|
+
| `onHiddenColumnsChange` | `(hiddenColumns: string[]) => void` | — (required) | A column was shown or hidden, or «Показать все». |
|
|
147
|
+
| `columnOrder` | `string[]` | — | The table's `columnOrder`. |
|
|
148
|
+
| `onColumnOrderChange` | `(columnOrder: string[]) => void` | — | Turns the items into a sortable list (a grip, Alt + ↑ / ↓) with «Исходный порядок»; the new order of every column. |
|
|
149
|
+
| `size` | `"xs" \| "s" \| "m" \| "l" \| "xl"` | host tier, else `"m"` | Tier of the trigger and the menu. |
|
|
150
|
+
| `labels` | `Partial<DataTableColumnsMenuLabels>` | — | The chooser's strings: `columns`, `showColumns`, `showAllColumns`, `resetColumnOrder`, `reorderColumn`. |
|
|
151
|
+
| `…rest` | `Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" \| "type">` | — | `className` and the other attributes of the trigger button. |
|
|
152
|
+
|
|
122
153
|
### Types
|
|
123
154
|
|
|
124
155
|
#### `DataTableColumn<Row>`
|
|
@@ -141,6 +172,7 @@ One column of `columns`: data, not a part.
|
|
|
141
172
|
| `truncate` | `boolean` | — | One line with an ellipsis; width from `maxWidth` (or `width`); string values get a `title`. |
|
|
142
173
|
| `grow` | `boolean` | — | Takes the free width and wraps its text; the table then fills its container. With `minWidth` it never gets narrower: below that the table scrolls instead of cells overlapping. |
|
|
143
174
|
| `hideable` | `boolean` | `true` | The column can be hidden through `hiddenColumns` and `columnsMenu`; `false` for the key column that names the row and for the row-actions column. |
|
|
175
|
+
| `reorderable` | `boolean` | `true` | The column moves when columns reorder (`columnOrder`); `false` holds its place: no column moves past it, the chooser shows a lock. A `hideHeader` column and a pinned first or last column (`stickyFirstColumn`, `stickyLastColumn`) hold theirs too. |
|
|
144
176
|
| `scale` | `boolean \| { min?: number; max?: number }` | — | Heatmap: each cell is shaded by its numeric value on the five-step chart scale. `true` spans the column's minimum to maximum over all rows; `{ min, max }` fixes either end. |
|
|
145
177
|
| `width · minWidth · maxWidth` | `string` | — | CSS sizes of the column (`"14rem"`). |
|
|
146
178
|
|
|
@@ -198,6 +230,13 @@ on screen.
|
|
|
198
230
|
|
|
199
231
|
With both `getRowHref` and `onRowClick`, `onRowClick` gets only plain clicks: `event.preventDefault()` there routes in the app, while ⌘/Ctrl-clicks, middle clicks and «open in new tab» stay the browser's.
|
|
200
232
|
|
|
233
|
+
### reorderHandle
|
|
234
|
+
| Value | Looks like | Use when | Default |
|
|
235
|
+
|---|---|---|---|
|
|
236
|
+
| `end` | a grip (`Dnd.Handle`) ends every row, in the trailing cell under the column chooser; it sticks with `stickyLastColumn` | most tables: the kit's place for a grip (List, FieldList, Kanban), clear of the selection and the key cell | yes |
|
|
237
|
+
| `start` | a grip column leads every row, before the checkboxes; it sticks with `stickyFirstColumn` | a list whose order is its main point (stages, a playlist) read from the start edge | |
|
|
238
|
+
| `row` | no grip: the whole row is carried (the cursor says so; a finger holds still), the row itself takes Tab and Alt + ↑ / ↓; its text cannot be selected, the checkbox cell only selects | short lists of settings or priorities with nothing to select, copy or open by a link in their rows | |
|
|
239
|
+
|
|
201
240
|
### column `align`
|
|
202
241
|
| Value | Looks like | Use when | Default |
|
|
203
242
|
|---|---|---|---|
|
|
@@ -219,8 +258,10 @@ Heads keep `start` whatever the cell alignment, with the sort icon at the end ed
|
|
|
219
258
|
| `stickyLastColumn` | the last visible column sticks to the end edge, opaque in the row's fill (hover, zebra, selected, expanded), with a hairline on its start edge | a row actions column (⋯ menu, an icon Button) of a table wider than its container | `false` |
|
|
220
259
|
| `stickyGroupHeaders` | group header rows stick under the head while their rows scroll | long grouped tables in a `scrollHeight` window | `false` |
|
|
221
260
|
| `collapsedGroups` · `defaultCollapsedGroups` · `onCollapsedGroupsChange` | any of the three makes groups fold: a chevron toggle leads each group header, a collapsed group shows its header alone | long grouped tables where people work one group at a time; `defaultCollapsedGroups={[]}` starts with every group open | off |
|
|
222
|
-
| `columnsMenu` | an icon-only ghost Button (`view.columns`)
|
|
261
|
+
| `columnsMenu` | an icon-only ghost Button (`view.columns`) in the head cell of the row-actions column (the last column, `hideHeader`), lined up with the ⋯ under it; it opens the column chooser. Without that column nothing renders and the table warns | a table with row actions and more columns than a narrow screen holds; without row actions, `DataTableColumnsMenu` in the page's tools | `false` |
|
|
223
262
|
| column `hideable` | `false`: the column stays even when listed in `hiddenColumns`; checked and disabled in the column chooser | the key column that names the row, the row-actions column | `true` |
|
|
263
|
+
| `columnOrder` · `defaultColumnOrder` · `onColumnOrderChange` | any of the three makes columns reorder: a press that moves on a header carries the header alone (a finger holds still) while the body stays, Alt + ← / → on the header, grips in the column chooser plus «Исходный порядок» | tables people arrange for themselves; `defaultColumnOrder={[]}` starts in the order of `columns` | off |
|
|
264
|
+
| column `reorderable` | `false`: the column holds its place — its header carries nothing, no column moves past it, the chooser shows a lock. A `hideHeader` column and a pinned first / last column hold theirs too | the key column, a total that must stay last | `true` |
|
|
224
265
|
| `showHeader` | head row shown | off only for self-explanatory key/value tables | `true` |
|
|
225
266
|
| `fullWidth` | spans the container; `false` = content width | `false` for short lookup tables in wide layouts | `true` |
|
|
226
267
|
| column `numeric` | tabular figures, end-aligned, no wrap | numbers, money | — |
|
|
@@ -245,28 +286,34 @@ Heads keep `start` whatever the cell alignment, with the sort icon at the end ed
|
|
|
245
286
|
| new rows | rows new in `rows` (by `getRowKey`) | `data-animate`: top-level cells drop in from above (`enterMotion`) |
|
|
246
287
|
| sub-rows and detail panels | an expand / a collapse | both fold open and shut through the kit's Disclosure (`0fr ↔ 1fr`, the fade and a short slide on the same `base` clock; a detail opens on the enter curve); the row height and padding live inside the fold, so nothing is left to jump. Sub-rows: `data-fold` on the row (`open` · `closed`), every cell's content a Disclosure; a collapsed row's sub-rows stay mounted until the fold ends. Detail row: `data-state`; it unmounts after the close |
|
|
247
288
|
| overflowing sideways | the table is wider than its viewport | `data-overflow-start` (scrolled from the start) and `data-overflow-end` (more to the end) on the root: a soft shadow over that edge, from the end edge of the sticky first column for the start and from the start edge of the sticky last column for the end; fades in and out over `fast` |
|
|
248
|
-
| hidden columns | `hiddenColumns` / `defaultHiddenColumns`, `columnsMenu` | the listed columns (except `hideable: false`) are not rendered in the head, rows, skeleton or state rows; widths are measured again; a sort by a hidden column keeps the row order |
|
|
289
|
+
| hidden columns | `hiddenColumns` / `defaultHiddenColumns`, `columnsMenu`, `DataTableColumnsMenu` | the listed columns (except `hideable: false`) are not rendered in the head, rows, skeleton or state rows; widths are measured again; a sort by a hidden column keeps the row order |
|
|
249
290
|
| opening rows | `getRowHref` / `onRowClick`, `isRowClickable` | `data-clickable` (pointer, pressed wash) on rows that open; link rows: `data-link`, an `<a data-row-link>` in every content cell; in-place rows: `tabindex="0"`. Focus (the link, or the row) rings the whole row with the hover wash |
|
|
250
291
|
| grouped | `getRowGroup` | one `<tbody>` per group run on screen, opened by a `<th scope="rowgroup">` row one ladder step off the table; `data-sticky` on it with `stickyGroupHeaders` |
|
|
251
292
|
| collapsed group | `collapsedGroups` / `defaultCollapsedGroups` | `data-collapsed` on the group header row, its toggle `aria-expanded="false"` with the chevron at rest; the group's `<tbody>` holds the header alone, its rows only lay out the column widths in the hidden measuring body. Its rows fold away and back like sub-rows (`data-fold`, `base`), whether a toggle or the owner changed the keys. «Select all» still takes the rows of collapsed groups; paging still counts them |
|
|
252
|
-
| reorderable | `onReorder` (not with `getRowChildren` / `renderExpanded`) | `data-reorderable` on the root; a leading
|
|
293
|
+
| reorderable | `onReorder` (not with `getRowChildren` / `renderExpanded`) | `data-reorderable` and `data-reorder-handle` (`end` · `start` · `row`) on the root; a grip ending each row in the trailing cell, a leading grip column, or the row itself (drag, a touch hold, Alt + ↑ / ↓ on the grip or the row); each run of rows (the page, or each group) is a `Dnd.Sortable` `<tbody>`, so with `getRowGroup` rows move inside their group; a row put last on a page stands in front of the first row of the next page; a lifted row leaves a dashed gap row across every column, its neighbours glide (`base`), the copy flies in a one-row table at the row's column widths and sets down on its new place; a keyboard move glides the same way |
|
|
253
294
|
| saving a move | `onReorder` returned a pending promise | the row stays in its new place, `aria-busy`, its cells at 60% and its grip busy (Dnd: a spinner in the disabled handle); a rejection glides it back |
|
|
254
|
-
| reorder off | a sort by a column | the handles are disabled with a Tooltip (`labels.reorderSorted`); no drag, no Alt + arrows |
|
|
295
|
+
| reorder off | a sort by a column | the handles are disabled with a Tooltip (`labels.reorderSorted`); no drag, no Alt + arrows (rows that are their own grip just hold still) |
|
|
296
|
+
| reorderable columns | `columnOrder` / `defaultColumnOrder` / `onColumnOrderChange` | the head row is a `Dnd.Sortable as="tr"`: each movable head cell is a `Dnd.SortableItem` (`dragFromControls`, so a press on the sort button picks it up too); a plain one is a tab stop (`tabindex="0"`, `aria-keyshortcuts`, the inset ring), a sortable one passes the keys from its sort button; the chooser's items are a `Dnd.Sortable` list with a grip (or a lock) at their end. A new order rebuilds the body once and the cells of the column that moved fade in (`enterMotion` `.enterFade`); a move from outside the head row (the chooser, the owner) glides the head cells (`base`), each column keeps its width by id |
|
|
297
|
+
| carrying a column | a header press that travelled 4 px (a finger: held 280 ms) | Dnd's gesture: a copy of the header alone flies on the lift shadow; the header stays in its column as Dnd's gap (the dashed, tinted slot of every sortable list, `data-lifted`) and glides with the other headers to where they will stand (`base`; at once under reduced motion), and the viewport scrolls near its edges. The body stays still the whole gesture. The drop lands the copy on the header's new place and rebuilds the body once (the moved column fades in); Escape glides the headers back, nothing else moved; the click that ends a drag never sorts |
|
|
255
298
|
| heatmap cell | column `scale` and a numeric value (`sortAccessor`, else `accessor`) | `data-scale="1"…"5"` on the cell: its base is `--prime-color-chart-scale-<n>`, text `--prime-color-chart-scale-fg` on 4–5; the range spans all rows (or the fixed `min` / `max`), so paging and sorting never change a shade; the value stays in the cell, non-numbers stay plain |
|
|
256
299
|
| filling the parent | `scrollHeight="fill"` | `data-scroll-height="fill"` on the root; the viewport takes the free height |
|
|
257
300
|
| state swap | the body moves between loading, rows, empty and error | the body fades in over `base` (opacity only, the same swap motion as [Crossfade](../crossfade/COMPONENT.md)); the head stays still; nothing moves on the first render |
|
|
258
301
|
|
|
259
|
-
Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data-sticky-header`, `data-sticky-first-column`, `data-sticky-last-column`, `data-
|
|
302
|
+
Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data-sticky-header`, `data-sticky-first-column`, `data-sticky-last-column`, `data-table-width` (`fill` · `auto` · `grow`), `data-highlight-row`, `data-highlight-column`, `data-striped`, `data-loading`, `data-reorderable`, `data-reorder-handle`, `data-selectable`, `data-expandable`, `data-dragging` (while drag-selecting), `data-overflow-start` · `data-overflow-end` (edges that hide columns), `data-scroll-height` (`fill`). Sorting and paging swap rows instantly; everything is still under `prefers-reduced-motion`.
|
|
260
303
|
|
|
261
304
|
## Layout & spacing
|
|
262
305
|
- The root is a layer of the surface ladder (`data-depth`, one above its host): it fills its container on `--prime-color-layer-current`, radius 12, and clips full-bleed head and rows; zebra rows and the sticky column are one ladder step off it, the head two. On a card or in a modal it is the next layer by itself — never wrap it in a Card.
|
|
263
|
-
- `toolbar`
|
|
264
|
-
-
|
|
306
|
+
- A filter stands next to what it filters. `toolbar` is the table's own tools row whenever the page holds more than this table (KPI cards, a chart, another block): its search and filter (`SmartFilter.Toolbar`, chips in `toolbarChips`), `DataTableColumnsMenu`, a table / cards view, bulk actions on the selected rows. When the table is the whole page, «next to the table» is the page's bar: its search and filter stay in `Page.Tools` and `Page.Chips`. Either way the bar keeps what acts on the whole page — the title with its line of data, a period that drives several blocks, sections, the primary action (`Page.Actions`). One control never gets a row of its own.
|
|
307
|
+
- The tools row is the one tool row of the kit (`Page.Tools` lays out the same way): controls in the order written, a search or a line of text (`Typography`) takes the free room, switchers, pickers and buttons keep their content width at the end edge. Under 36rem of table width the search takes a line of its own and a `SegmentedControl` (`fullWidth` unless set) fills the line it shares with the buttons; a search never parts from its filter button (`SmartFilter.Toolbar` keeps both). Wrap the table in `SmartFilter.Root` so the toolbar and the chips share one state.
|
|
308
|
+
- Toolbar padding `--prime-space-3` × cell padding, items `gap: --prime-space-2`, chips 12 under the row; below 30rem of table width the footer stacks.
|
|
265
309
|
- The footer has a hairline on top: range on the left, Pagination on the right. The range shows only with more than one page or with infinite scroll.
|
|
266
310
|
- Columns scroll sideways inside the table, never the page; pin the identifying column with `stickyFirstColumn` and the row actions column with `stickyLastColumn` (it pins the last visible column, so hiding it pins the one before). A 320 px column holds the table without breaking it.
|
|
267
311
|
- While columns are hidden past an edge, that edge shows a `--prime-space-4` shadow (`--prime-color-bg-edge-shadow`) over the content; it takes no space and follows RTL. With `stickyFirstColumn` the start shadow falls from the pinned column's end edge; with `stickyLastColumn` the end shadow falls from the pinned column's start edge. With `stickyHeader` the pinned head cells hold both corners.
|
|
268
|
-
-
|
|
269
|
-
-
|
|
312
|
+
- Layers, bottom to top: every cell that scrolls · the pinned columns (lead cells, the first and the last column, the end grip cell) · sticky group headers · the sticky head · the corners (head cells pinned to a column edge). Each cell keeps what it holds inside its own layer, so a control in a scrolled cell — a Switch, an icon Button, a focus ring — always passes under the pinned columns and the head, whatever its own stacking. A head label truncates inside its cell and never runs under the next one.
|
|
313
|
+
- Fewer columns, less scrolling: offer the column chooser (the key column and the row-actions column `hideable: false`). The kit stores nothing: keep `onHiddenColumnsChange`'s list where the app keeps settings and pass it back as `hiddenColumns`. Never hide columns silently by width.
|
|
314
|
+
- The column chooser has one place per table. Over the row actions (`columnsMenu`): an icon-only ghost neutral Button one tier below the table (the `view.columns` icon; `labels.columns` is its name and its Tooltip) in the head cell of the row-actions column (the last visible column, `hideHeader`), lined up with the ⋯ buttons under it, sticking with that column under `stickyLastColumn`. Without row actions: `DataTableColumnsMenu` in the page's tools (`Page.Tools`, or the host's own toolbar) — a soft neutral Button with the icon, `labels.columns` and a Badge with the count of shown columns, at the host tier — bound to the same `hiddenColumns` (and `columnOrder`) state the table reads. Both open the same Dropdown: a `labels.showColumns` group heading over one check item per column (the check at the end).
|
|
315
|
+
- Reorderable rows: the grip is the fixed `xs` `Dnd.Handle`. At the end it takes a trailing cell (the grip plus the end padding), after the row actions; at the start a leading cell before the checkboxes (the first column's padding is the gap to it). Rows that are their own grip (`row`) add no cell.
|
|
316
|
+
- Reorderable columns move only between the columns that hold their place: the selection, toggle, grip and trailing cells never move, a pinned first or last column stays at its edge, a `hideHeader` (row actions) or `reorderable: false` column keeps its slot. A hidden column keeps its slot in `columnOrder` too, so showing it again puts it back where it was. Keep `columnOrder` with `hiddenColumns` where the app keeps settings. The chooser's Dropdown opens below (lined up with the trigger's end edge over the row actions), lists every column in table order and ends with `labels.showAllColumns` (and `labels.resetColumnOrder` with `columnOrder`); a `hideable: false` column stays checked and disabled, and the last visible column cannot be hidden. With `columnOrder` the items are a `Dnd.Sortable` list, a grip (or a lock) ending each item after its check mark.
|
|
270
317
|
- Row height per `size` is public: `--prime-table-row-height-xs` · `-s` · `-m` · `-l` · `-xl` (36 · 36 · 44 · 52 · 52). A placeholder outside the table that stands in its geometry (a heatmap, a matrix skeleton) uses the same token.
|
|
271
318
|
- A row that opens keeps controls out of its key cell (that cell is the link); controls in other cells stand above the row's link. Mark a custom widget with `data-row-click-ignore`.
|
|
272
319
|
- Group headers: the name at the start, totals at the end (`renderGroupHeader` returns both, the kit lays them out across the visible width), head-tier text in `text-secondary`, row height = the head height. With collapsible groups the chevron toggle (the expand toggle's ghost Button) leads the header at the cell padding.
|
|
@@ -275,7 +322,7 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
275
322
|
- Long data needs no slicing: past 100 rows a body in a bounded viewport (`scrollHeight`, `"fill"`, `paging="infinite"`) renders only the rows around the view; spacer rows keep the scrollbar exact, the focused row stays rendered, rows carry `aria-rowindex` and the table `aria-rowcount`. Grouped (`getRowGroup`), reorderable (`onReorder`) and `renderExpanded` bodies, and a table the page scrolls, render every row. The browser's find-in-page sees only rendered rows: give a long table a search (the page's `SmartFilter`).
|
|
276
323
|
- `scrollHeight="fill"` needs a parent with a height: a flex or grid column with `min-height: 0`, `Page.Root fill` in an `AppShell.Root fillViewport`, or a sized pane.
|
|
277
324
|
- The sticky head stops sticking on screens lower than 480px (landscape phones), so it never takes a large share of the height.
|
|
278
|
-
- Cell content: text, two-line text (title + `caption`), an Avatar with text, a Badge, or a control one tier down (`s` in an `m` table).
|
|
325
|
+
- Cell content: text, two-line text (title + `caption`), an Avatar with text, a Badge, or a control one tier down (`s` in an `m` table). The column chooser in the head uses `s` in an `m` table; toolbar controls take the table's tier (leave them without `size`).
|
|
279
326
|
- Column widths: `width` / `minWidth` / `maxWidth` strings; never size cells with custom CSS.
|
|
280
327
|
- Sub-rows indent by `--dt-indent` per level (avatar of the tier + gap); override it on the root for other leading content.
|
|
281
328
|
|
|
@@ -286,24 +333,26 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
286
333
|
|
|
287
334
|
| Key | Action |
|
|
288
335
|
|---|---|
|
|
289
|
-
| `Tab` | Moves through the toolbar, sort buttons, the column chooser (`columnsMenu`), row handles, checkboxes, expand toggles, row links, rows that open in place and Pagination. |
|
|
336
|
+
| `Tab` | Moves through the toolbar, sort buttons, plain headers that move their column (`columnOrder`), the column chooser (`columnsMenu`, `DataTableColumnsMenu`), row handles, checkboxes, expand toggles, row links, rows that open in place or move themselves (`reorderHandle="row"`) and Pagination. |
|
|
290
337
|
| `Enter` · `Space` | Sorts by the column, toggles the row or the group, or opens the focused row (`onRowClick`); Enter on a row link follows it. |
|
|
291
338
|
| `Space` | Toggles the focused row checkbox. |
|
|
292
339
|
| `Shift + Space` | Selects the range from the last toggled row. |
|
|
293
|
-
| `Alt + ↑` · `Alt + ↓` | On a row handle (`onReorder`), moves the row one place up or down. |
|
|
294
|
-
| `
|
|
340
|
+
| `Alt + ↑` · `Alt + ↓` | On a row handle, or on the row itself with `reorderHandle="row"` (`onReorder`), moves the row one place up or down; on an item of the column chooser (`columnOrder`), moves that column one place. |
|
|
341
|
+
| `Alt + ←` · `Alt + →` | On a header (its sort button, or the plain header) with `columnOrder`, moves the column one place back or on; focus stays on it. A column that holds its place does not move and no column steps over it. The direction follows the writing direction (`dir="rtl"`: ← goes on). |
|
|
342
|
+
| `Escape` | Cancels a row drag or a header drag; the row or the headers glide back, the body never moved. |
|
|
295
343
|
|
|
296
344
|
### ARIA
|
|
297
345
|
<!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
|
|
298
346
|
|
|
299
|
-
- Native `<table>` semantics; head cells are `scope="col"` (the
|
|
347
|
+
- Native `<table>` semantics; head cells are `scope="col"` (the head cells of the grip columns are `<td>`); sortable heads hold a `<button>` and expose `aria-sort`; the whole head cell shows the inset focus ring.
|
|
348
|
+
- With `columnOrder` the head row is a `Dnd.Sortable`: a header that moves its column carries `aria-keyshortcuts` on its focus target (the sort button; a plain header is a tab stop of its own with Dnd's role description); a pick-up, a drop and a keyboard move are announced by Dnd («Сумма: позиция 3 из 7»). In the column chooser each item keeps `menuitemcheckbox` with `aria-keyshortcuts`, its grip (`labels.reorderColumn`) is out of the Tab order, a column that holds its place shows a lock named «Закреплено».
|
|
300
349
|
- A link row has one link for assistive tech: the `<a>` in the key cell, named by its text. Its copies over the other cells are `aria-hidden` and `tabindex="-1"`, so the pointer can use the whole row. An in-place row is focusable itself (`tabindex="0"`) and reads as its cells. Either way the focused row shows a ring around all its cells.
|
|
301
350
|
- A row never opens from a control inside it (links, buttons, fields, `label`, `summary`, ARIA widgets, `data-row-click-ignore`), from a text selection, or from a React event that bubbled out of a portal (a ⋯ menu item).
|
|
302
351
|
- A group header is a `<th scope="rowgroup">` across every column, first in its own `<tbody>`.
|
|
303
|
-
- The column chooser
|
|
352
|
+
- The column chooser is a menu button: the icon one (`columnsMenu`) named by `labels.columns` (its tooltip says the same), `DataTableColumnsMenu` by its text and count. Its items are a group named `labels.showColumns` of `menuitemcheckbox` with `aria-checked`; the key column is `aria-disabled`. In the row-actions head cell the column keeps its own name (`aria-labelledby`).
|
|
304
353
|
- Row checkboxes are named by `labels.selectRow` with `{label}` from `getRowLabel` (pass it so the names are unique); the head checkbox by `labels.selectAll`, `indeterminate` on a partial selection.
|
|
305
354
|
- After a selection change a polite live region announces `labels.selectedCount`; selected rows carry `aria-selected`.
|
|
306
|
-
- A row handle is a button named by `labels.reorderRow` with `aria-keyshortcuts`; a pick-up, a drop and a keyboard move are announced in a polite live region («Договор: позиция 2 из 6»). While its move is saved the row is `aria-busy`; while the table is sorted the handles are disabled and a tooltip gives `labels.reorderSorted
|
|
355
|
+
- A row handle is a button named by `labels.reorderRow` with `aria-keyshortcuts`; with `reorderHandle="row"` the row itself is the tab stop with `aria-keyshortcuts` and a role description, and reads as its cells. A pick-up, a drop and a keyboard move are announced in a polite live region («Договор: позиция 2 из 6»). While its move is saved the row is `aria-busy`; while the table is sorted the handles are disabled and a tooltip gives `labels.reorderSorted` (rows that are their own grip just stop moving).
|
|
307
356
|
- The expand toggle is a button with `aria-expanded`, `aria-controls` (detail row or sub-rows) and `labels.expand` / `labels.collapse`.
|
|
308
357
|
- A group toggle (collapsible groups) is a button in the group header with `aria-expanded`, `aria-controls` (the group's rows, while open; not on reorderable rows, which carry no id) and `labels.expandGroup` / `labels.collapseGroup` with `{group}`.
|
|
309
358
|
- A column `description` describes its header cell (`aria-describedby`); its tooltip opens from the sort button, or from the info glyph (a tab stop named `labels.columnInfo`) on a plain header.
|
|
@@ -324,8 +373,11 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
324
373
|
| `selectedCount` | `"Выбрано: {count}"` | Polite announcement after the selection changes. |
|
|
325
374
|
| `expand` | `"Развернуть строку {label}"` | Name of the toggle of a collapsed row. |
|
|
326
375
|
| `collapse` | `"Свернуть строку {label}"` | Name of the toggle of an expanded row. |
|
|
327
|
-
| `columns` | `"Колонки"` | Name and tooltip of the
|
|
376
|
+
| `columns` | `"Колонки"` | Name and tooltip of the icon trigger (`columnsMenu`), the text of `DataTableColumnsMenu`; `{shown}` and `{total}` are column counts. |
|
|
377
|
+
| `showColumns` | `"Показывать"` | The heading of the chooser's list of columns. |
|
|
328
378
|
| `showAllColumns` | `"Показать все"` | The column chooser item that shows every column again. |
|
|
379
|
+
| `resetColumnOrder` | `"Исходный порядок"` | The column chooser item that puts the order of `columns` back (`columnOrder`). |
|
|
380
|
+
| `reorderColumn` | `"Переместить колонку {label}"` | Name of a column's grip in the column chooser (`columnOrder`); `{label}` is its header. |
|
|
329
381
|
| `reorderRow` | `"Переместить строку {label}"` | `aria-label` of a row's drag handle (`onReorder`); `{label}` is `getRowLabel(row)`. |
|
|
330
382
|
| `reorderSorted` | `"Порядок задан сортировкой. Сбросьте её, чтобы перемещать строки"` | Tooltip of the disabled handles while the table is sorted by a column. |
|
|
331
383
|
| `expandGroup` | `"Развернуть группу {group}"` | Name of the toggle of a collapsed group; `{group}` is the group key. |
|
|
@@ -342,7 +394,7 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
342
394
|
| [trend.tsx](examples/trend.tsx) | Clients with revenue by month as a compact Sparkline in a cell of each row, next to the half-year total — `cell`, `width`. |
|
|
343
395
|
| [content-width.tsx](examples/content-width.tsx) | A short lookup table sized by its content with a centered column, and a `grow` column that takes the free width and wraps — `fullWidth`, `align`, `grow`. |
|
|
344
396
|
| [appearance.tsx](examples/appearance.tsx) | Row styling: zebra rows without lines, a column wash under the pointer, a headless key/value list — `striped`, `rowDividers`, `highlightColumnOnHover`, `columnDividers`, `showHeader`. |
|
|
345
|
-
| [toolbar.tsx](examples/toolbar.tsx) | A campaign table with its own search
|
|
397
|
+
| [toolbar.tsx](examples/toolbar.tsx) | A campaign table with its own tools row: the search takes the free room, the status switch keeps its width at the end, and on a narrow table they wrap; the empty text when nothing matches — `toolbar`, `empty`. |
|
|
346
398
|
| [selection.tsx](examples/selection.tsx) | Team members with checkboxes: Shift+click range, press-and-drag, «select all» and bulk actions in the toolbar — `selectable`, `selected`, `onSelectedChange`, `getRowLabel`. |
|
|
347
399
|
| [nested-rows.tsx](examples/nested-rows.tsx) | Partners with their expense lines: sub-rows indented under the parent's name, a chevron toggle, together with selection and sorting — `getRowChildren`, `expanded`, `onExpandedChange`. |
|
|
348
400
|
| [detail-panel.tsx](examples/detail-panel.tsx) | Order details in a full-width row under an expanded order, aligned with the first content column; a cancelled order has nothing to show and gets no toggle — `renderExpanded`, `defaultExpanded`, `isRowExpandable`. |
|
|
@@ -350,15 +402,16 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
350
402
|
| [actions-column.tsx](examples/actions-column.tsx) | Orders wider than the table: the order number stays at the start, the ⋯ row actions at the end with the column chooser in their header cell, and the columns between scroll sideways — `stickyFirstColumn`, `stickyLastColumn`, `columnsMenu`. |
|
|
351
403
|
| [row-link.tsx](examples/row-link.tsx) | Invoices whose rows are links: ⌘-click or a middle click opens a new tab, a plain click routes inside the app, the totals row and the ⋯ menu never open a row — `getRowHref`, `onRowClick`, `isRowClickable`. |
|
|
352
404
|
| [row-open.tsx](examples/row-open.tsx) | Deals that open in a side panel: a click, or Enter and Space on the focused row; the panel keeps the last deal while it slides away — `onRowClick`. |
|
|
353
|
-
| [reorder.tsx](examples/reorder.tsx) | Pipeline stages put in order by the
|
|
405
|
+
| [reorder.tsx](examples/reorder.tsx) | Pipeline stages put in order by a grip at the end of the row, at its start or by the whole row, or with Alt + ↑ / ↓; each move is saved with a spinner in its grip, and sorting by a column turns reordering off — `onReorder`, `reorderHandle`, `getRowLabel`. |
|
|
354
406
|
| [groups.tsx](examples/groups.tsx) | Payments grouped by month with the month's total in its header row; headers stay on top while their rows scroll, a past month folds away — `getRowGroup`, `renderGroupHeader`, `stickyGroupHeaders`, `defaultCollapsedGroups`. |
|
|
355
|
-
| [columns-visibility.tsx](examples/columns-visibility.tsx) | Orders
|
|
407
|
+
| [columns-visibility.tsx](examples/columns-visibility.tsx) | Orders without a row-actions column: the column chooser stands in the tools above the table with the count of shown columns; the choice outlives a reload in local storage, the order number always stays — `DataTableColumnsMenu`, `hiddenColumns`, `onHiddenColumnsChange`, `hideable`. |
|
|
408
|
+
| [column-order.tsx](examples/column-order.tsx) | Orders whose columns a manager puts in place by dragging a header — it moves alone and the gap opens among the headers where it lands — or with Alt + ← / →, and shows, hides and reorders in the chooser over the row menu; the order number stays first and the row menu last, and the choice outlives a reload — `columnOrder`, `onColumnOrderChange`, `reorderable`, `columnsMenu`, `hiddenColumns`. |
|
|
356
409
|
| [infinite-scroll.tsx](examples/infinite-scroll.tsx) | An audit log that reveals loaded rows in batches and then asks the server for more — `paging`, `infiniteBatchSize`, `hasMore`, `loadingMore`, `onLoadMore`. |
|
|
357
|
-
| [server.tsx](examples/server.tsx) | A CRM deal list the server sorts, pages and searches; a new search returns to page 1 — `source`, `rowCount`, `sort`, `page`, `loading`. |
|
|
410
|
+
| [server.tsx](examples/server.tsx) | A CRM deal list the server sorts, pages and searches, with its tools in its own row — search, filter and column chooser, the active filters under them; a new search returns to page 1 — `source`, `rowCount`, `sort`, `page`, `loading`, `toolbar`, `toolbarChips`. |
|
|
358
411
|
| [fill-height.tsx](examples/fill-height.tsx) | An audit log that fills the height of its panel and loads older entries while scrolling; the status shows while the request is pending — `scrollHeight`, `paging`, `onLoadMore`. |
|
|
359
412
|
| [states.tsx](examples/states.tsx) | Loading skeleton, an empty period and a load error with a retry: the head stays, only the body changes — `loading`, `loadingRows`, `empty`, `loadError`. |
|
|
360
413
|
| [controlled.tsx](examples/controlled.tsx) | Sort, page and the column choice owned by the parent (a URL or a store): a header click goes asc → desc → none, a new sort returns to page 1 — `sort`, `onSortChange`, `page`, `onPageChange`, `hiddenColumns`, `onHiddenColumnsChange`. |
|
|
361
|
-
| [narrow.tsx](examples/narrow.tsx) | A 320 px support queue: columns scroll inside the table with the first one pinned, the column chooser stays
|
|
414
|
+
| [narrow.tsx](examples/narrow.tsx) | A 320 px support queue: columns scroll inside the table with the first one pinned, the column chooser stays in reach above it, the footer reflows and the pager turns compact — `stickyFirstColumn`, `DataTableColumnsMenu`, `size`. |
|
|
362
415
|
| [screen.tsx](examples/screen.tsx) | A list page: the title, a line of figures, search, filters and the one primary action in its top bar, a DataTable of invoices with statuses, row actions, pages and a filtered empty state — `empty`, `pageSize`, `stickyFirstColumn`. |
|
|
363
416
|
|
|
364
417
|
## Mistakes
|
|
@@ -370,13 +423,21 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
370
423
|
- Wrapping the table in a Card for a border → the table is already a bounded block.
|
|
371
424
|
- A custom checkbox column → use `selectable` (range, drag and announcements built in).
|
|
372
425
|
- Up / down arrow buttons in a cell to order rows → use `onReorder` (drag, touch hold, Alt + arrows, announcements, server save built in).
|
|
426
|
+
- A grip column added by hand, or the grip moved with CSS → `reorderHandle` (`end` by default, `start`, or `row`).
|
|
427
|
+
- `reorderHandle="row"` on link rows (`getRowHref`) or rows with text to copy → the link takes every press and the text cannot be selected; keep a grip (`end`).
|
|
428
|
+
- Up / down arrows or a «Порядок колонок» dialog to reorder columns, or a hand-made header drag → `columnOrder` (header drag, Alt + ← / →, chooser grips, announcements built in); keep it with `hiddenColumns` in the user's settings.
|
|
429
|
+
- Moving body cells while a header is carried, or a drag that carries the whole column → the head row alone moves; the body takes the order once, on the drop.
|
|
430
|
+
- Removing a column from `columns` to «delete» it for a user → hide it (`hiddenColumns`); its data and its place in `columnOrder` stay.
|
|
373
431
|
- Applying the new order before the server answers and undoing it by hand → return the request's promise from `onReorder` and apply after it resolves: the table keeps the row in place meanwhile and moves it back on a rejection.
|
|
374
432
|
- A long table in a `scrollHeight` window without `stickyHeader` → the head scrolls away.
|
|
375
433
|
- `paging="none"` for hundreds of rows → use `pages` or `infinite`.
|
|
376
|
-
- Dropping columns below a breakpoint → people lose data without knowing it;
|
|
377
|
-
- A hand-built column chooser from Dropdown items → `columnsMenu` (key column, last visible column, «Показать
|
|
378
|
-
-
|
|
379
|
-
-
|
|
434
|
+
- Dropping columns below a breakpoint → people lose data without knowing it; offer the column chooser.
|
|
435
|
+
- A hand-built column chooser from Dropdown items → `columnsMenu` over the row actions, else `DataTableColumnsMenu` (key column, last visible column, «Показать все», reorder built in).
|
|
436
|
+
- `columnsMenu` on a table without a row-actions column → nothing renders and the table warns; put `DataTableColumnsMenu` beside the table's other tools (its `toolbar`, or `Page.Tools` on a page that is the table), bound to the same `hiddenColumns`.
|
|
437
|
+
- A toolbar with one button (the column chooser, «Экспорт», «Создать») → a whole row above the table for one control; the chooser stands over the row actions or beside the table's search, the page's actions go to `Page.Actions`.
|
|
438
|
+
- The table's search, filter, chips or column chooser in the page's bar above KPI cards or a chart they do not filter → people read them as filters of the whole page; put them in the table's `toolbar` / `toolbarChips` and keep the bar for the title, the period that drives every block and the primary action.
|
|
439
|
+
- A period that drives the cards and the table, the page's sections, figures or actions in `toolbar` → they act on the whole page: `Page.Tools`, `Page.Actions`, `Page.Description`.
|
|
440
|
+
- A hand-made flex row or a `SmartFilter.Root` with its own column inside `toolbar` → pass the controls to `toolbar` as they are (it lays them out), the chips to `toolbarChips`, and wrap the DataTable in `SmartFilter.Root`.
|
|
380
441
|
- Letting the key column be hidden → set `hideable: false` on it, so every row keeps its name.
|
|
381
442
|
- Wrapping the table in an `overflow-x` box of your own → it already scrolls inside and shows the edge shadows.
|
|
382
443
|
- A row actions column that scrolls out of view in a wide table, or a hand-made `position: sticky` on it → make it the last column and set `stickyLastColumn`.
|
|
@@ -393,5 +454,5 @@ Root attributes: `data-size`, `data-row-dividers`, `data-column-dividers`, `data
|
|
|
393
454
|
- A new search that keeps the current page → set `page` to 1 together with the search.
|
|
394
455
|
|
|
395
456
|
## Related
|
|
396
|
-
- **Built from:** [Checkbox](../checkbox/COMPONENT.md), [Button](../button/COMPONENT.md), [Dropdown](../dropdown/COMPONENT.md) (the column chooser), [Pagination](../pagination/COMPONENT.md), [ScrollContainer](../scroll-container/COMPONENT.md), [EmptyPage](../empty-page/COMPONENT.md), [VisuallyHidden](../visually-hidden/COMPONENT.md) (loading status, selection announcement), [Skeleton](../skeleton/COMPONENT.md) (loading rows, in the table tier), [Dnd](../dnd/COMPONENT.md) (`onReorder`: `Dnd.Sortable as="tbody"`, `Dnd.Handle`), [Tooltip](../tooltip/COMPONENT.md) (handle hint, a column `description`, the column chooser), Icon (sort arrows, the info glyph of a column `description`, the expand chevron, the column chooser trigger); `useVirtualRows` (the window of a long body)
|
|
457
|
+
- **Built from:** [Checkbox](../checkbox/COMPONENT.md), [Button](../button/COMPONENT.md), [Dropdown](../dropdown/COMPONENT.md) (the column chooser), [Pagination](../pagination/COMPONENT.md), [ScrollContainer](../scroll-container/COMPONENT.md), [EmptyPage](../empty-page/COMPONENT.md), [VisuallyHidden](../visually-hidden/COMPONENT.md) (loading status, selection announcement), [Skeleton](../skeleton/COMPONENT.md) (loading rows, in the table tier), [Dnd](../dnd/COMPONENT.md) (`onReorder`: `Dnd.Sortable as="tbody"`, `Dnd.Handle`; `columnOrder`: the head row as a `Dnd.Sortable as="tr"`, the chooser's `Dnd.Sortable` list, `Dnd.Handle`, the lock), [Badge](../badge/COMPONENT.md) (the count on `DataTableColumnsMenu`), [Tooltip](../tooltip/COMPONENT.md) (handle hint, a column `description`, the column chooser), Icon (sort arrows, the info glyph of a column `description`, the expand chevron, the column chooser trigger); `useVirtualRows` (the window of a long body); `useFlipList` (the head cells glide to an order set from outside the head row); `enterMotion` (the moved column fades in); `ToolRow` (the `toolbar` row and the `toolbarChips` line) with [SmartFilter](../smart-filter/COMPONENT.md), [Input](../input/COMPONENT.md) and [Typography](../typography/COMPONENT.md) (what grows in the tool row), [SegmentedControl](../segmented-control/COMPONENT.md), [Datepicker](../datepicker/COMPONENT.md) and [Select](../select/COMPONENT.md) (what fills a short line; the first two get `fullWidth`)
|
|
397
458
|
- **See also:** [Sparkline](../sparkline/COMPONENT.md) (`layout="compact"` in a cell), [SmartFilter](../smart-filter/COMPONENT.md), [Page](../../layout/page/COMPONENT.md) (`Page.Tools`, `Page.Chips`), [Gantt](../gantt/COMPONENT.md), [Kanban](../kanban/COMPONENT.md), [Badge](../badge/COMPONENT.md), [Avatar](../avatar/COMPONENT.md), [Card](../card/COMPONENT.md)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/** Orders whose columns a manager puts in place by dragging a header — it moves alone and the gap opens among the headers where it lands — or with Alt + ← / →, and shows, hides and reorders in the chooser over the row menu; the order number stays first and the row menu last, and the choice outlives a reload — `columnOrder`, `onColumnOrderChange`, `reorderable`, `columnsMenu`, `hiddenColumns`. */
|
|
2
|
+
import { Badge, Button, DataTable, type DataTableColumn, Dropdown, Icon } from "prime-ui-kit";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
type Status = "paid" | "shipped" | "waiting";
|
|
6
|
+
type Order = {
|
|
7
|
+
id: string;
|
|
8
|
+
client: string;
|
|
9
|
+
manager: string;
|
|
10
|
+
city: string;
|
|
11
|
+
status: Status;
|
|
12
|
+
date: string;
|
|
13
|
+
total: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const STATUS: Record<Status, { label: string; color: "green" | "blue" | "orange" }> = {
|
|
17
|
+
paid: { label: "Оплачен", color: "green" },
|
|
18
|
+
shipped: { label: "Отгружен", color: "blue" },
|
|
19
|
+
waiting: { label: "Ждёт оплаты", color: "orange" },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ORDERS: Order[] = [
|
|
23
|
+
{
|
|
24
|
+
id: "№ 1040",
|
|
25
|
+
client: "ООО «Северный ветер»",
|
|
26
|
+
manager: "Ольга Смирнова",
|
|
27
|
+
city: "Москва",
|
|
28
|
+
status: "paid",
|
|
29
|
+
date: "02.10.2026",
|
|
30
|
+
total: 184_500,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "№ 1041",
|
|
34
|
+
client: "ИП Козлов",
|
|
35
|
+
manager: "Игорь Волков",
|
|
36
|
+
city: "Казань",
|
|
37
|
+
status: "waiting",
|
|
38
|
+
date: "03.10.2026",
|
|
39
|
+
total: 27_900,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "№ 1042",
|
|
43
|
+
client: "АО «Технопарк»",
|
|
44
|
+
manager: "Ольга Смирнова",
|
|
45
|
+
city: "Санкт-Петербург",
|
|
46
|
+
status: "shipped",
|
|
47
|
+
date: "04.10.2026",
|
|
48
|
+
total: 612_000,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "№ 1043",
|
|
52
|
+
client: "ООО «Лето»",
|
|
53
|
+
manager: "Мария Орлова",
|
|
54
|
+
city: "Екатеринбург",
|
|
55
|
+
status: "paid",
|
|
56
|
+
date: "05.10.2026",
|
|
57
|
+
total: 58_350,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const RUB = new Intl.NumberFormat("ru-RU", {
|
|
62
|
+
style: "currency",
|
|
63
|
+
currency: "RUB",
|
|
64
|
+
maximumFractionDigits: 0,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const COLUMNS: DataTableColumn<Order>[] = [
|
|
68
|
+
{ id: "id", header: "Заказ", accessor: "id", hideable: false, reorderable: false },
|
|
69
|
+
{ id: "client", header: "Клиент", accessor: "client", sortable: true },
|
|
70
|
+
{ id: "manager", header: "Менеджер", accessor: "manager" },
|
|
71
|
+
{ id: "city", header: "Город", accessor: "city" },
|
|
72
|
+
{
|
|
73
|
+
id: "status",
|
|
74
|
+
header: "Статус",
|
|
75
|
+
cell: (row) => (
|
|
76
|
+
<Badge.Root color={STATUS[row.status].color}>{STATUS[row.status].label}</Badge.Root>
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
{ id: "date", header: "Дата", accessor: "date", numeric: true },
|
|
80
|
+
{
|
|
81
|
+
id: "total",
|
|
82
|
+
header: "Сумма",
|
|
83
|
+
accessor: "total",
|
|
84
|
+
sortable: true,
|
|
85
|
+
numeric: true,
|
|
86
|
+
cell: (row) => RUB.format(row.total),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "actions",
|
|
90
|
+
header: "Действия",
|
|
91
|
+
hideHeader: true,
|
|
92
|
+
hideable: false,
|
|
93
|
+
align: "end",
|
|
94
|
+
cell: (row) => (
|
|
95
|
+
<Dropdown.Root>
|
|
96
|
+
<Dropdown.Trigger>
|
|
97
|
+
<Button.Root variant="ghost" tone="neutral" aria-label={`Действия с заказом ${row.id}`}>
|
|
98
|
+
<Button.Icon>
|
|
99
|
+
<Icon name="action.more" />
|
|
100
|
+
</Button.Icon>
|
|
101
|
+
</Button.Root>
|
|
102
|
+
</Dropdown.Trigger>
|
|
103
|
+
<Dropdown.Content>
|
|
104
|
+
<Dropdown.Item>Открыть заказ</Dropdown.Item>
|
|
105
|
+
<Dropdown.Item>Скачать счёт</Dropdown.Item>
|
|
106
|
+
</Dropdown.Content>
|
|
107
|
+
</Dropdown.Root>
|
|
108
|
+
),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
/** The kit keeps nothing: the app stores the order and the hidden columns where it likes. */
|
|
113
|
+
const STORAGE_KEY = "orders.columns";
|
|
114
|
+
type ColumnView = { order: string[]; hidden: string[] };
|
|
115
|
+
const DEFAULT_VIEW: ColumnView = { order: [], hidden: ["city"] };
|
|
116
|
+
|
|
117
|
+
function readView(): ColumnView {
|
|
118
|
+
try {
|
|
119
|
+
const saved = window.localStorage.getItem(STORAGE_KEY);
|
|
120
|
+
return saved
|
|
121
|
+
? { ...DEFAULT_VIEW, ...(JSON.parse(saved) as Partial<ColumnView>) }
|
|
122
|
+
: DEFAULT_VIEW;
|
|
123
|
+
} catch {
|
|
124
|
+
return DEFAULT_VIEW;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default function DataTableColumnOrderExample() {
|
|
129
|
+
const [view, setView] = React.useState(readView);
|
|
130
|
+
|
|
131
|
+
const changeView = (next: ColumnView) => {
|
|
132
|
+
setView(next);
|
|
133
|
+
try {
|
|
134
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
135
|
+
} catch {
|
|
136
|
+
// Private mode or a full storage: the choice lasts until the reload.
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<DataTable
|
|
142
|
+
columns={COLUMNS}
|
|
143
|
+
columnOrder={view.order}
|
|
144
|
+
onColumnOrderChange={(order) => changeView({ ...view, order })}
|
|
145
|
+
hiddenColumns={view.hidden}
|
|
146
|
+
onHiddenColumnsChange={(hidden) => changeView({ ...view, hidden })}
|
|
147
|
+
rows={ORDERS}
|
|
148
|
+
getRowKey={(row) => row.id}
|
|
149
|
+
columnsMenu
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
/** Orders
|
|
2
|
-
import { Badge, DataTable, type DataTableColumn } from "prime-ui-kit";
|
|
1
|
+
/** Orders without a row-actions column: the column chooser stands in the tools above the table with the count of shown columns; the choice outlives a reload in local storage, the order number always stays — `DataTableColumnsMenu`, `hiddenColumns`, `onHiddenColumnsChange`, `hideable`. */
|
|
2
|
+
import { Badge, DataTable, type DataTableColumn, DataTableColumnsMenu } from "prime-ui-kit";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
+
import styles from "./examples.module.css";
|
|
6
|
+
|
|
5
7
|
type Status = "paid" | "shipped" | "waiting";
|
|
6
8
|
type Order = {
|
|
7
9
|
id: string;
|
|
@@ -129,14 +131,21 @@ export default function DataTableColumnsVisibilityExample() {
|
|
|
129
131
|
};
|
|
130
132
|
|
|
131
133
|
return (
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
<div className={styles.withTools}>
|
|
135
|
+
<div className={styles.tools}>
|
|
136
|
+
<DataTableColumnsMenu
|
|
137
|
+
columns={COLUMNS}
|
|
138
|
+
hiddenColumns={hidden}
|
|
139
|
+
onHiddenColumnsChange={changeHidden}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<DataTable
|
|
143
|
+
columns={COLUMNS}
|
|
144
|
+
hiddenColumns={hidden}
|
|
145
|
+
rows={ORDERS}
|
|
146
|
+
getRowKey={(row) => row.id}
|
|
147
|
+
stickyFirstColumn
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
141
150
|
);
|
|
142
151
|
}
|