@jielga/tmdatagrid 1.0.0 → 1.0.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 (78) hide show
  1. package/README.md +8 -8
  2. package/dist/index.d.ts +225 -225
  3. package/dist/index.js +58 -50
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/package.json +3 -2
  7. package/skills/appearance/SKILL.md +322 -0
  8. package/skills/cell-selection/SKILL.md +240 -0
  9. package/skills/columns/SKILL.md +261 -86
  10. package/skills/data/SKILL.md +289 -0
  11. package/skills/editing/SKILL.md +492 -0
  12. package/skills/editing/references/editing-api.md +124 -0
  13. package/skills/editing/references/editors-and-validation.md +198 -0
  14. package/skills/filtering/SKILL.md +344 -0
  15. package/skills/getting-started/SKILL.md +48 -27
  16. package/skills/grouping/SKILL.md +264 -0
  17. package/skills/options/SKILL.md +31 -20
  18. package/skills/rows/SKILL.md +369 -0
  19. package/skills/rows/references/rows-api.md +117 -0
  20. package/skills/server-side/SKILL.md +7 -7
  21. package/skills/testing/SKILL.md +12 -12
  22. package/src/tmdatagrid/TMDataGridContext.ts +2 -2
  23. package/src/tmdatagrid/components/TMDataGrid.module.css +2 -2
  24. package/src/tmdatagrid/components/TMDataGrid.tsx +5 -5
  25. package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +4 -4
  26. package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +2 -2
  27. package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +6 -6
  28. package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
  29. package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +4 -4
  30. package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +6 -6
  31. package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +6 -6
  32. package/src/tmdatagrid/components/TMDataGridFilterPills.module.css +2 -2
  33. package/src/tmdatagrid/components/TMDataGridFilterPills.tsx +1 -1
  34. package/src/tmdatagrid/components/TMDataGridFooter.module.css +1 -1
  35. package/src/tmdatagrid/components/TMDataGridFooter.tsx +12 -6
  36. package/src/tmdatagrid/components/TMDataGridGroupColumn.module.css +1 -1
  37. package/src/tmdatagrid/components/TMDataGridGroupColumn.tsx +5 -5
  38. package/src/tmdatagrid/components/TMDataGridHeaderCell.module.css +8 -8
  39. package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +12 -12
  40. package/src/tmdatagrid/components/TMDataGridRowNumberColumn.tsx +4 -4
  41. package/src/tmdatagrid/components/TMDataGridSearch.tsx +5 -5
  42. package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +8 -8
  43. package/src/tmdatagrid/components/TMDataGridTable.module.css +18 -18
  44. package/src/tmdatagrid/components/TMDataGridTable.tsx +116 -116
  45. package/src/tmdatagrid/components/TMDataGridToolbar.tsx +5 -5
  46. package/src/tmdatagrid/components/editors/TMDataGridBooleanEditor.tsx +1 -1
  47. package/src/tmdatagrid/components/editors/TMDataGridDateEditor.tsx +2 -2
  48. package/src/tmdatagrid/components/editors/TMDataGridMultiSelectEditor.tsx +1 -1
  49. package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
  50. package/src/tmdatagrid/components/editors/TMDataGridSelectEditor.tsx +2 -2
  51. package/src/tmdatagrid/components/editors/editorShared.ts +3 -3
  52. package/src/tmdatagrid/components/filters/DgDateRangeFilter.tsx +1 -1
  53. package/src/tmdatagrid/components/filters/DgRangeSliderFilter.tsx +1 -1
  54. package/src/tmdatagrid/components/filters/DgTriStateFilter.tsx +1 -1
  55. package/src/tmdatagrid/components/filters/TMDataGridFilterValueInput.tsx +2 -2
  56. package/src/tmdatagrid/components/sticky.module.css +8 -8
  57. package/src/tmdatagrid/core/autosize.ts +4 -4
  58. package/src/tmdatagrid/core/capabilities.ts +17 -17
  59. package/src/tmdatagrid/core/cellExport.ts +13 -13
  60. package/src/tmdatagrid/core/cellNavigation.ts +6 -6
  61. package/src/tmdatagrid/core/cellRange.ts +4 -4
  62. package/src/tmdatagrid/core/columnOptions.ts +2 -2
  63. package/src/tmdatagrid/core/columnOrdering.ts +2 -2
  64. package/src/tmdatagrid/core/columnUtils.ts +3 -3
  65. package/src/tmdatagrid/core/editEngine.ts +49 -49
  66. package/src/tmdatagrid/core/expanding.ts +5 -5
  67. package/src/tmdatagrid/core/filterControls.ts +5 -5
  68. package/src/tmdatagrid/core/filterOperators.ts +14 -14
  69. package/src/tmdatagrid/core/labels.ts +8 -8
  70. package/src/tmdatagrid/core/matchHighlight.ts +4 -4
  71. package/src/tmdatagrid/core/persistence.ts +8 -8
  72. package/src/tmdatagrid/core/quickSearch.ts +8 -8
  73. package/src/tmdatagrid/core/rowPinning.ts +3 -3
  74. package/src/tmdatagrid/core/rowSelection.ts +12 -12
  75. package/src/tmdatagrid/core/sizes.ts +1 -1
  76. package/src/tmdatagrid/core/summary.ts +3 -3
  77. package/src/tmdatagrid/useTMDataGrid.tsx +79 -79
  78. package/skills/features/SKILL.md +0 -352
