@jielga/tmdatagrid 1.0.1 → 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.
@@ -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,15 +5,16 @@ 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.1'
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'
@@ -26,7 +27,7 @@ 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,6 +65,8 @@ 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
 
@@ -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.
@@ -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: