@jielga/tmdatagrid 2.0.0-beta.0 → 2.0.0-beta.2
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 +32 -34
- package/dist/index.d.ts +164 -102
- package/dist/index.js +2060 -1705
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +37 -34
- package/skills/cell-selection/SKILL.md +19 -19
- package/skills/columns/SKILL.md +9 -9
- package/skills/data/SKILL.md +53 -53
- package/skills/editing/SKILL.md +152 -311
- package/skills/editing/references/common-mistakes.md +177 -0
- package/skills/editing/references/editing-api.md +56 -28
- package/skills/editing/references/editors-and-validation.md +26 -19
- package/skills/filtering/SKILL.md +51 -50
- package/skills/getting-started/SKILL.md +10 -10
- package/skills/grouping/SKILL.md +41 -43
- package/skills/options/SKILL.md +4 -4
- package/skills/rows/SKILL.md +59 -59
- package/skills/rows/references/rows-api.md +7 -7
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +11 -10
- package/src/tmdatagrid/components/TMDataGrid.module.css +13 -1
- package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +20 -8
- package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +29 -18
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +5 -8
- package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
- package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +246 -86
- package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +176 -77
- package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +11 -6
- package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +9 -5
- package/src/tmdatagrid/components/TMDataGridTable.module.css +47 -3
- package/src/tmdatagrid/components/TMDataGridTable.tsx +101 -40
- package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
- package/src/tmdatagrid/components/icons.ts +1 -0
- package/src/tmdatagrid/core/autosize.ts +30 -6
- package/src/tmdatagrid/core/capabilities.ts +15 -5
- package/src/tmdatagrid/core/cellExport.ts +6 -7
- package/src/tmdatagrid/core/cellNavigation.ts +2 -2
- package/src/tmdatagrid/core/cellRange.ts +6 -6
- package/src/tmdatagrid/core/columnOrdering.ts +30 -1
- package/src/tmdatagrid/core/columnUtils.ts +14 -0
- package/src/tmdatagrid/core/draftCellContext.ts +68 -0
- package/src/tmdatagrid/core/editEngine.ts +111 -37
- package/src/tmdatagrid/core/filterOperators.ts +6 -6
- package/src/tmdatagrid/core/labels.ts +13 -1
- package/src/tmdatagrid/core/labelsSv.ts +4 -0
- package/src/tmdatagrid/core/matchHighlight.ts +3 -3
- package/src/tmdatagrid/core/persistence.ts +3 -3
- package/src/tmdatagrid/core/rowSelection.ts +3 -3
- package/src/tmdatagrid/index.ts +3 -1
- package/src/tmdatagrid/useTMDataGrid.tsx +141 -99
|
@@ -11,7 +11,7 @@ description: >
|
|
|
11
11
|
metadata:
|
|
12
12
|
type: core
|
|
13
13
|
library: '@jielga/tmdatagrid'
|
|
14
|
-
library_version: '2.0.0-beta.
|
|
14
|
+
library_version: '2.0.0-beta.2'
|
|
15
15
|
sources:
|
|
16
16
|
- 'Jielga/TMDataGrid:src/docs/getting-started.md'
|
|
17
17
|
- 'Jielga/TMDataGrid:src/docs/anatomy.md'
|
|
@@ -169,10 +169,10 @@ Pass the row type so `onRowClick` stays typed:
|
|
|
169
169
|
<TMDataGrid.Table<Employee> onRowClick={(row) => open(row.original.id)} />
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
`renderRowContextMenu` fills the dropdown of a Mantine `Menu` the grid
|
|
173
|
-
pointer on a right-click (or a long press). The grid
|
|
174
|
-
|
|
175
|
-
so the render prop only
|
|
172
|
+
`renderRowContextMenu` fills the dropdown of a Mantine `Menu` that the grid
|
|
173
|
+
opens at the pointer on a right-click (or a long press). The grid renders the
|
|
174
|
+
`Menu`, positions it, and closes it on Escape, an outside click, a body scroll
|
|
175
|
+
or an item pick, so the render prop supplies only the contents:
|
|
176
176
|
|
|
177
177
|
```tsx
|
|
178
178
|
<TMDataGrid.Table<Employee>
|
|
@@ -192,11 +192,11 @@ so the render prop only says what goes in it:
|
|
|
192
192
|
/>
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
`cell` is the one that was right-clicked, `table` is
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
`cell` is the one that was right-clicked, `table` is for actions that read the
|
|
196
|
+
selection, and `close` is for dropdown content that is not a `Menu.Item` (those
|
|
197
|
+
close themselves). Return `null` to leave a row without a menu. It is called
|
|
198
|
+
during render, and only for the open row, so it must stay pure: put the work in
|
|
199
|
+
the item handlers. Right-clicking does not change the selection or the
|
|
200
200
|
highlight; the row carries `data-context-menu` while its menu is open. Pass
|
|
201
201
|
`rowContextMenuProps` for `width`, `shadow`, `position` and the rest.
|
|
202
202
|
|
package/skills/grouping/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0-beta.
|
|
17
|
+
library_version: '2.0.0-beta.2'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/TMDataGrid:src/docs/grouping.md'
|
|
20
20
|
- 'Jielga/TMDataGrid:src/docs/summary-row.md'
|
|
@@ -29,8 +29,8 @@ category, and the summary row totals everything along the bottom edge.
|
|
|
29
29
|
|
|
30
30
|
## Grouping
|
|
31
31
|
|
|
32
|
-
On by default, and nothing changes until a column is grouped
|
|
33
|
-
|
|
32
|
+
On by default, and nothing changes until a column is grouped, which users do
|
|
33
|
+
from **Group by …** in any column menu. To open already grouped, seed the
|
|
34
34
|
state:
|
|
35
35
|
|
|
36
36
|
```tsx
|
|
@@ -41,8 +41,8 @@ const grid = useTMDataGrid({
|
|
|
41
41
|
});
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Grouping a column **removes it
|
|
45
|
-
and a generated Group column (`GROUP_COLUMN_ID`) appears at the front, pinned
|
|
44
|
+
Grouping a column **removes it**, since its values have moved into the tree
|
|
45
|
+
lane, and a generated Group column (`GROUP_COLUMN_ID`) appears at the front, pinned
|
|
46
46
|
beside the checkbox lane. Each group row shows its value, its record count and a
|
|
47
47
|
chevron. Grouping a second column nests.
|
|
48
48
|
|
|
@@ -53,9 +53,8 @@ instead of removing them.
|
|
|
53
53
|
|
|
54
54
|
## Aggregation
|
|
55
55
|
|
|
56
|
-
Off
|
|
57
|
-
|
|
58
|
-
and it fills in.
|
|
56
|
+
Off by default. A group row leaves every cell blank except the tree lane. Give a
|
|
57
|
+
column an `aggregationFn` and its group cells fill in.
|
|
59
58
|
|
|
60
59
|
```tsx
|
|
61
60
|
columnHelper.accessor("salary", {
|
|
@@ -81,8 +80,8 @@ leaves the groups where they are.
|
|
|
81
80
|
|
|
82
81
|
## Group rows are not data rows
|
|
83
82
|
|
|
84
|
-
A group row is built on its first child's record, so
|
|
85
|
-
would
|
|
83
|
+
A group row is built on its first child's record, so passing it to a callback
|
|
84
|
+
would pass a row that looks real but is the wrong one. Group rows therefore:
|
|
86
85
|
|
|
87
86
|
- do not fire `onRowClick` or the cell handlers
|
|
88
87
|
- cannot be highlighted, pinned, or given a details panel
|
|
@@ -91,8 +90,8 @@ would hand over a real-looking row that is the wrong one. Group rows therefore:
|
|
|
91
90
|
|
|
92
91
|
A group row's checkbox selects every record under it at any depth, including
|
|
93
92
|
records inside collapsed sub-groups, showing a tick once all are selected and a
|
|
94
|
-
dash while only some are. Only the records reach `rowSelection
|
|
95
|
-
never in it
|
|
93
|
+
dash while only some are. Only the records reach `rowSelection`; a group row is
|
|
94
|
+
never in it, so `getSelectedRowModel()` and the toolbar count do not depend on
|
|
96
95
|
how the tree is arranged. Under `enableMultiRowSelection: false` group rows carry
|
|
97
96
|
no checkbox.
|
|
98
97
|
|
|
@@ -102,14 +101,14 @@ no checkbox.
|
|
|
102
101
|
|
|
103
102
|
**Grouping and the built-in pager do not work together, and grouping wins.** As
|
|
104
103
|
soon as a column is grouped the grid renders the whole tree and relies on
|
|
105
|
-
virtualization
|
|
104
|
+
virtualization. `TMDataGrid.Footer` greys its pager out and replaces the range
|
|
106
105
|
with `Grouped · all N rows`. Ungroup and paging resumes where it left off.
|
|
107
106
|
|
|
108
107
|
This is deliberate. A page can only count one kind of thing, and once the rows
|
|
109
|
-
are a tree neither
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
are a tree neither option works: counting every row splits a group across a page
|
|
109
|
+
boundary, and counting top-level rows redefines "rows per page" as groups per
|
|
110
|
+
page. Rendering the whole tree is the grid's default mode in any case, so only
|
|
111
|
+
the pager is lost.
|
|
113
112
|
|
|
114
113
|
`isPagingActive(table, features)` is exported so a custom pager can grey itself
|
|
115
114
|
out the same way. To have both, page on the server: group there and feed the
|
|
@@ -117,8 +116,8 @@ grid one page of a tree at a time with `manualPagination` and `manualGrouping`.
|
|
|
117
116
|
|
|
118
117
|
## The summary row
|
|
119
118
|
|
|
120
|
-
There is no flag. Give a column a `footer` and the row appears
|
|
121
|
-
|
|
119
|
+
There is no flag. Give a column a `footer` and the row appears. It exists
|
|
120
|
+
whenever at least one visible column defines one.
|
|
122
121
|
|
|
123
122
|
```tsx
|
|
124
123
|
import { aggregateColumn } from "@jielga/tmdatagrid";
|
|
@@ -131,8 +130,8 @@ columnHelper.accessor("salary", {
|
|
|
131
130
|
```
|
|
132
131
|
|
|
133
132
|
`footer` is TanStack's own column option, rendered the way the header is.
|
|
134
|
-
`aggregateColumn({ table, columnId, fn })` computes over every **filtered** row
|
|
135
|
-
all pages, following the filters live
|
|
133
|
+
`aggregateColumn({ table, columnId, fn })` computes over every **filtered** row,
|
|
134
|
+
all pages, following the filters live, through the registered aggregation
|
|
136
135
|
functions, with `fn` defaulting to `"sum"`.
|
|
137
136
|
|
|
138
137
|
```tsx
|
|
@@ -140,9 +139,8 @@ aggregateColumn({ table, columnId: "age", fn: "mean" });
|
|
|
140
139
|
aggregateColumn({ table, columnId: "location", fn: "uniqueCount" });
|
|
141
140
|
```
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
front of them when it is not.
|
|
142
|
+
It follows the filters deliberately. A total that does not change as the user
|
|
143
|
+
narrows the grid is misleading.
|
|
146
144
|
|
|
147
145
|
Pinned columns keep their lanes in the summary row, the generated lanes define
|
|
148
146
|
no `footer` so their cells stay blank, and the row is sticky at
|
|
@@ -153,8 +151,8 @@ no `footer` so their cells stay blank, and the row is sticky at
|
|
|
153
151
|
### CRITICAL Expecting group rows to total automatically
|
|
154
152
|
|
|
155
153
|
Grouping alone leaves every cell blank on a group row, because the grid clears
|
|
156
|
-
TanStack's `aggregationFn: "auto"` default. Nothing errors
|
|
157
|
-
|
|
154
|
+
TanStack's `aggregationFn: "auto"` default. Nothing errors; the tree just looks
|
|
155
|
+
empty.
|
|
158
156
|
|
|
159
157
|
Wrong:
|
|
160
158
|
|
|
@@ -173,9 +171,9 @@ Source: `src/docs/grouping.md` (Aggregation).
|
|
|
173
171
|
### HIGH Looking for the grouped column in the grid
|
|
174
172
|
|
|
175
173
|
`groupedColumnMode` defaults to `"remove"`, so grouping by Department takes the
|
|
176
|
-
Department column out
|
|
177
|
-
column's cells, or a test that queries its header, stops finding it
|
|
178
|
-
|
|
174
|
+
Department column out, since its values are in the tree lane. Code that reads
|
|
175
|
+
that column's cells, or a test that queries its header, stops finding it as soon
|
|
176
|
+
as a user groups.
|
|
179
177
|
|
|
180
178
|
Correct, when the column must stay:
|
|
181
179
|
|
|
@@ -187,19 +185,19 @@ Source: `src/docs/grouping.md` (What grouping does to the grid).
|
|
|
187
185
|
|
|
188
186
|
### HIGH Combining the pager with grouping
|
|
189
187
|
|
|
190
|
-
`enablePagination: true` and a grouped column cannot both
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
`enablePagination: true` and a grouped column cannot both apply. The pager greys
|
|
189
|
+
out instead of paging the tree, so a footer count wired to `getPageCount()`
|
|
190
|
+
reports a number nobody can navigate to. Read
|
|
193
191
|
`isPagingActive(table, features)` before trusting the pager state.
|
|
194
192
|
|
|
195
193
|
Source: `src/docs/grouping.md` (Grouping suspends pagination).
|
|
196
194
|
|
|
197
195
|
### HIGH Handing a group row to a row callback
|
|
198
196
|
|
|
199
|
-
Group rows
|
|
200
|
-
|
|
201
|
-
built from `row.original` on the tree lane acts on one record
|
|
202
|
-
group.
|
|
197
|
+
Group rows do not fire `onRowClick` or the cell handlers, and cannot be pinned,
|
|
198
|
+
expanded or edited. Their `row.original` is an arbitrary child's record, so a
|
|
199
|
+
bulk action built from `row.original` on the tree lane acts on one record
|
|
200
|
+
instead of the group.
|
|
203
201
|
|
|
204
202
|
Correct:
|
|
205
203
|
|
|
@@ -214,16 +212,16 @@ Source: `src/docs/grouping.md` (Group rows are not data rows).
|
|
|
214
212
|
### MEDIUM Totalling the page instead of the data
|
|
215
213
|
|
|
216
214
|
`aggregateColumn` deliberately runs over every filtered row, all pages. Summing
|
|
217
|
-
`table.getRowModel().rows` instead totals only what is currently paged in,
|
|
218
|
-
|
|
219
|
-
it is not even the whole page.
|
|
215
|
+
`table.getRowModel().rows` instead totals only what is currently paged in, and
|
|
216
|
+
under virtualization not even that: only the mounted rows.
|
|
220
217
|
|
|
221
218
|
Source: `src/docs/summary-row.md` (Totalling a column).
|
|
222
219
|
|
|
223
220
|
### MEDIUM Grouping a server-paged grid
|
|
224
221
|
|
|
225
|
-
`manualPagination: true` turns grouping off
|
|
226
|
-
build groups out of an arbitrary slice. Group on the server and
|
|
222
|
+
`manualPagination: true` turns grouping off, because the client holds one page
|
|
223
|
+
and would build groups out of an arbitrary slice. Group on the server and
|
|
224
|
+
declare it.
|
|
227
225
|
|
|
228
226
|
Correct:
|
|
229
227
|
|
|
@@ -247,9 +245,9 @@ Source: `src/docs/grouping.md` (Server-side grids).
|
|
|
247
245
|
| `groupedColumnMode` | Table option | `"reorder" \| "remove" \| false` | `"remove"` | Whether a grouped column leaves the grid or moves to the front. |
|
|
248
246
|
| `manualGrouping` | Table option | `boolean` | `false` | The rows arrive grouped. Required to group a server-paged grid. |
|
|
249
247
|
| `initialState.grouping` | Table option | `string[]` | `[]` | Column ids grouped at mount. A settings slice, so it persists. |
|
|
250
|
-
| `aggregationFn` | Column option | `TMDataGridAggregationName \| fn` | – | How a column fills in its group
|
|
248
|
+
| `aggregationFn` | Column option | `TMDataGridAggregationName \| fn` | – | How a column fills in its group cells. Unset leaves them blank. |
|
|
251
249
|
| `aggregatedCell` | Column option | `(ctx) => ReactNode` | The `cell` renderer | Renders a group row's value differently. |
|
|
252
|
-
| `footer` | Column option | `(ctx) => ReactNode` | – | Renders this column's summary cell
|
|
250
|
+
| `footer` | Column option | `(ctx) => ReactNode` | – | Renders this column's summary cell. Defining one adds the row. |
|
|
253
251
|
| `aggregateColumn` | Export | `({ table, columnId, fn }) => unknown` | `fn: "sum"` | Aggregates over every filtered row, all pages. |
|
|
254
252
|
| `TMDataGridAggregationName` | Export | type | – | The registered function names. |
|
|
255
253
|
| `GROUP_COLUMN_ID` | Export | `"__group__"` | – | Id of the generated tree column. |
|
package/skills/options/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0-beta.
|
|
17
|
+
library_version: '2.0.0-beta.2'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
|
|
20
20
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
|
@@ -47,7 +47,7 @@ rather than forwarded to TanStack.
|
|
|
47
47
|
| --- | --- | --- | --- |
|
|
48
48
|
| `data` | `TData[]` | – | Row data. Keep the reference stable with `useMemo`. |
|
|
49
49
|
| `columns` | `ColumnDef[]` | – | Created with `createTMDataGridColumnHelper`. |
|
|
50
|
-
| `getRowId` | `(row, index) => string` | Row index | Used by row selection and virtualization. Required once `
|
|
50
|
+
| `getRowId` | `(row, index) => string` | Row index | Used by row selection and virtualization. Required once `editing` is set. |
|
|
51
51
|
| `enableRowSelection` | `boolean \| (row) => boolean` | `true` | `false` removes row selection and its checkbox column. |
|
|
52
52
|
| `selectionMode` | `"checkbox" \| "row" \| "checkboxAndHighlight" \| "highlight"` | `"checkbox"` | What selecting looks like and what a row click does. Defined by the grid - see the `rows` skill. |
|
|
53
53
|
| `showSelectedBackground` | `boolean` | Follows the mode | Background tint on selected rows: off for `"checkbox"`, on for `"row"`. Colour is `--dg-row-selected-bg`. Defined by the grid. |
|
|
@@ -65,7 +65,7 @@ rather than forwarded to TanStack.
|
|
|
65
65
|
| `initialState` | `Partial<TableState>` | See below | Merged over the grid defaults. |
|
|
66
66
|
| `meta` | `TMDataGridTableMeta` | `{}` | Grid configuration, see below. |
|
|
67
67
|
| `persist` | `TMDataGridPersistence` | – | State persistence, see below. |
|
|
68
|
-
| `
|
|
68
|
+
| `editing` | `TMDataGridEditingOptions` | off | Turns editing on. `mode` (`"cell" \| "cellConfirm" \| "row" \| "draft"`) picks the commit policy; the object also holds `onCommit`, `onCommitDrafts`, `rowValidators`, `isRowEditable`, `newRowDefaults`, `newRowsSticky`, `onRowAdd` and `onRowDelete` - see the `editing` skill. |
|
|
69
69
|
| `labels` | `TMDataGridLabelsOverride` | English | Overrides for the grid's strings and `aria-label`s. |
|
|
70
70
|
|
|
71
71
|
### Default initial state
|
|
@@ -152,7 +152,7 @@ persistence is skipped rather than throwing.
|
|
|
152
152
|
| --- | --- | --- |
|
|
153
153
|
| `table` | `Table<TMDataGridFeatures, TData>` | The TanStack table instance. |
|
|
154
154
|
| `ui` | `Store<TMDataGridUiState, TMDataGridUiActions>` | State of the filter and column panels. |
|
|
155
|
-
| `edit` | `TMDataGridEditApi` | The edit engine, inert until `
|
|
155
|
+
| `edit` | `TMDataGridEditApi` | The edit engine, inert until `editing` is set. See the `editing` skill. |
|
|
156
156
|
| `features` | `TMDataGridFeatureFlags` | Table-level feature switches, re-read on each render. |
|
|
157
157
|
| `labels` | `TMDataGridLabels` | The resolved label set, overrides merged over English. |
|
|
158
158
|
| `resetSettings` | `() => void` | Puts the settings state back to a clean first visit. |
|
package/skills/rows/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ description: >
|
|
|
17
17
|
metadata:
|
|
18
18
|
type: core
|
|
19
19
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '2.0.0-beta.
|
|
20
|
+
library_version: '2.0.0-beta.2'
|
|
21
21
|
sources:
|
|
22
22
|
- 'Jielga/TMDataGrid:src/docs/row-selection.md'
|
|
23
23
|
- 'Jielga/TMDataGrid:src/docs/row-interaction.md'
|
|
@@ -36,8 +36,8 @@ cell cursors and ranges are `cell-selection`.
|
|
|
36
36
|
## Selection
|
|
37
37
|
|
|
38
38
|
`selectionMode` sets what selecting looks like **and** what a bare row click
|
|
39
|
-
does.
|
|
40
|
-
multi-selection or move a highlight,
|
|
39
|
+
does. It is one option rather than two because a click can toggle a
|
|
40
|
+
multi-selection or move a highlight, but not both.
|
|
41
41
|
|
|
42
42
|
| Mode | Checkbox column | Row click |
|
|
43
43
|
| --- | --- | --- |
|
|
@@ -50,18 +50,19 @@ multi-selection or move a highlight, never both.
|
|
|
50
50
|
const grid = useTMDataGrid({ data, columns, selectionMode: "row" });
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The first two write to TanStack's `rowSelection`, so
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
The first two write to TanStack's `rowSelection`, so the toolbar count,
|
|
54
|
+
`getSelectedRowModel()` and persistence all behave the same either way. Under
|
|
55
|
+
`"row"` the click follows desktop-list conventions: a plain click
|
|
56
56
|
makes the selection this row, Ctrl/Cmd toggles it and leaves the rest, Shift
|
|
57
57
|
selects the range from the anchor, Ctrl+Shift adds that range. Rows are
|
|
58
58
|
focusable in this mode, and Space or Enter toggles the focused row.
|
|
59
59
|
|
|
60
|
-
`enableRowSelection: false` removes the
|
|
60
|
+
`enableRowSelection: false` removes the checkbox column and row-click selection
|
|
61
|
+
in any mode.
|
|
61
62
|
|
|
62
63
|
### The highlight is not a selection
|
|
63
64
|
|
|
64
|
-
The highlight is state of its own, not a slice of `rowSelection
|
|
65
|
+
The highlight is state of its own, not a slice of `rowSelection`. That is what
|
|
65
66
|
lets `"checkboxAndHighlight"` run both at once: tick rows for a bulk action,
|
|
66
67
|
click one to open its record beside the grid.
|
|
67
68
|
|
|
@@ -78,9 +79,9 @@ const grid = useTMDataGrid({
|
|
|
78
79
|
|
|
79
80
|
### Reading a selection
|
|
80
81
|
|
|
81
|
-
Read it through the table store
|
|
82
|
-
one identity across renders, so the React Compiler caches a bare
|
|
83
|
-
`getSelectedRowModel()` and the
|
|
82
|
+
Read it through the table store rather than by calling the method directly.
|
|
83
|
+
`table` keeps one identity across renders, so the React Compiler caches a bare
|
|
84
|
+
`getSelectedRowModel()` and the component stops updating.
|
|
84
85
|
|
|
85
86
|
```tsx
|
|
86
87
|
import { useSelector } from "@tanstack/react-store";
|
|
@@ -93,9 +94,9 @@ const selected = useSelector(grid.table.store, () =>
|
|
|
93
94
|
### The background
|
|
94
95
|
|
|
95
96
|
`showSelectedBackground` follows the mode: on for `"row"`, where the background
|
|
96
|
-
is the only feedback a click gives, off for `"checkbox"`, where the box
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
is the only feedback a click gives, and off for `"checkbox"`, where the box
|
|
98
|
+
already shows it. Set it explicitly to override either way. The colour is
|
|
99
|
+
`--dg-row-selected-bg`, set on the grid element rather than through the flag:
|
|
99
100
|
|
|
100
101
|
```tsx
|
|
101
102
|
<TMDataGrid
|
|
@@ -124,13 +125,13 @@ not replace selection or the highlight; yours runs in addition.
|
|
|
124
125
|
<TMDataGrid.Table<Employee> onRowClick={(row) => open(row.original.id)} />
|
|
125
126
|
```
|
|
126
127
|
|
|
127
|
-
Pass the row type, as above, and `row.original` is typed.
|
|
128
|
-
|
|
129
|
-
handler would receive a
|
|
128
|
+
Pass the row type, as above, and `row.original` is typed. None of the four fires
|
|
129
|
+
on a group row: TanStack builds a group row on its first child's record, so a
|
|
130
|
+
handler would receive a row that looks real but is the wrong one.
|
|
130
131
|
|
|
131
|
-
`renderRowContextMenu`
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
`renderRowContextMenu` supplies the contents of the menu. The grid renders the
|
|
133
|
+
Mantine `Menu`, opens it at the pointer and closes it on Escape, an outside
|
|
134
|
+
click, a body scroll and after an item is picked.
|
|
134
135
|
|
|
135
136
|
```tsx
|
|
136
137
|
<TMDataGrid.Table<Employee>
|
|
@@ -157,10 +158,10 @@ a pure function of its arguments and do the work in the item handlers. Return
|
|
|
157
158
|
the row with `data-context-menu` while its menu is open, so an action meant for
|
|
158
159
|
a multi-selection reads `table` and falls back to the clicked row.
|
|
159
160
|
|
|
160
|
-
Under `cellSelection: "range"` the grid has its own items for this menu
|
|
161
|
-
export
|
|
162
|
-
the composition
|
|
163
|
-
|
|
161
|
+
Under `cellSelection: "range"` the grid has its own items for this menu: copy,
|
|
162
|
+
export and include headers. `internalItems` is those items, and **using it takes
|
|
163
|
+
over the composition**: place it and the menu is exactly what you returned; omit
|
|
164
|
+
it and the grid keeps its own items above a divider and yours below.
|
|
164
165
|
|
|
165
166
|
```tsx
|
|
166
167
|
renderRowContextMenu={({ row, internalItems }) => (
|
|
@@ -202,14 +203,14 @@ Returning an empty list leaves the column with no menu button at all.
|
|
|
202
203
|
|
|
203
204
|
A row is a strip of cells, some sticky in pinned lanes, and hover, selection,
|
|
204
205
|
the highlight, the cell range and striping all paint on top of it. `--row-bg`
|
|
205
|
-
|
|
206
|
-
them and
|
|
207
|
-
sorting and filtering rather than sticking to records, and pinned rows
|
|
208
|
-
|
|
206
|
+
sets the variable those layers compose against; `background` overrides all of
|
|
207
|
+
them and they stop showing. `striped` follows position in the view, so it
|
|
208
|
+
survives sorting and filtering rather than sticking to records, and pinned rows
|
|
209
|
+
are not striped.
|
|
209
210
|
|
|
210
211
|
Rows carry `data-selected`, `data-selected-bg`, `data-highlighted`,
|
|
211
212
|
`data-grouped`, `data-depth`, `data-context-menu` and `data-row-id`, so a
|
|
212
|
-
stylesheet can
|
|
213
|
+
stylesheet can target any of it without a callback.
|
|
213
214
|
|
|
214
215
|
## The details panel
|
|
215
216
|
|
|
@@ -223,9 +224,9 @@ const grid = useTMDataGrid({
|
|
|
223
224
|
});
|
|
224
225
|
```
|
|
225
226
|
|
|
226
|
-
Each panel is measured, so heights need not be uniform
|
|
227
|
-
`renderDetailsEstHeight` (default `160`) is
|
|
228
|
-
|
|
227
|
+
Each panel is measured, so heights need not be uniform.
|
|
228
|
+
`renderDetailsEstHeight` (default `160`) is what the virtualizer assumes for one
|
|
229
|
+
it has not measured yet. A generated chevron column, `DETAILS_COLUMN_ID`, is
|
|
229
230
|
prepended and pinned left after the checkbox and tree lanes; it cannot be
|
|
230
231
|
hidden, moved, resized or unpinned, and its header opens and closes every panel.
|
|
231
232
|
|
|
@@ -233,8 +234,8 @@ Which rows are open is TanStack's own `expanded` state, so anything can open
|
|
|
233
234
|
one - `row.toggleExpanded()` from a menu item, `initialState.expanded`,
|
|
234
235
|
`table.toggleAllRowsExpanded()` - and it persists as a `data` slice. The panel
|
|
235
236
|
is a cell spanning the row, not a row: `aria-rowcount` still counts records, and
|
|
236
|
-
a click inside it
|
|
237
|
-
|
|
237
|
+
a click inside it does not select the row underneath. Group rows have no panel;
|
|
238
|
+
expanding one opens its children.
|
|
238
239
|
|
|
239
240
|
## Pinning and numbering
|
|
240
241
|
|
|
@@ -249,26 +250,26 @@ const grid = useTMDataGrid({
|
|
|
249
250
|
});
|
|
250
251
|
```
|
|
251
252
|
|
|
252
|
-
There is no built-in pin gesture and no pin icon
|
|
253
|
+
There is no built-in pin gesture and no pin icon. Build one from TanStack's own
|
|
253
254
|
`row.pin("top" | "bottom" | false)`, `row.getIsPinned()` and `row.getCanPin()`,
|
|
254
255
|
either as a display column whose cell is a pin button or in the row context
|
|
255
256
|
menu. Read the pinned state through a subscription
|
|
256
257
|
(`useSelector(row.table.store, () => row.getIsPinned())`) rather than calling it
|
|
257
258
|
in a component body: the `row` identity survives a pin, so the React Compiler
|
|
258
|
-
would cache the call and the icon would never change. Whichever
|
|
259
|
-
build,
|
|
259
|
+
would cache the call and the icon would never change. Whichever control you
|
|
260
|
+
build, include unpin in it. Pinned rows leave the scrolling order and render in
|
|
260
261
|
sticky blocks: top under the header, bottom above the summary row.
|
|
261
262
|
|
|
262
|
-
Pinned rows are still body rows
|
|
263
|
-
and per-row styling all behave normally.
|
|
264
|
-
|
|
265
|
-
row stays at its edge even when a filter or the pager would
|
|
266
|
-
**group rows never pin**.
|
|
263
|
+
Pinned rows are still body rows: selection, editing, details, the context menu
|
|
264
|
+
and per-row styling all behave normally. They are excluded only from the
|
|
265
|
+
features that depend on scroll order: striping, the cell range and the number
|
|
266
|
+
gutter. A pinned row stays at its edge even when a filter or the pager would
|
|
267
|
+
have dropped it, and **group rows never pin**.
|
|
267
268
|
|
|
268
|
-
`enableRowNumbers` adds a gutter outermost left that numbers the current view
|
|
269
|
-
sorted, filtered, continuing across pages.
|
|
270
|
-
|
|
271
|
-
own over the record's id.
|
|
269
|
+
`enableRowNumbers` adds a gutter outermost left that numbers the current view:
|
|
270
|
+
sorted, filtered, continuing across pages. The number is a position in the
|
|
271
|
+
current view, not an identifier for the record. For a stable identifier, add a
|
|
272
|
+
column of your own over the record's id.
|
|
272
273
|
|
|
273
274
|
## Common mistakes
|
|
274
275
|
|
|
@@ -276,7 +277,7 @@ own over the record's id.
|
|
|
276
277
|
|
|
277
278
|
A coloured row stops responding to hover, selection, the highlight and the cell
|
|
278
279
|
range, because `background` paints over every layer composed on top of the row.
|
|
279
|
-
Nothing errors; the row
|
|
280
|
+
Nothing errors; the row stops reacting.
|
|
280
281
|
|
|
281
282
|
Wrong:
|
|
282
283
|
|
|
@@ -296,8 +297,7 @@ Source: `src/docs/row-styling.md` (Set `--row-bg`, not `background`).
|
|
|
296
297
|
|
|
297
298
|
`table` keeps one identity for the life of the grid, so the React Compiler
|
|
298
299
|
memoizes a bare `getSelectedRowModel()` against it. The count renders once and
|
|
299
|
-
then never changes
|
|
300
|
-
subscription.
|
|
300
|
+
then never changes.
|
|
301
301
|
|
|
302
302
|
Wrong:
|
|
303
303
|
|
|
@@ -317,9 +317,9 @@ Source: `src/docs/row-selection.md` (Acting on a selection).
|
|
|
317
317
|
|
|
318
318
|
### HIGH Looking for the highlight in `rowSelection`
|
|
319
319
|
|
|
320
|
-
The highlight is separate state, which is what lets
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
The highlight is separate state, which is what lets `"checkboxAndHighlight"`
|
|
321
|
+
run both at once. It never appears in `rowSelection`, `getSelectedRowModel()` or
|
|
322
|
+
the persisted selection slice.
|
|
323
323
|
|
|
324
324
|
Wrong:
|
|
325
325
|
|
|
@@ -366,10 +366,10 @@ Source: `src/docs/row-details.md` (Opening a row from elsewhere).
|
|
|
366
366
|
|
|
367
367
|
### MEDIUM Assuming group rows behave like data rows
|
|
368
368
|
|
|
369
|
-
Group rows are built on their first child's record. They
|
|
370
|
-
`onCellClick`, `onCellDoubleClick`
|
|
371
|
-
take no row number, and they have no details panel. A handler
|
|
372
|
-
every row reaches it
|
|
369
|
+
Group rows are built on their first child's record. They do not fire
|
|
370
|
+
`onRowClick`, `onCellClick`, `onCellDoubleClick` or `onCellContextMenu`, they
|
|
371
|
+
never pin, they take no row number, and they have no details panel. A handler
|
|
372
|
+
written as though every row reaches it silently skips them.
|
|
373
373
|
|
|
374
374
|
Source: `src/docs/row-interaction.md`, `src/docs/row-pinning.md`.
|
|
375
375
|
|
|
@@ -388,16 +388,16 @@ Source: `src/docs/row-details.md`.
|
|
|
388
388
|
|
|
389
389
|
### MEDIUM Expecting pinned rows to persist
|
|
390
390
|
|
|
391
|
-
`rowPinning` is deliberately left out of `settingsKey`: row ids are data, and
|
|
391
|
+
`rowPinning` is deliberately left out of `settingsKey`: row ids are data, and the
|
|
392
392
|
layout store outlives any one data set. A pinned id whose row leaves `data` is
|
|
393
|
-
|
|
393
|
+
not shown, and returns to its edge if the data comes back.
|
|
394
394
|
|
|
395
395
|
Source: `src/docs/row-pinning.md`.
|
|
396
396
|
|
|
397
397
|
## References
|
|
398
398
|
|
|
399
399
|
- [Rows API](references/rows-api.md) - every option, table prop, callback,
|
|
400
|
-
export, CSS variable and data attribute these five topics
|
|
400
|
+
export, CSS variable and data attribute belonging to these five topics.
|
|
401
401
|
|
|
402
402
|
See also: the `grouping` skill for group rows and the tree lane, the
|
|
403
403
|
`cell-selection` skill for the cell cursor and ranges, and the `appearance`
|
|
@@ -33,17 +33,17 @@ All are props of `TMDataGrid.Table`, not hook options.
|
|
|
33
33
|
| `onCellContextMenu` | `(args) => void` | Cell right-click. |
|
|
34
34
|
| `renderRowContextMenu` | `({ table, row, cell, close, internalItems }) => ReactNode` | Contents of the row's context menu. `null` for no menu. Reading `internalItems` hands the composition over. |
|
|
35
35
|
| `renderColumnMenuItems` | `({ column, table, internalItems }) => ReactNode[]` | Contents of a column's menu. An empty list removes the button. |
|
|
36
|
-
| `rowContextMenuProps` | `MenuProps` | Passed to the Mantine `Menu`
|
|
36
|
+
| `rowContextMenuProps` | `MenuProps` | Passed to the Mantine `Menu` unchanged, apart from its open state. |
|
|
37
37
|
|
|
38
38
|
`TMDataGridCellEventArgs` is `{ cell, row, column, event }`. The context-menu
|
|
39
39
|
slot's `cell` is `null` only when a custom cell renderer stopped the
|
|
40
|
-
event. One `Menu` serves the whole body
|
|
41
|
-
its hooks on every render, and the virtualized body
|
|
42
|
-
frame.
|
|
40
|
+
event. One `Menu` serves the whole body rather than one per row: a closed
|
|
41
|
+
Mantine `Popover` still runs its hooks on every render, and the virtualized body
|
|
42
|
+
re-renders on every scroll frame.
|
|
43
43
|
|
|
44
44
|
On touch devices a long press (500 ms) opens the same menu. Mantine sets
|
|
45
|
-
`user-select: none` on the element it
|
|
46
|
-
text
|
|
45
|
+
`user-select: none` on the element it attaches a context menu to, so body cell
|
|
46
|
+
text is not selectable with the mouse in a grid that has one.
|
|
47
47
|
|
|
48
48
|
## Styling
|
|
49
49
|
|
|
@@ -69,7 +69,7 @@ Row data attributes:
|
|
|
69
69
|
| `data-grouped` | Group rows |
|
|
70
70
|
| `data-depth` | Every row - the nesting level |
|
|
71
71
|
| `data-context-menu` | The row whose context menu is open |
|
|
72
|
-
| `data-deleted` | Rows marked for deletion under
|
|
72
|
+
| `data-deleted` | Rows marked for deletion under draft editing |
|
|
73
73
|
| `data-row-id` | Every row - its id |
|
|
74
74
|
|
|
75
75
|
## Details
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
metadata:
|
|
11
11
|
type: core
|
|
12
12
|
library: '@jielga/tmdatagrid'
|
|
13
|
-
library_version: '2.0.0-beta.
|
|
13
|
+
library_version: '2.0.0-beta.2'
|
|
14
14
|
sources:
|
|
15
15
|
- 'Jielga/TMDataGrid:src/docs/server-side.md'
|
|
16
16
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -11,7 +11,7 @@ description: >
|
|
|
11
11
|
metadata:
|
|
12
12
|
type: core
|
|
13
13
|
library: '@jielga/tmdatagrid'
|
|
14
|
-
library_version: '2.0.0-beta.
|
|
14
|
+
library_version: '2.0.0-beta.2'
|
|
15
15
|
sources:
|
|
16
16
|
- 'Jielga/TMDataGrid:src/docs/testing.md'
|
|
17
17
|
- 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGrid.tsx'
|
|
@@ -71,7 +71,8 @@ Body cells carry no `data-dg-part` - the coordinate pair already names them.
|
|
|
71
71
|
|
|
72
72
|
**Keyed by `data-row-id`**: `row`, `entry-row`, `details`, `select-row`,
|
|
73
73
|
`details-toggle`, `group-toggle`, `edit-row`, `delete-row`, `save-row`,
|
|
74
|
-
`cancel-row`, `
|
|
74
|
+
`cancel-row`, `row-state`, `revert-row`, `restore-row`, `confirm-new-row`,
|
|
75
|
+
`discard-new-row`.
|
|
75
76
|
|
|
76
77
|
**Keyed by `data-column-id`**: `header`, `header-sort`, `header-menu`,
|
|
77
78
|
`header-filter`, `filter-row`, `filter-pill`, `columns-toggle`.
|
|
@@ -148,11 +149,11 @@ rather than adding a timeout.
|
|
|
148
149
|
|
|
149
150
|
## Common mistakes
|
|
150
151
|
|
|
151
|
-
### Selecting
|
|
152
|
+
### Selecting a control by its aria-label
|
|
152
153
|
|
|
153
|
-
Every icon-only control has one, but they come from `labels` and are
|
|
154
|
-
|
|
155
|
-
the
|
|
154
|
+
Every icon-only control has one, but they come from `labels` and are translated.
|
|
155
|
+
A suite written on `getByRole("button", { name: "Filters" })` breaks as soon as
|
|
156
|
+
the grid renders in Swedish, and again on any copy change. Use the part.
|
|
156
157
|
|
|
157
158
|
### Counting row elements
|
|
158
159
|
|
|
@@ -163,10 +164,10 @@ layout, so the count depends on the stubbed element size. Assert
|
|
|
163
164
|
|
|
164
165
|
### getByRole("cell") on a grid with cell selection
|
|
165
166
|
|
|
166
|
-
`
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
`cellSelection` turns every `cell` into a `gridcell`, and the grid's `table`
|
|
168
|
+
into a `grid`, because a widget with a keyboard cursor is not a static table.
|
|
169
|
+
Tests written on the role break when the feature is switched on. Query cells by
|
|
170
|
+
`[data-row-id][data-column-id]` instead.
|
|
170
171
|
|
|
171
172
|
### Expecting a row far down the list to exist
|
|
172
173
|
|