@@ -3,27 +3,28 @@ name: getting-started
3
3
  description: >
4
4
  Set up TMDataGrid, a compound React data grid built on TanStack Table v9 and
5
5
  Mantine. Covers useTMDataGrid, the TMDataGrid root, context, the component
6
- catalog (Table, Footer, Toolbar, Spacer, SummaryCount, FilterButton,
7
- ColumnsButton, FilterPanel, FilterPills, ColumnsPanel), the size scale and the
8
- bounded-height layout requirement. Load when adding a grid, choosing which
9
- parts to render, or when rows do not appear.
6
+ catalog (Table, Footer, Toolbar, Spacer, SummaryCount, Search,
7
+ LoadingIndicator, EditActions, FilterButton, ColumnsButton, FilterPanel,
8
+ FilterPills, ColumnsPanel), the size scale and the bounded-height layout
9
+ requirement. Load when adding a grid, choosing which parts to render, or when
10
+ rows do not appear.
10
11
  metadata:
11
12
  type: core
12
13
  library: '@jielga/tmdatagrid'
13
- library_version: '1.0.0'
14
+ library_version: '1.0.2'
14
15
  sources:
15
16
  - 'Jielga/TMDataGrid:src/docs/getting-started.md'
16
- - 'Jielga/TMDataGrid:src/docs/components.md'
17
+ - 'Jielga/TMDataGrid:src/docs/anatomy.md'
17
18
  - 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGrid.tsx'
18
19
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/sizes.ts'
19
20
  ---
20
21
 
21
- # TMDataGrid — Getting started
22
+ # TMDataGrid - Getting started
22
23
 
23
24
  `useTMDataGrid` creates the table, `TMDataGrid` provides it through context, and
24
25
  the parts rendered inside read what they need from that context. Only the parts
25
26
  you render exist, and only the features you enable have state: pagination is
26
- opt-in via `enablePagination` — by default every row renders, virtualized.
27
+ opt-in via `enablePagination` - by default every row renders, virtualized.
27
28
 
28
29
  ## Install
29
30
 
@@ -103,14 +104,31 @@ Spread the hook result (`{...grid}`) rather than assigning `table`, `ui` and
103
104
  | Column menu | On hover: sort, filter, pin, move, hide, manage columns. |
104
105
  | Filter panel | Column, operator and value rows. |
105
106
  | Column manager | Search, toggle, show/hide all, reset. |
106
- | Row selection | Checkbox column pinned to the left, or click-to-select rows with `rowSelectionMode: "row"`. |
107
+ | Row selection | Checkbox column pinned to the left, or click-to-select rows with `selectionMode: "row"`. See the `rows` skill. |
107
108
  | Pagination | Off by default: all rows render, virtualized. Opt in with `enablePagination: true`. |
108
109
  | Sizing | `size="xs"` to `size="xl"` scales rows, type and controls. |
109
110
 
