@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.
- package/README.md +8 -8
- package/dist/index.d.ts +225 -225
- package/dist/index.js +58 -50
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -2
- package/skills/appearance/SKILL.md +322 -0
- package/skills/cell-selection/SKILL.md +240 -0
- package/skills/columns/SKILL.md +261 -86
- package/skills/data/SKILL.md +289 -0
- package/skills/editing/SKILL.md +492 -0
- package/skills/editing/references/editing-api.md +124 -0
- package/skills/editing/references/editors-and-validation.md +198 -0
- package/skills/filtering/SKILL.md +344 -0
- package/skills/getting-started/SKILL.md +48 -27
- package/skills/grouping/SKILL.md +264 -0
- package/skills/options/SKILL.md +31 -20
- package/skills/rows/SKILL.md +369 -0
- package/skills/rows/references/rows-api.md +117 -0
- package/skills/server-side/SKILL.md +7 -7
- package/skills/testing/SKILL.md +12 -12
- package/src/tmdatagrid/TMDataGridContext.ts +2 -2
- package/src/tmdatagrid/components/TMDataGrid.module.css +2 -2
- package/src/tmdatagrid/components/TMDataGrid.tsx +5 -5
- package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +4 -4
- package/src/tmdatagrid/components/TMDataGridColumnsPanel.tsx +2 -2
- package/src/tmdatagrid/components/TMDataGridDetailsColumn.tsx +6 -6
- package/src/tmdatagrid/components/TMDataGridEditActions.tsx +2 -2
- package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +4 -4
- package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +6 -6
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +6 -6
- package/src/tmdatagrid/components/TMDataGridFilterPills.module.css +2 -2
- package/src/tmdatagrid/components/TMDataGridFilterPills.tsx +1 -1
- package/src/tmdatagrid/components/TMDataGridFooter.module.css +1 -1
- package/src/tmdatagrid/components/TMDataGridFooter.tsx +12 -6
- package/src/tmdatagrid/components/TMDataGridGroupColumn.module.css +1 -1
- package/src/tmdatagrid/components/TMDataGridGroupColumn.tsx +5 -5
- package/src/tmdatagrid/components/TMDataGridHeaderCell.module.css +8 -8
- package/src/tmdatagrid/components/TMDataGridHeaderCell.tsx +12 -12
- package/src/tmdatagrid/components/TMDataGridRowNumberColumn.tsx +4 -4
- package/src/tmdatagrid/components/TMDataGridSearch.tsx +5 -5
- package/src/tmdatagrid/components/TMDataGridSelectColumn.tsx +8 -8
- package/src/tmdatagrid/components/TMDataGridTable.module.css +18 -18
- package/src/tmdatagrid/components/TMDataGridTable.tsx +116 -116
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +5 -5
- package/src/tmdatagrid/components/editors/TMDataGridBooleanEditor.tsx +1 -1
- package/src/tmdatagrid/components/editors/TMDataGridDateEditor.tsx +2 -2
- package/src/tmdatagrid/components/editors/TMDataGridMultiSelectEditor.tsx +1 -1
- package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +1 -1
- package/src/tmdatagrid/components/editors/TMDataGridSelectEditor.tsx +2 -2
- package/src/tmdatagrid/components/editors/editorShared.ts +3 -3
- package/src/tmdatagrid/components/filters/DgDateRangeFilter.tsx +1 -1
- package/src/tmdatagrid/components/filters/DgRangeSliderFilter.tsx +1 -1
- package/src/tmdatagrid/components/filters/DgTriStateFilter.tsx +1 -1
- package/src/tmdatagrid/components/filters/TMDataGridFilterValueInput.tsx +2 -2
- package/src/tmdatagrid/components/sticky.module.css +8 -8
- package/src/tmdatagrid/core/autosize.ts +4 -4
- package/src/tmdatagrid/core/capabilities.ts +17 -17
- package/src/tmdatagrid/core/cellExport.ts +13 -13
- package/src/tmdatagrid/core/cellNavigation.ts +6 -6
- package/src/tmdatagrid/core/cellRange.ts +4 -4
- package/src/tmdatagrid/core/columnOptions.ts +2 -2
- package/src/tmdatagrid/core/columnOrdering.ts +2 -2
- package/src/tmdatagrid/core/columnUtils.ts +3 -3
- package/src/tmdatagrid/core/editEngine.ts +49 -49
- package/src/tmdatagrid/core/expanding.ts +5 -5
- package/src/tmdatagrid/core/filterControls.ts +5 -5
- package/src/tmdatagrid/core/filterOperators.ts +14 -14
- package/src/tmdatagrid/core/labels.ts +8 -8
- package/src/tmdatagrid/core/matchHighlight.ts +4 -4
- package/src/tmdatagrid/core/persistence.ts +8 -8
- package/src/tmdatagrid/core/quickSearch.ts +8 -8
- package/src/tmdatagrid/core/rowPinning.ts +3 -3
- package/src/tmdatagrid/core/rowSelection.ts +12 -12
- package/src/tmdatagrid/core/sizes.ts +1 -1
- package/src/tmdatagrid/core/summary.ts +3 -3
- package/src/tmdatagrid/useTMDataGrid.tsx +79 -79
- 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,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
parts to render, or when
|
|
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.
|
|
14
|
+
library_version: '1.0.2'
|
|
14
15
|
sources:
|
|
15
16
|
- 'Jielga/TMDataGrid:src/docs/getting-started.md'
|
|
16
|
-
- 'Jielga/TMDataGrid:src/docs/
|
|
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
|
|
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`
|
|
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 `
|
|
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
|
|
111
|
-
option also removes its interface
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
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 `
|
|
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` |
|
|
155
|
+
| `TMDataGrid.Spacer` | - | Pushes later toolbar items right. |
|
|
138
156
|
| `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
|
|
139
|
-
| `TMDataGrid.
|
|
140
|
-
| `TMDataGrid.
|
|
141
|
-
| `TMDataGrid.
|
|
142
|
-
| `TMDataGrid.
|
|
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`
|
|
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
|
|
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 `
|
|
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
|
|
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
|
|
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
|
|
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.
|
package/skills/options/SKILL.md
CHANGED
|
@@ -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,
|
|
8
|
+
enablePagination, selectionMode and showSelectedBackground options, the
|
|
9
9
|
persist option with dataKey/settingsKey slice selection and storageMode, the
|
|
10
|
-
returned table
|
|
11
|
-
reading grid state with useSelector.
|
|
12
|
-
persisting column layout or filters, or
|
|
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.
|
|
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
|
|
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`, `
|
|
42
|
-
`
|
|
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
|
-
| `
|
|
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
|
+
| `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 }`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|