@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.
Files changed (52) hide show
  1. package/README.md +32 -34
  2. package/dist/index.d.ts +164 -102
  3. package/dist/index.js +2060 -1705
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/package.json +1 -1
  7. package/skills/appearance/SKILL.md +37 -34
  8. package/skills/cell-selection/SKILL.md +19 -19
  9. package/skills/columns/SKILL.md +9 -9
  10. package/skills/data/SKILL.md +53 -53
  11. package/skills/editing/SKILL.md +152 -311
  12. package/skills/editing/references/common-mistakes.md +177 -0
  13. package/skills/editing/references/editing-api.md +56 -28
  14. package/skills/editing/references/editors-and-validation.md +26 -19
  15. package/skills/filtering/SKILL.md +51 -50
  16. package/skills/getting-started/SKILL.md +10 -10
  17. package/skills/grouping/SKILL.md +41 -43
  18. package/skills/options/SKILL.md +4 -4
  19. package/skills/rows/SKILL.md +59 -59
  20. package/skills/rows/references/rows-api.md +7 -7
  21. package/skills/server-side/SKILL.md +1 -1
  22. package/skills/testing/SKILL.md +11 -10
  23. package/src/tmdatagrid/components/TMDataGrid.module.css +13 -1
  24. package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +20 -8
  25. package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +29 -18
  26. package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +5 -8
  27. package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
  28. package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +246 -86
  29. package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +176 -77
  30. package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +11 -6
  31. package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +9 -5
  32. package/src/tmdatagrid/components/TMDataGridTable.module.css +47 -3
  33. package/src/tmdatagrid/components/TMDataGridTable.tsx +101 -40
  34. package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
  35. package/src/tmdatagrid/components/icons.ts +1 -0
  36. package/src/tmdatagrid/core/autosize.ts +30 -6
  37. package/src/tmdatagrid/core/capabilities.ts +15 -5
  38. package/src/tmdatagrid/core/cellExport.ts +6 -7
  39. package/src/tmdatagrid/core/cellNavigation.ts +2 -2
  40. package/src/tmdatagrid/core/cellRange.ts +6 -6
  41. package/src/tmdatagrid/core/columnOrdering.ts +30 -1
  42. package/src/tmdatagrid/core/columnUtils.ts +14 -0
  43. package/src/tmdatagrid/core/draftCellContext.ts +68 -0
  44. package/src/tmdatagrid/core/editEngine.ts +111 -37
  45. package/src/tmdatagrid/core/filterOperators.ts +6 -6
  46. package/src/tmdatagrid/core/labels.ts +13 -1
  47. package/src/tmdatagrid/core/labelsSv.ts +4 -0
  48. package/src/tmdatagrid/core/matchHighlight.ts +3 -3
  49. package/src/tmdatagrid/core/persistence.ts +3 -3
  50. package/src/tmdatagrid/core/rowSelection.ts +3 -3
  51. package/src/tmdatagrid/index.ts +3 -1
  52. 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.0'
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 opens at the
173
- pointer on a right-click (or a long press). The grid owns the `Menu` - opening,
174
- positioning, and closing on Escape, outside click, body scroll or an item pick -
175
- so the render prop only says what goes in it:
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 there for actions that read
196
- the selection, and `close` is for dropdown content that is not a `Menu.Item`
197
- (those close themselves). Return `null` to leave a row without a menu. It is
198
- called during render, and only for the open row, so it must stay pure - put the
199
- work in the item handlers. Right-clicking does not change the selection or the
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
 
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0-beta.0'
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 - the reader does
33
- that from **Group by …** in any column menu. To open already grouped, seed the
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** - its values have moved into the tree lane -
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 unless asked for. A grouped grid is a tree, not a summary: a group row
57
- leaves every cell blank except the tree lane. Give a column an `aggregationFn`
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 handing it to a callback
85
- would hand over a real-looking row that is the wrong one. Group rows therefore:
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` - a group row is
95
- never in it - so `getSelectedRowModel()` and the toolbar count are unaffected by
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; `TMDataGrid.Footer` greys its pager out and replaces the range
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 answer is usable: counting every row splits a group across a
110
- page boundary, and counting top-level rows quietly redefines "rows per page" as
111
- groups per page. Rendering the whole tree is the grid's default mode anyway -
112
- pagination is the opt-in - so nothing is lost but the pager.
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; it exists
121
- exactly when at least one visible column defines one.
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 - through the registered aggregation
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
- Following the filters is the point: a footer that keeps saying the same number
144
- while the reader narrows the grid looks like it is answering the question in
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 - the tree simply
157
- looks empty, which reads as a broken feature.
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 - its values are in the tree lane. Code that reads that
177
- column's cells, or a test that queries its header, stops finding it the moment a
178
- reader groups.
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 be honoured. The pager
191
- greys out rather than paging the tree, so a footer count wired to
192
- `getPageCount()` reports a number nobody can navigate to. Read
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 sit out `onRowClick`, the cell handlers, pinning, details and
200
- editing, and their `row.original` is an arbitrary child's record. A bulk action
201
- built from `row.original` on the tree lane acts on one record instead of the
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, which
218
- matches the screen but answers a different question - and under virtualization
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: the client holds one page and would
226
- build groups out of an arbitrary slice. Group on the server and declare it.
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 rows. Unset means blank. |
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, and summons the row. |
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. |
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0-beta.0'
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 `editMode` is set. |
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
- | `editMode` | `"cell" \| "cellConfirm" \| "row" \| "batch"` | off | Turns editing on. Brings `rowValidators`, `isRowEditable`, `onEditCommit`, `onEditCommitBatch`, `newRowDefaults`, `onRowAdd` and `onRowDelete` with it - see the `editing` skill. |
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 `editMode` is set. See the `editing` skill. |
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. |
@@ -17,7 +17,7 @@ description: >
17
17
  metadata:
18
18
  type: core
19
19
  library: '@jielga/tmdatagrid'
20
- library_version: '2.0.0-beta.0'
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. One option rather than two, because a click can either toggle a
40
- multi-selection or move a highlight, never both.
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 everything downstream - the
54
- toolbar count, `getSelectedRowModel()`, persistence - is unaffected by the
55
- choice. Under `"row"` the click follows desktop-list conventions: a plain click
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 selection half whatever the mode.
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` - which is what
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, never by calling the method bare. `table` keeps
82
- one identity across renders, so the React Compiler caches a bare
83
- `getSelectedRowModel()` and the reader stops updating.
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 already
97
- says so. Set it to override either way. The colour is `--dg-row-selected-bg`,
98
- changed on the grid element rather than through the flag:
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. Group rows sit out all
128
- four: TanStack builds a group row on top of its first child's record, so a
129
- handler would receive a real-looking row that is the wrong one.
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` is a slot saying what goes inside the menu. The grid
132
- owns the Mantine `Menu`, opening it at the pointer and closing it on Escape, an
133
- outside click, a body scroll and after an item is picked.
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 - copy,
161
- export, include headers. `internalItems` is those items, and **reading it hands
162
- the composition over**: place it and the menu is exactly what you returned;
163
- never mention it and the grid keeps its half above a divider and yours below.
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
- feeds the variable those layers compose against; `background` wins over all of
206
- them and kills them. `striped` follows position in the view, so it survives
207
- sorting and filtering rather than sticking to records, and pinned rows sit it
208
- out.
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 reach any of it without a callback.
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 only what the virtualizer assumes
228
- for one it has not seen. A generated chevron column, `DETAILS_COLUMN_ID`, is
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 stops there rather than selecting the row underneath. Group
237
- rows have no panel; expanding one opens its children.
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 - build one from TanStack's own
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 gesture you
259
- build, put unpin in it too. Pinned rows leave the scrolling order and render in
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 - selection, editing, details, the context menu
263
- and per-row styling all behave normally. What they sit out are the statements
264
- about scrolling order: striping, the cell range, and the number gutter. A pinned
265
- row stays at its edge even when a filter or the pager would have dropped it, and
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. It answers "where am I in what I am
270
- looking at", not "which record is this"; a stable identifier is a column of your
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 simply goes inert.
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, which reads as a broken toolbar rather than a missing
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
- `"checkboxAndHighlight"` run both at once. It never appears in `rowSelection`,
322
- `getSelectedRowModel()` or the persisted selection slice.
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 sit out `onRowClick`,
370
- `onCellClick`, `onCellDoubleClick` and `onCellContextMenu`, they never pin, they
371
- take no row number, and they have no details panel. A handler written as though
372
- every row reaches it will silently skip them.
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 a
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
- simply not shown, and returns to its edge if the data comes back.
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 own.
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` untouched apart from its open state. |
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: a closed Mantine `Popover` still runs
41
- its hooks on every render, and the virtualized body re-renders every scroll
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 hangs a context menu off, so body cell
46
- text stops being mouse-selectable in a grid that has one.
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 batch editing |
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.0'
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'
@@ -11,7 +11,7 @@ description: >
11
11
  metadata:
12
12
  type: core
13
13
  library: '@jielga/tmdatagrid'
14
- library_version: '2.0.0-beta.0'
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`, `restore-row`, `confirm-new-row`, `discard-new-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 chrome by its aria-label
152
+ ### Selecting a control by its aria-label
152
153
 
153
- Every icon-only control has one, but they come from `labels` and are yours to
154
- translate. A suite written on `getByRole("button", { name: "Filters" })` breaks
155
- the day the grid renders in Swedish, and again on any copy change. Use the part.
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
- `enableCellSelection` turns every `cell` into a `gridcell`, and the grid's
167
- `table` into a `grid` - a widget with a keyboard cursor is not a table of
168
- content. Tests written on the role break when the feature is switched on. Query
169
- cells by `[data-row-id][data-column-id]`, which do not move.
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