110
- Each is bound to a capability check — disabling the standard table or column
111
- option also removes its interface. Column ordering (`enableColumnOrdering`) and
112
- pagination (`enablePagination`) are the two switches the grid defines itself.
113
- See the `features` skill.
111
+ Each is bound to a capability check - disabling the standard table or column
112
+ option also removes its interface, and empty menus and inactive buttons are
113
+ never rendered. Column ordering (`enableColumnOrdering`) and pagination
114
+ (`enablePagination`) are the two switches the grid defines itself, because
115
+ TanStack ships state and APIs for both but no `enable` option.
116
+
117
+ ## Which skill covers what
118
+
119
+ | Need to | Load |
120
+ | --- | --- |
121
+ | Define columns, size, hide, pin, reorder, sort them | `columns` |
122
+ | Filter, or add a search box | `filtering` |
123
+ | Select rows, react to clicks, colour rows, details, pin rows | `rows` |
124
+ | Move a cell cursor, select ranges, copy, export CSV | `cell-selection` |
125
+ | Make cells editable | `editing` |
126
+ | Group rows, aggregate, add a summary row | `grouping` |
127
+ | Page, tune scrolling, handle empty and loading states | `data` |
128
+ | Back the grid with a server | `server-side` |
129
+ | Configure the hook, or persist state | `options` |
130
+ | Theme, size, compose the toolbar, translate | `appearance` |
131
+ | Write tests against a grid | `testing` |
114
132
 
115
133
  ## Layout
116
134
 
@@ -125,21 +143,24 @@ takes a fixed pixel width once resized or pinned.
125
143
 
126
144
  All read the grid from context and must be rendered inside `TMDataGrid`, except
127
145
  `TMDataGrid.FilterPills`, which takes the grid as an `api` prop and can live
128
- anywhere on the page. Order and presence are up to you — the root is a plain
146
+ anywhere on the page. Order and presence are up to you - the root is a plain
129
147
  flex column.
130
148
 
131
149
  | Component | Props | Notes |
132
150
  | --- | --- | --- |
133
151
  | `TMDataGrid` | `table`, `ui`, `features`, `size`, `className`, `style` | Root. Provides context. `style` also takes CSS variables: `--dg-row-selected-bg`, `--dg-row-height`, `--dg-header-height`, `--dg-font-size`, `--dg-padding`. |
134
- | `TMDataGrid.Table` | `onRowClick(row)`, `rowContextMenu` render prop, `rowContextMenuProps` | Header, virtualized body, filter panel. `onRowClick` runs in addition to selection under `rowSelectionMode: "row"`. |
152
+ | `TMDataGrid.Table` | `onRowClick(row)`, `rowContextMenu` render prop, `rowContextMenuProps` | Header, virtualized body, filter panel. `onRowClick` runs in addition to selection under `selectionMode: "row"`. |
135
153
  | `TMDataGrid.Footer` | `pageSizeOptions` (default `[10, 25, 50, 100]`), `pagination` render prop | Pagination controls. Renders nothing unless pagination is enabled. |
136
154
  | `TMDataGrid.Toolbar` | `children` | Flex row above the grid. |
137
- | `TMDataGrid.Spacer` | — | Pushes later toolbar items right. |
155
+ | `TMDataGrid.Spacer` | - | Pushes later toolbar items right. |
138
156
  | `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
139
- | `TMDataGrid.FilterButton` | — | Toggles filter panel. Renders nothing if no column is filterable. |
140
- | `TMDataGrid.ColumnsButton` | — | Opens column manager. Renders nothing if no column is hideable. |
141
- | `TMDataGrid.FilterPanel` | — | Rendered by `.Table`; exported for custom layouts. Header close button, Escape, click-away, "Add filter" and "Clear all". |
142
- | `TMDataGrid.ColumnsPanel` | — | Rendered by `.ColumnsButton`; exported for custom layouts. |
157
+ | `TMDataGrid.Search` | `placeholder`, `debounce` (default `250`), `w` (default `220`) | Quick search over every column, debounced into `globalFilter`. Renders nothing under `enableGlobalFilter: false`. |
158
+ | `TMDataGrid.LoadingIndicator` | - | Small spinner while `meta.loading` is `true` and rows stay on screen. |
159
+ | `TMDataGrid.EditActions` | - | Save with the pending count, and Discard. Renders nothing while editing is off - see the `editing` skill. |
160
+ | `TMDataGrid.FilterButton` | - | Toggles filter panel. Renders nothing if no column is filterable. |
161
+ | `TMDataGrid.ColumnsButton` | - | Opens column manager. Renders nothing if no column is hideable. |
162
+ | `TMDataGrid.FilterPanel` | - | Rendered by `.Table`; exported for custom layouts. Header close button, Escape, click-away, "Add filter" and "Clear all". |
163
+ | `TMDataGrid.ColumnsPanel` | - | Rendered by `.ColumnsButton`; exported for custom layouts. |
143
164
  | `TMDataGrid.FilterPills` | `api`, `size` (default `"sm"`), `showClearAll` (default `true`), `onPillClick(columnId)`, `className` | One pill per active filter, ✕ to clear it. Takes the api as a prop, so it can be rendered outside the grid. Also exported as `TMDataGridFilterPills`. |
144
165
 
145
166
  Pass the row type so `onRowClick` stays typed:
@@ -149,8 +170,8 @@ Pass the row type so `onRowClick` stays typed:
149
170
  ```
150
171
 
151
172
  `rowContextMenu` fills the dropdown of a Mantine `Menu` the grid opens at the
152
- pointer on a right-click (or a long press). The grid owns the `Menu` — opening,
153
- positioning, and closing on Escape, outside click, body scroll or an item pick —
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 -
154
175
  so the render prop only says what goes in it:
155
176
 
156
177
  ```tsx
@@ -174,7 +195,7 @@ so the render prop only says what goes in it:
174
195
  `cell` is the one that was right-clicked, `table` is there for actions that read
175
196
  the selection, and `close` is for dropdown content that is not a `Menu.Item`
176
197
  (those close themselves). Return `null` to leave a row without a menu. It is
177
- called during render, and only for the open row, so it must stay pure — put the
198
+ called during render, and only for the open row, so it must stay pure - put the
178
199
  work in the item handlers. Right-clicking does not change the selection or the
179
200
  highlight; the row carries `data-context-menu` while its menu is open. Pass
180
201
  `rowContextMenuProps` for `width`, `shadow`, `position` and the rest.
@@ -229,7 +250,7 @@ mirrors them. Set `meta.rowHeight` for a height outside the scale.
229
250
  | `getColumnLabel(column)` | `meta.label`, a string header, or the column id. |
230
251
  | `getColumnType(column)` | `meta.type`, defaulting to `"string"`. |
231
252
  | `formatFilterLabel({ label, type, filter })` | The one-line filter description used on the pills. |
232
- | `moveColumn({ table, columnId, targetId, side })` | Moves a column next to another. See the `features` skill. |
253
+ | `moveColumn({ table, columnId, targetId, side })` | Moves a column next to another. See the `columns` skill. |
233
254
  | `moveColumnByStep({ table, columnId, direction })` | Moves a column one position within its region. |
234
255
  | `SELECT_COLUMN_ID` | Id of the generated checkbox column. |
235
256
 
@@ -241,7 +262,7 @@ A new array on every render rebuilds the table's column model. Define `columns`
241
262
  at module scope; keep `data` stable with `useMemo`.
242
263
 
243
264
  ```tsx
244
- // Wrong — rebuilds the column model every render.
265
+ // Wrong - rebuilds the column model every render.
245
266
  export function Employees({ data }: { data: Employee[] }) {
246
267
  const columns = columnHelper.columns([...]);
247
268
  const grid = useTMDataGrid({ data, columns });
@@ -251,7 +272,7 @@ export function Employees({ data }: { data: Employee[] }) {
251
272
  ### Root with no bounded height
252
273
 
253
274
  The root is `overflow: hidden`. Without `flex: 1; minHeight: 0` in a flex parent
254
- or an explicit height, the table area collapses and no rows are visible — there
275
+ or an explicit height, the table area collapses and no rows are visible - there
255
276
  is no error.
256
277
 
257
278
  ### Assigning the hook result field by field
@@ -261,7 +282,7 @@ and feature toggles stop being reactive. Spread instead: `<TMDataGrid {...grid}>
261
282
 
262
283
  ### Expecting Footer to enable pagination
263
284
 
264
- Rendering `TMDataGrid.Footer` does not switch pagination on — the option does.
285
+ Rendering `TMDataGrid.Footer` does not switch pagination on - the option does.
265
286
  With pagination off (the default) the Footer renders nothing and every row is
266
287
  rendered, virtualized. Set `enablePagination: true` (or `manualPagination:
267
288
  true` for server paging) to page the data and show the pager.
@@ -0,0 +1,264 @@
1
+ ---
2
+ name: grouping
3
+ description: >
4
+ Group TMDataGrid rows into a tree, and total them. Covers enableGrouping,
5
+ initialState.grouping, the generated GROUP_COLUMN_ID tree lane, why a grouped
6
+ column leaves the grid and how groupedColumnMode keeps it, aggregationFn and
7
+ aggregatedCell with the registered names (sum, mean, count, uniqueCount,
8
+ extent, …), why the grid clears TanStack's "auto" default, group-row
9
+ selection, why group rows are not data rows, why grouping suspends the pager
10
+ and isPagingActive, manualGrouping for server-side trees, and the summary row
11
+ through a column footer with aggregateColumn. Load when grouping rows,
12
+ aggregating a column, adding totals along the bottom edge, or when the pager
13
+ greys out.
14
+ metadata:
15
+ type: core
16
+ library: '@jielga/tmdatagrid'
17
+ library_version: '1.0.2'
18
+ sources:
19
+ - 'Jielga/TMDataGrid:src/docs/grouping.md'
20
+ - 'Jielga/TMDataGrid:src/docs/summary-row.md'
21
+ - 'Jielga/TMDataGrid:src/tmdatagrid/core/grouping.ts'
22
+ - 'Jielga/TMDataGrid:src/tmdatagrid/core/summary.ts'
23
+ ---
24
+
25
+ # TMDataGrid - Grouping and totals
26
+
27
+ Two independent things: grouping folds the rows into a tree and totals each
28
+ category, and the summary row totals everything along the bottom edge.
29
+
30
+ ## Grouping
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
34
+ state:
35
+
36
+ ```tsx
37
+ const grid = useTMDataGrid({
38
+ data,
39
+ columns,
40
+ initialState: { grouping: ["department"] },
41
+ });
42
+ ```
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
46
+ beside the checkbox lane. Each group row shows its value, its record count and a
47
+ chevron. Grouping a second column nests.
48
+
49
+ Because a grouped column is no longer in the grid, **Ungroup** lives on the tree
50
+ column's menu, one item per grouped column. `groupedColumnMode: "reorder"`
51
+ (TanStack's own default) keeps grouped columns in the grid, moved to the front,
52
+ instead of removing them.
53
+
54
+ ## Aggregation
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.
59
+
60
+ ```tsx
61
+ columnHelper.accessor("salary", {
62
+ header: "Salary",
63
+ aggregationFn: "sum",
64
+ aggregatedCell: (info) => <strong>{sek(Number(info.getValue()))}</strong>,
65
+ meta: { type: "number", align: "right" },
66
+ });
67
+ ```
68
+
69
+ `"sum"`, `"min"`, `"max"`, `"extent"`, `"mean"`, `"median"`, `"unique"`,
70
+ `"uniqueCount"`, `"count"` and `"auto"` are registered, and a function is
71
+ accepted too.
72
+
73
+ TanStack's grouping feature defaults every column to `aggregationFn: "auto"`.
74
+ **The grid clears that default**, so grouping does not silently start summing
75
+ numeric columns. Setting `"auto"` yourself restores it.
76
+
77
+ Grouping runs before sorting, so sorting sorts the rows *inside* each group and
78
+ orders the groups by their aggregated value. A column with no aggregation has no
79
+ value on a group row, so sorting on it reorders rows within each group and
80
+ leaves the groups where they are.
81
+
82
+ ## Group rows are not data rows
83
+
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:
86
+
87
+ - do not fire `onRowClick` or the cell handlers
88
+ - cannot be highlighted, pinned, or given a details panel
89
+ - never edit
90
+ - carry `data-grouped` and `data-depth`, with `--dg-row-group-bg` behind them
91
+
92
+ A group row's checkbox selects every record under it at any depth, including
93
+ 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
96
+ how the tree is arranged. Under `enableMultiRowSelection: false` group rows carry
97
+ no checkbox.
98
+
99
+ `getGroupDataRows(row)` returns every record under a group row, at any depth.
100
+
101
+ ## Grouping suspends pagination
102
+
103
+ **Grouping and the built-in pager do not work together, and grouping wins.** As
104
+ 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
106
+ with `Grouped · all N rows`. Ungroup and paging resumes where it left off.
107
+
108
+ 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.
113
+
114
+ `isPagingActive(table, features)` is exported so a custom pager can grey itself
115
+ out the same way. To have both, page on the server: group there and feed the
116
+ grid one page of a tree at a time with `manualPagination` and `manualGrouping`.
117
+
118
+ ## The summary row
119
+
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.
122
+
123
+ ```tsx
124
+ import { aggregateColumn } from "@jielga/tmdatagrid";
125
+
126
+ columnHelper.accessor("salary", {
127
+ header: "Salary",
128
+ footer: ({ table }) =>
129
+ sek(Number(aggregateColumn({ table, columnId: "salary" }))),
130
+ });
131
+ ```
132
+
133
+ `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
136
+ functions, with `fn` defaulting to `"sum"`.
137
+
138
+ ```tsx
139
+ aggregateColumn({ table, columnId: "age", fn: "mean" });
140
+ aggregateColumn({ table, columnId: "location", fn: "uniqueCount" });
141
+ ```
142
+
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.
146
+
147
+ Pinned columns keep their lanes in the summary row, the generated lanes define
148
+ no `footer` so their cells stay blank, and the row is sticky at
149
+ `--dg-summary-height`.
150
+
151
+ ## Common mistakes
152
+
153
+ ### CRITICAL Expecting group rows to total automatically
154
+
155
+ 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.
158
+
159
+ Wrong:
160
+
161
+ ```tsx
162
+ columnHelper.accessor("salary", { header: "Salary" });
163
+ ```
164
+
165
+ Correct:
166
+
167
+ ```tsx
168
+ columnHelper.accessor("salary", { header: "Salary", aggregationFn: "sum" });
169
+ ```
170
+
171
+ Source: `src/docs/grouping.md` (Aggregation).
172
+
173
+ ### HIGH Looking for the grouped column in the grid
174
+
175
+ `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.
179
+
180
+ Correct, when the column must stay:
181
+
182
+ ```tsx
183
+ useTMDataGrid({ data, columns, groupedColumnMode: "reorder" });
184
+ ```
185
+
186
+ Source: `src/docs/grouping.md` (What grouping does to the grid).
187
+
188
+ ### HIGH Combining the pager with grouping
189
+
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
193
+ `isPagingActive(table, features)` before trusting the pager state.
194
+
195
+ Source: `src/docs/grouping.md` (Grouping suspends pagination).
196
+
197
+ ### HIGH Handing a group row to a row callback
198
+
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.
203
+
204
+ Correct:
205
+
206
+ ```tsx
207
+ import { getGroupDataRows } from "@jielga/tmdatagrid";
208
+
209
+ const records = getGroupDataRows(groupRow).map((row) => row.original);
210
+ ```
211
+
212
+ Source: `src/docs/grouping.md` (Group rows are not data rows).
213
+
214
+ ### MEDIUM Totalling the page instead of the data
215
+
216
+ `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.
220
+
221
+ Source: `src/docs/summary-row.md` (Totalling a column).
222
+
223
+ ### MEDIUM Grouping a server-paged grid
224
+
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.
227
+
228
+ Correct:
229
+
230
+ ```tsx
231
+ useTMDataGrid({
232
+ data: page.rows,
233
+ columns,
234
+ manualPagination: true,
235
+ manualGrouping: true,
236
+ enableGrouping: true,
237
+ });
238
+ ```
239
+
240
+ Source: `src/docs/grouping.md` (Server-side grids).
241
+
242
+ ## Reference
243
+
244
+ | Name | Kind | Type | Default | What it does |
245
+ | --- | --- | --- | --- | --- |
246
+ | `enableGrouping` | Table option | `boolean` | `true` | Group by and Ungroup menu items. Also a column option. |
247
+ | `groupedColumnMode` | Table option | `"reorder" \| "remove" \| false` | `"remove"` | Whether a grouped column leaves the grid or moves to the front. |
248
+ | `manualGrouping` | Table option | `boolean` | `false` | The rows arrive grouped. Required to group a server-paged grid. |
249
+ | `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. |
251
+ | `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. |
253
+ | `aggregateColumn` | Export | `({ table, columnId, fn }) => unknown` | `fn: "sum"` | Aggregates over every filtered row, all pages. |
254
+ | `TMDataGridAggregationName` | Export | type | – | The registered function names. |
255
+ | `GROUP_COLUMN_ID` | Export | `"__group__"` | – | Id of the generated tree column. |
256
+ | `formatGroupValue` | Export | `(value) => string` | – | How the tree lane renders a group's value. |
257
+ | `getGroupDataRows` | Export | `(row) => Row[]` | – | Every record under a group row, at any depth. |
258
+ | `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything. `false` while grouped. |
259
+ | `--dg-row-group-bg` | CSS variable | colour | Themed | Group row background. |
260
+ | `--dg-summary-height` | CSS variable | length | From `size` | Height of the summary row. |
261
+ | `data-grouped` · `data-depth` | Data attributes | – | – | On group rows, and the nesting level on every row. |
262
+
263
+ See also: the `rows` skill for selection and the details lane, and the `data`
264
+ skill for the pager grouping suspends.
@@ -5,28 +5,29 @@ description: >
5
5
  of TanStack TableOptions, the default initialState (pagination, columnPinning,
6
6
  globalFilterFn), the meta object (loading, noResultsLabel, rowHeight,
7
7
  totalRowCount), the grid's own persist, enableColumnOrdering,
8
- enablePagination, rowSelectionMode and highlightSelectedRows options, the
8
+ enablePagination, selectionMode and showSelectedBackground options, the
9
9
  persist option with dataKey/settingsKey slice selection and storageMode, the
10
- returned table/ui/features triple, ui panel and column drag actions, and
11
- reading grid state with useSelector. Load when configuring the hook,
12
- persisting column layout or filters, or reacting to grid state from a parent.
10
+ returned api (table, ui, edit, features, labels, resetSettings, scrollToRow),
11
+ ui panel and column drag actions, and reading grid state with useSelector.
12
+ Load when configuring the hook, persisting column layout or filters, or
13
+ reacting to grid state from a parent.
13
14
  metadata:
14
15
  type: core
15
16
  library: '@jielga/tmdatagrid'
16
- library_version: '1.0.0'
17
+ library_version: '1.0.2'
17
18
  sources:
18
19
  - 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
19
20
  - 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
20
21
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/persistence.ts'
21
22
  ---
22
23
 
23
- # TMDataGrid — useTMDataGrid
24
+ # TMDataGrid - useTMDataGrid
24
25
 
25
26
  Creates the table instance and the state used by the grid interface.
26
27
 
27
28
  ```tsx
28
29
  const grid = useTMDataGrid<TData>(options);
29
- // { table, ui, features }
30
+ // { table, ui, edit, features, labels, resetSettings, scrollToRow }
30
31
  ```
31
32
 
32
33
  Spread the result onto `TMDataGrid`.
@@ -38,18 +39,18 @@ All TanStack `TableOptions` are supported and passed through unchanged, includin
38
39
  flags and `rowCount`. The `features` option is supplied internally and cannot be
39
40
  overridden.
40
41
 
41
- `persist`, `enableColumnOrdering`, `enablePagination`, `rowSelectionMode` and
42
- `highlightSelectedRows` are the grid's own options and are consumed by the hook
42
+ `persist`, `enableColumnOrdering`, `enablePagination`, `selectionMode` and
43
+ `showSelectedBackground` are the grid's own options and are consumed by the hook
43
44
  rather than forwarded to TanStack.
44
45
 
45
46
  | Option | Type | Default | Description |
46
47
  | --- | --- | --- | --- |
47
48
  | `data` | `TData[]` | – | Row data. Keep the reference stable with `useMemo`. |
48
49
  | `columns` | `ColumnDef[]` | – | Created with `createTMDataGridColumnHelper`. |
49
- | `getRowId` | `(row, index) => string` | Row index | Used by row selection and virtualization. |
50
+ | `getRowId` | `(row, index) => string` | Row index | Used by row selection and virtualization. Required once `editMode` is set. |
50
51
  | `enableRowSelection` | `boolean \| (row) => boolean` | `true` | `false` removes row selection and its checkbox column. |
51
- | `rowSelectionMode` | `"checkbox" \| "row"` | `"checkbox"` | `"row"` drops the checkbox column and selects on row click. Defined by the grid. |
52
- | `highlightSelectedRows` | `boolean` | Follows the mode | Highlight background on selected rows: off for `"checkbox"`, on for `"row"`. Colour is the `--dg-row-selected-bg` CSS variable. Defined by the grid. |
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
+ | `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. |
53
54
  | `enableSorting` | `boolean` | `true` | Enables sorting for the table. |
54
55
  | `enableColumnFilters` | `boolean` | `true` | Enables filtering for the table. |
55
56
  | `enableHiding` | `boolean` | `true` | Enables column visibility for the table. |
@@ -64,12 +65,14 @@ rather than forwarded to TanStack.
64
65
  | `initialState` | `Partial<TableState>` | See below | Merged over the grid defaults. |
65
66
  | `meta` | `TMDataGridTableMeta` | `{}` | Grid configuration, see below. |
66
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. |
69
+ | `labels` | `TMDataGridLabelsOverride` | English | Overrides for the grid's strings and `aria-label`s. |
67
70
 
68
71
  ### Default initial state
69
72
 
70
73
  | Slice | Default |
71
74
  | --- | --- |
72
- | `pagination` | `{ pageIndex: 0, pageSize: 25 }` — inert until pagination is enabled |
75
+ | `pagination` | `{ pageIndex: 0, pageSize: 25 }` - inert until pagination is enabled |
73
76
  | `columnPinning.left` | The checkbox column, followed by any columns you provide |
74
77
  | `globalFilterFn` | `"includesString"` |
75
78
 
@@ -127,8 +130,12 @@ const persist = {
127
130
 
128
131
  | Group | Available slices |
129
132
  | --- | --- |
130
- | `dataKey` | `columnFilters`, `globalFilter`, `sorting`, `pagination` |
131
- | `settingsKey` | `columnVisibility`, `columnSizing`, `columnOrder`, `columnPinning` |
133
+ | `dataKey` | `columnFilters`, `globalFilter`, `sorting`, `pagination`, `expanded` |
134
+ | `settingsKey` | `columnVisibility`, `columnSizing`, `columnOrder`, `columnPinning`, `grouping` |
135
+
136
+ `grouping` is a settings slice because it names columns rather than values, so it
137
+ survives the data changing under it the way the column layout does. `expanded` is
138
+ a data slice for the opposite reason.
132
139
 
133
140
  `DATA_STATE_SLICES` and `SETTINGS_STATE_SLICES` export the same values. Slice
134
141
  names are typed per group, so only valid names are accepted.
@@ -136,7 +143,7 @@ names are typed per group, so only valid names are accepted.
136
143
  Restoring happens once on mount through `initialState`. Writing is a subscription
137
144
  to the table store, so state changed directly through the table API is persisted
138
145
  too. Only selected slices are read back, and unrecognised keys are ignored. All
139
- storage access is guarded — if storage is unavailable, disabled or full,
146
+ storage access is guarded - if storage is unavailable, disabled or full,
140
147
  persistence is skipped rather than throwing.
141
148
 
142
149
  ## Return value
@@ -145,7 +152,11 @@ persistence is skipped rather than throwing.
145
152
  | --- | --- | --- |
146
153
  | `table` | `Table<TMDataGridFeatures, TData>` | The TanStack table instance. |
147
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. |
148
156
  | `features` | `TMDataGridFeatureFlags` | Table-level feature switches, re-read on each render. |
157
+ | `labels` | `TMDataGridLabels` | The resolved label set, overrides merged over English. |
158
+ | `resetSettings` | `() => void` | Puts the settings state back to a clean first visit. |
159
+ | `scrollToRow` | `({ rowId, align? }) => boolean` | Scrolls a row into view through the virtualizer. `false` when the row is not in the current view. |
149
160
 
150
161
  Both stores are subscribable, which is how a parent reacts to grid state without
151
162
  owning it:
@@ -180,7 +191,7 @@ The last two move the cell cursor and the selected rectangle under
180
191
  `cellSelection`; DOM focus follows `focusedCell` and scrolls its row into view.
181
192
 
182
193
  `startColumnDrag` / `endColumnDrag` are called by the header cells while a column is being dragged, and
183
- `ui.draggedColumnId` holds the column being moved — browsers keep `dataTransfer`
194
+ `ui.draggedColumnId` holds the column being moved - browsers keep `dataTransfer`
184
195
  unreadable until the drop.
185
196
 
186
197
  `openColumnFilter(grid, columnId)` combines the two steps the column menu uses:
@@ -190,11 +201,11 @@ add an empty filter row for the column if none exists, then open the panel.
190
201
 
191
202
  ### Reading store state during render
192
203
 
193
- `grid.table.store.state` is a plain read — it does not subscribe the component,
204
+ `grid.table.store.state` is a plain read - it does not subscribe the component,
194
205
  so the value is correct on first render and then never updates.
195
206
 
196
207
  ```tsx
197
- // Wrong — never re-renders when the selection changes.
208
+ // Wrong - never re-renders when the selection changes.
198
209
  const count = Object.keys(grid.table.store.state.rowSelection).length;
199
210
 
200
211
  // Right.
@@ -219,5 +230,5 @@ Include a tenant or user identifier in the key.
219
230
  ### Unstable data reference
220
231
 
221
232
  A new array identity on each render re-runs the row model. `data={rows ?? []}`
222
- allocates a fresh `[]` every render when `rows` is undefined — hoist the empty
233
+ allocates a fresh `[]` every render when `rows` is undefined - hoist the empty
223
234
  array to a module-scope constant or `useMemo` the expression.