@jielga/tmdatagrid 2.0.0-beta.9 → 2.0.0
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 +5 -212
- package/dist/index.d.ts +1281 -768
- package/dist/index.js +4607 -3250
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/adding-rows.md +132 -0
- package/docs/anatomy.md +119 -0
- package/docs/card-view.md +108 -0
- package/docs/cell-selection.md +194 -0
- package/docs/column-layout.md +182 -0
- package/docs/column-menu.md +66 -0
- package/docs/columns.md +268 -0
- package/docs/components.md +311 -0
- package/docs/draft-store.md +242 -0
- package/docs/editing.md +303 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +319 -0
- package/docs/filtering.md +362 -0
- package/docs/getting-started.md +123 -0
- package/docs/grouping.md +165 -0
- package/docs/loading-and-empty.md +92 -0
- package/docs/localization.md +79 -0
- package/docs/menu.md +143 -0
- package/docs/migrating-to-2.md +163 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +114 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +179 -0
- package/docs/quick-search.md +84 -0
- package/docs/row-details.md +115 -0
- package/docs/row-interaction.md +149 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +136 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +112 -0
- package/docs/server-query.md +246 -0
- package/docs/server-side.md +206 -0
- package/docs/sorting.md +101 -0
- package/docs/styling.md +126 -0
- package/docs/summary-row.md +76 -0
- package/docs/testing.md +744 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +22 -46
- package/skills/appearance/SKILL.md +72 -19
- package/skills/cell-selection/SKILL.md +46 -47
- package/skills/columns/SKILL.md +90 -34
- package/skills/data/SKILL.md +86 -16
- package/skills/editing/SKILL.md +67 -40
- package/skills/editing/references/common-mistakes.md +77 -69
- package/skills/editing/references/editing-api.md +22 -19
- package/skills/editing/references/editors-and-validation.md +24 -17
- package/skills/filtering/SKILL.md +148 -40
- package/skills/getting-started/SKILL.md +17 -15
- package/skills/grouping/SKILL.md +31 -16
- package/skills/options/SKILL.md +7 -7
- package/skills/rows/SKILL.md +22 -18
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +150 -32
- package/skills/testing-components/SKILL.md +230 -0
- package/skills/testing-editing/SKILL.md +240 -0
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
- package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +5 -51
- package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
- package/src/components/TMDataGridExportPicker.module.css +77 -0
- package/src/components/TMDataGridExportPicker.tsx +234 -0
- package/src/components/TMDataGridFilterPanel.module.css +54 -0
- package/src/components/TMDataGridFilterPanel.tsx +348 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +15 -8
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +357 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
- package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -0
- package/src/components/TMDataGridToolbar.tsx +181 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/editorShared.ts +16 -2
- package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
- package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
- package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
- package/src/components/filters/controlLayout.ts +32 -0
- package/src/components/filters/filterControlFor.ts +65 -0
- package/src/components/generatedColumns.tsx +187 -0
- package/src/{tmdatagrid/components → components}/icons.ts +1 -0
- package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
- package/src/components/useHideableColumns.ts +52 -0
- package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
- package/src/{tmdatagrid/core → core}/columnOptions.ts +60 -8
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
- package/src/core/export.ts +704 -0
- package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
- package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
- package/src/core/filterSurface.ts +99 -0
- package/src/{tmdatagrid/core → core}/grouping.ts +21 -0
- package/src/{tmdatagrid/core → core}/labels.ts +51 -6
- package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
- package/src/core/pageReset.ts +120 -0
- package/src/core/pagination.ts +81 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/{tmdatagrid/core → core}/summary.ts +20 -4
- package/src/{tmdatagrid/index.ts → index.ts} +69 -35
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
- /package/src/{tmdatagrid/core → core}/capabilities.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
- /package/src/{tmdatagrid/core → core}/controlledState.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
- /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
- /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
- /package/src/{tmdatagrid/core → core}/resizePreview.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
- /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
- /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
package/skills/grouping/SKILL.md
CHANGED
|
@@ -14,12 +14,12 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0
|
|
17
|
+
library_version: '2.0.0'
|
|
18
18
|
sources:
|
|
19
|
-
- 'Jielga/TMDataGrid:
|
|
20
|
-
- 'Jielga/TMDataGrid:
|
|
21
|
-
- 'Jielga/TMDataGrid:
|
|
22
|
-
- 'Jielga/TMDataGrid:
|
|
19
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/grouping.md'
|
|
20
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/summary-row.md'
|
|
21
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/grouping.ts'
|
|
22
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/summary.ts'
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
# TMDataGrid - Grouping and totals
|
|
@@ -86,9 +86,12 @@ would pass a row that looks real but is the wrong one. Group rows therefore:
|
|
|
86
86
|
- do not fire `onRowClick` or the cell handlers
|
|
87
87
|
- cannot be highlighted, pinned, or given a details panel
|
|
88
88
|
- never edit
|
|
89
|
+
- are still handed to `rowStyle` and `rowClassName`, with that child's record
|
|
90
|
+
as `original`, so guard a callback reading `original` with
|
|
91
|
+
`row.getIsGrouped()` and colour the group rows with `--dg-row-group-bg`
|
|
89
92
|
- carry `data-grouped="true"` and `data-depth`, with `--dg-row-group-bg`
|
|
90
|
-
behind them. `data-grouped` is on
|
|
91
|
-
|
|
93
|
+
behind them. `data-grouped` is present only on group rows, so
|
|
94
|
+
`[data-grouped]` and `[data-grouped="true"]` are equivalent
|
|
92
95
|
|
|
93
96
|
A group row's checkbox selects every record under it at any depth, including
|
|
94
97
|
records inside collapsed sub-groups, showing a tick once all are selected and a
|
|
@@ -142,7 +145,9 @@ aggregateColumn({ table, columnId: "location", fn: "uniqueCount" });
|
|
|
142
145
|
```
|
|
143
146
|
|
|
144
147
|
It follows the filters deliberately. A total that does not change as the user
|
|
145
|
-
narrows the grid is misleading.
|
|
148
|
+
narrows the grid is misleading. Its only argument is `table`, so a toolbar
|
|
149
|
+
readout or any other component holding the table reads the same total without a
|
|
150
|
+
`footer`.
|
|
146
151
|
|
|
147
152
|
Pinned columns keep their lanes in the summary row, the generated lanes define
|
|
148
153
|
no `footer` so their cells stay blank, and the row is sticky at
|
|
@@ -168,7 +173,7 @@ Correct:
|
|
|
168
173
|
columnHelper.accessor("salary", { header: "Salary", aggregationFn: "sum" });
|
|
169
174
|
```
|
|
170
175
|
|
|
171
|
-
Source: `
|
|
176
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (Aggregation).
|
|
172
177
|
|
|
173
178
|
### HIGH Looking for the grouped column in the grid
|
|
174
179
|
|
|
@@ -183,7 +188,7 @@ Correct, when the column must stay:
|
|
|
183
188
|
useTMDataGrid({ data, columns, groupedColumnMode: "reorder" });
|
|
184
189
|
```
|
|
185
190
|
|
|
186
|
-
Source: `
|
|
191
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (What grouping does to the grid).
|
|
187
192
|
|
|
188
193
|
### HIGH Combining the pager with grouping
|
|
189
194
|
|
|
@@ -192,14 +197,16 @@ out instead of paging the tree, so a footer count wired to `getPageCount()`
|
|
|
192
197
|
reports a number nobody can navigate to. Read
|
|
193
198
|
`isPagingActive(table, features)` before trusting the pager state.
|
|
194
199
|
|
|
195
|
-
Source: `
|
|
200
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (Grouping suspends pagination).
|
|
196
201
|
|
|
197
202
|
### HIGH Handing a group row to a row callback
|
|
198
203
|
|
|
199
204
|
Group rows do not fire `onRowClick` or the cell handlers, and cannot be pinned,
|
|
200
205
|
expanded or edited. Their `row.original` is an arbitrary child's record, so a
|
|
201
206
|
bulk action built from `row.original` on the tree lane acts on one record
|
|
202
|
-
instead of the group.
|
|
207
|
+
instead of the group. `rowStyle` and `rowClassName` are the callbacks group
|
|
208
|
+
rows do reach, so one reading `row.original` colours the group by whichever
|
|
209
|
+
child came first.
|
|
203
210
|
|
|
204
211
|
Correct:
|
|
205
212
|
|
|
@@ -207,9 +214,17 @@ Correct:
|
|
|
207
214
|
import { getGroupDataRows } from "@jielga/tmdatagrid";
|
|
208
215
|
|
|
209
216
|
const records = getGroupDataRows(groupRow).map((row) => row.original);
|
|
217
|
+
|
|
218
|
+
<TMDataGrid.Table<Employee>
|
|
219
|
+
rowStyle={(row) =>
|
|
220
|
+
!row.getIsGrouped() && row.original.status === "Terminated"
|
|
221
|
+
? { "--row-bg": "color-mix(in srgb, var(--mantine-color-red-6) 12%, transparent)" }
|
|
222
|
+
: undefined
|
|
223
|
+
}
|
|
224
|
+
/>;
|
|
210
225
|
```
|
|
211
226
|
|
|
212
|
-
Source: `
|
|
227
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (Group rows are not data rows).
|
|
213
228
|
|
|
214
229
|
### MEDIUM Totalling the page instead of the data
|
|
215
230
|
|
|
@@ -217,7 +232,7 @@ Source: `src/docs/grouping.md` (Group rows are not data rows).
|
|
|
217
232
|
`table.getRowModel().rows` instead totals only what is currently paged in, and
|
|
218
233
|
under virtualization not even that: only the mounted rows.
|
|
219
234
|
|
|
220
|
-
Source: `
|
|
235
|
+
Source: `packages/tmdatagrid/docs/summary-row.md` (Totalling a column).
|
|
221
236
|
|
|
222
237
|
### MEDIUM Grouping a server-paged grid
|
|
223
238
|
|
|
@@ -237,7 +252,7 @@ useTMDataGrid({
|
|
|
237
252
|
});
|
|
238
253
|
```
|
|
239
254
|
|
|
240
|
-
Source: `
|
|
255
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (Server-side grids).
|
|
241
256
|
|
|
242
257
|
## Reference
|
|
243
258
|
|
|
@@ -258,7 +273,7 @@ Source: `src/docs/grouping.md` (Server-side grids).
|
|
|
258
273
|
| `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything. `false` while grouped. |
|
|
259
274
|
| `--dg-row-group-bg` | CSS variable | colour | Themed | Group row background. |
|
|
260
275
|
| `--dg-summary-height` | CSS variable | length | From `size` | Height of the summary row. |
|
|
261
|
-
| `data-grouped` · `data-depth` | Data attributes | – | – | `"true"` on group rows (
|
|
276
|
+
| `data-grouped` · `data-depth` | Data attributes | – | – | `"true"` on group rows (absent on the rest), and the nesting level on every row. |
|
|
262
277
|
|
|
263
278
|
See also: the `rows` skill for selection and the details lane, and the `data`
|
|
264
279
|
skill for the pager grouping suspends.
|
package/skills/options/SKILL.md
CHANGED
|
@@ -14,11 +14,11 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0
|
|
17
|
+
library_version: '2.0.0'
|
|
18
18
|
sources:
|
|
19
|
-
- 'Jielga/TMDataGrid:
|
|
20
|
-
- 'Jielga/TMDataGrid:
|
|
21
|
-
- 'Jielga/TMDataGrid:
|
|
19
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/use-tm-data-grid.md'
|
|
20
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/useTMDataGrid.tsx'
|
|
21
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/persistence.ts'
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
# TMDataGrid - useTMDataGrid
|
|
@@ -74,7 +74,7 @@ rather than forwarded to TanStack.
|
|
|
74
74
|
| Slice | Default |
|
|
75
75
|
| --- | --- |
|
|
76
76
|
| `pagination` | `{ pageIndex: 0, pageSize: 25 }` - inert until pagination is enabled |
|
|
77
|
-
| `columnPinning.
|
|
77
|
+
| `columnPinning.start` | The checkbox column, followed by any columns you provide |
|
|
78
78
|
| `globalFilterFn` | `"includesString"` |
|
|
79
79
|
|
|
80
80
|
### Controlled state
|
|
@@ -208,8 +208,8 @@ const filterPanelOpen = useSelector(grid.ui, (state) => state.filterPanelOpen);
|
|
|
208
208
|
| --- | --- |
|
|
209
209
|
| `openFilterPanel` | `(columnId?: string \| null) => void` |
|
|
210
210
|
| `closeFilterPanel` | `() => void` |
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
211
|
+
| `focusPanelFilter` | `(columnId: string \| null) => void` |
|
|
212
|
+
| `focusHeaderFilter` | `(columnId: string \| null) => void` |
|
|
213
213
|
| `startColumnDrag` | `(columnId: string) => void` |
|
|
214
214
|
| `endColumnDrag` | `() => void` |
|
|
215
215
|
| `setHighlightedRow` | `(rowId: string \| null) => void` |
|
package/skills/rows/SKILL.md
CHANGED
|
@@ -17,14 +17,15 @@ description: >
|
|
|
17
17
|
metadata:
|
|
18
18
|
type: core
|
|
19
19
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '2.0.0
|
|
20
|
+
library_version: '2.0.0'
|
|
21
21
|
sources:
|
|
22
|
-
- 'Jielga/TMDataGrid:
|
|
23
|
-
- 'Jielga/TMDataGrid:
|
|
24
|
-
- 'Jielga/TMDataGrid:
|
|
25
|
-
- 'Jielga/TMDataGrid:
|
|
26
|
-
- 'Jielga/TMDataGrid:
|
|
27
|
-
- 'Jielga/TMDataGrid:
|
|
22
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-selection.md'
|
|
23
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-interaction.md'
|
|
24
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/column-menu.md'
|
|
25
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-styling.md'
|
|
26
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-details.md'
|
|
27
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/row-pinning.md'
|
|
28
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/rowSelection.ts'
|
|
28
29
|
---
|
|
29
30
|
|
|
30
31
|
# TMDataGrid - Rows
|
|
@@ -187,13 +188,15 @@ array rather than a node:
|
|
|
187
188
|
|
|
188
189
|
Returning an empty list leaves the column with no menu button at all.
|
|
189
190
|
|
|
191
|
+
Source: `packages/tmdatagrid/docs/column-menu.md`.
|
|
192
|
+
|
|
190
193
|
## Row styling
|
|
191
194
|
|
|
192
195
|
```tsx
|
|
193
196
|
<TMDataGrid.Table<Employee>
|
|
194
197
|
striped
|
|
195
198
|
rowStyle={(row) =>
|
|
196
|
-
row.original.status === "Terminated"
|
|
199
|
+
!row.getIsGrouped() && row.original.status === "Terminated"
|
|
197
200
|
? { "--row-bg": "color-mix(in srgb, var(--mantine-color-red-6) 12%, transparent)" }
|
|
198
201
|
: undefined
|
|
199
202
|
}
|
|
@@ -211,8 +214,9 @@ are not striped.
|
|
|
211
214
|
Rows carry `data-selected`, `data-selected-bg`, `data-highlighted`,
|
|
212
215
|
`data-grouped`, `data-depth`, `data-context-menu` and `data-row-id`, so a
|
|
213
216
|
stylesheet can target any of it without a callback. The boolean attributes are
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
present, with the value `"true"`, only while they apply - `data-grouped` only
|
|
218
|
+
on group rows - so `[data-grouped]` and `[data-grouped="true"]` are
|
|
219
|
+
equivalent. Pick a `--row-bg` that
|
|
216
220
|
reads under both colour schemes - a `-0` Mantine shade is near-white and
|
|
217
221
|
unreadable in dark mode; mix a mid shade into transparency instead.
|
|
218
222
|
|
|
@@ -295,7 +299,7 @@ Correct:
|
|
|
295
299
|
rowStyle={() => ({ "--row-bg": "pink" })}
|
|
296
300
|
```
|
|
297
301
|
|
|
298
|
-
Source: `
|
|
302
|
+
Source: `packages/tmdatagrid/docs/row-styling.md` (Set `--row-bg`, not `background`).
|
|
299
303
|
|
|
300
304
|
### CRITICAL Reading the selection without subscribing
|
|
301
305
|
|
|
@@ -317,7 +321,7 @@ const selected = useSelector(grid.table.store, () =>
|
|
|
317
321
|
);
|
|
318
322
|
```
|
|
319
323
|
|
|
320
|
-
Source: `
|
|
324
|
+
Source: `packages/tmdatagrid/docs/row-selection.md` (Acting on a selection).
|
|
321
325
|
|
|
322
326
|
### HIGH Looking for the highlight in `rowSelection`
|
|
323
327
|
|
|
@@ -338,7 +342,7 @@ Correct:
|
|
|
338
342
|
const current = useSelector(grid.ui, (state) => state.highlightedRowId);
|
|
339
343
|
```
|
|
340
344
|
|
|
341
|
-
Source: `
|
|
345
|
+
Source: `packages/tmdatagrid/docs/row-selection.md` (The highlight is not a selection).
|
|
342
346
|
|
|
343
347
|
### HIGH Expecting a click handler to replace the built-in behaviour
|
|
344
348
|
|
|
@@ -353,7 +357,7 @@ Correct, when the click should only navigate:
|
|
|
353
357
|
useTMDataGrid({ data, columns, selectionMode: "highlight" });
|
|
354
358
|
```
|
|
355
359
|
|
|
356
|
-
Source: `
|
|
360
|
+
Source: `packages/tmdatagrid/docs/row-interaction.md`.
|
|
357
361
|
|
|
358
362
|
### HIGH Reading `row.getIsExpanded()` in a cell without subscribing
|
|
359
363
|
|
|
@@ -366,7 +370,7 @@ Correct:
|
|
|
366
370
|
const expanded = useSelector(row.table.store, () => row.getIsExpanded());
|
|
367
371
|
```
|
|
368
372
|
|
|
369
|
-
Source: `
|
|
373
|
+
Source: `packages/tmdatagrid/docs/row-details.md` (Opening a row from elsewhere).
|
|
370
374
|
|
|
371
375
|
### MEDIUM Assuming group rows behave like data rows
|
|
372
376
|
|
|
@@ -375,7 +379,7 @@ Group rows are built on their first child's record. They do not fire
|
|
|
375
379
|
never pin, they take no row number, and they have no details panel. A handler
|
|
376
380
|
written as though every row reaches it silently skips them.
|
|
377
381
|
|
|
378
|
-
Source: `
|
|
382
|
+
Source: `packages/tmdatagrid/docs/row-interaction.md`, `packages/tmdatagrid/docs/row-pinning.md`.
|
|
379
383
|
|
|
380
384
|
### MEDIUM Open panels closing when `data` is replaced
|
|
381
385
|
|
|
@@ -388,7 +392,7 @@ Correct:
|
|
|
388
392
|
useTMDataGrid({ data, columns, renderDetails, autoResetExpanded: false });
|
|
389
393
|
```
|
|
390
394
|
|
|
391
|
-
Source: `
|
|
395
|
+
Source: `packages/tmdatagrid/docs/row-details.md`.
|
|
392
396
|
|
|
393
397
|
### MEDIUM Expecting pinned rows to persist
|
|
394
398
|
|
|
@@ -396,7 +400,7 @@ Source: `src/docs/row-details.md`.
|
|
|
396
400
|
layout store outlives any one data set. A pinned id whose row leaves `data` is
|
|
397
401
|
not shown, and returns to its edge if the data comes back.
|
|
398
402
|
|
|
399
|
-
Source: `
|
|
403
|
+
Source: `packages/tmdatagrid/docs/row-pinning.md`.
|
|
400
404
|
|
|
401
405
|
## References
|
|
402
406
|
|
|
@@ -4,16 +4,22 @@ description: >
|
|
|
4
4
|
Drive TMDataGrid from a server with TanStack manual modes - manualPagination,
|
|
5
5
|
manualSorting, manualFiltering, rowCount, controlled state and onXChange
|
|
6
6
|
callbacks. Covers the loading and totalRowCount meta fields, forwarding the
|
|
7
|
-
plain-JSON columnFilters model to an API with
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
plain-JSON columnFilters model to an API with activeColumnFilters, mapping
|
|
8
|
+
filters, sorting and the page index onto an endpoint's own query language
|
|
9
|
+
(field table, operator table, the three value shapes, meta.filter.operators
|
|
10
|
+
for an endpoint that answers only some operators, keying the fetch on the
|
|
11
|
+
request, paging against a page envelope), the first-page reset on a query
|
|
12
|
+
change, persistence interaction, and row selection across pages. Load when the
|
|
13
|
+
grid is backed by a paginated API rather than a local array, or when
|
|
14
|
+
translating grid filters into server-side queries.
|
|
10
15
|
metadata:
|
|
11
16
|
type: core
|
|
12
17
|
library: '@jielga/tmdatagrid'
|
|
13
|
-
library_version: '2.0.0
|
|
18
|
+
library_version: '2.0.0'
|
|
14
19
|
sources:
|
|
15
|
-
- 'Jielga/TMDataGrid:
|
|
16
|
-
- 'Jielga/TMDataGrid:
|
|
20
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/server-side.md'
|
|
21
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/server-query.md'
|
|
22
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/useTMDataGrid.tsx'
|
|
17
23
|
---
|
|
18
24
|
|
|
19
25
|
# TMDataGrid - Server-side data
|
|
@@ -68,12 +74,32 @@ const grid = useTMDataGrid({
|
|
|
68
74
|
| --- | --- | --- |
|
|
69
75
|
| Rendered rows | The current page, sliced locally | The rows returned by the server |
|
|
70
76
|
| Footer total | Pre-paginated row count | `options.rowCount` |
|
|
71
|
-
| `SummaryCount` total | Pre-filtered row count | `meta.totalRowCount
|
|
77
|
+
| `SummaryCount` total | Pre-filtered row count | `meta.totalRowCount`; the count alone without it |
|
|
72
78
|
| Loading state | Not applicable | `meta.loading` |
|
|
73
79
|
|
|
74
80
|
Column menus, the filter panel and the column manager behave identically in both
|
|
75
81
|
modes.
|
|
76
82
|
|
|
83
|
+
## The page index
|
|
84
|
+
|
|
85
|
+
Under `manualPagination` the grid resets `pageIndex` to 0 whenever the query
|
|
86
|
+
changes - a column filter, the quick search or the sort - so the next request
|
|
87
|
+
never asks for page 8 of a result set that now has three. The reset lands in
|
|
88
|
+
the same event as the change, so one request goes out. Pass the plain setters
|
|
89
|
+
as the change callbacks; do not pair them with a page reset of your own.
|
|
90
|
+
|
|
91
|
+
`resetPageOnQueryChange: false` switches it off. TanStack's
|
|
92
|
+
`autoResetPageIndex` is a different rule: it defaults to `!manualPagination`
|
|
93
|
+
and fires on a change to `data`, which server-side is the response landing.
|
|
94
|
+
|
|
95
|
+
## Column options
|
|
96
|
+
|
|
97
|
+
`meta.options: "faceted"` reads the distinct values in `data`, which
|
|
98
|
+
server-side is one page of them - the dropdown then offers whatever was on the
|
|
99
|
+
page the user is looking at. Declare the set as a list or a function instead.
|
|
100
|
+
The grid warns once per column when a faceted column resolves under
|
|
101
|
+
`manualFiltering` or `manualPagination`.
|
|
102
|
+
|
|
77
103
|
## Sending filters
|
|
78
104
|
|
|
79
105
|
Filter values are plain JSON, so `columnFilters` forwards without transformation:
|
|
@@ -85,17 +111,136 @@ Filter values are plain JSON, so `columnFilters` forwards without transformation
|
|
|
85
111
|
]
|
|
86
112
|
```
|
|
87
113
|
|
|
88
|
-
Translate at the API boundary
|
|
89
|
-
|
|
114
|
+
Translate at the API boundary. `activeColumnFilters` hands back the entries
|
|
115
|
+
that narrow anything, typed - `ColumnFiltersState` types `value` as `unknown`,
|
|
116
|
+
and an entry whose value is still empty matches all rows:
|
|
90
117
|
|
|
91
118
|
```ts
|
|
92
|
-
import {
|
|
119
|
+
import { activeColumnFilters } from "@jielga/tmdatagrid";
|
|
93
120
|
|
|
94
|
-
const active = columnFilters
|
|
121
|
+
const active = activeColumnFilters(columnFilters);
|
|
122
|
+
// [{ id: "lastName", value: { operator: "contains", value: "holm" } }]
|
|
95
123
|
```
|
|
96
124
|
|
|
125
|
+
It takes the `columnFilters` array, or the table where the grid owns the slice.
|
|
126
|
+
`isFilterActive(value)` is the single-value test it is built on. Only the
|
|
127
|
+
grid's own `{ operator, value }` shape is read: an entry holding some other
|
|
128
|
+
value - a custom filter control writing raw values - is dropped.
|
|
129
|
+
|
|
97
130
|
Debounce requests. The filter value input updates on every keystroke.
|
|
98
131
|
|
|
132
|
+
## Mapping onto the endpoint's query language
|
|
133
|
+
|
|
134
|
+
An API takes a request body of its own: its own field names, its own operator
|
|
135
|
+
set, its own status codes, and pages counted from 1. The layer between the
|
|
136
|
+
grid's state and that body is one function over two lookup tables, plus one
|
|
137
|
+
function on the way back:
|
|
138
|
+
|
|
139
|
+
- **A field table** keyed by column id, giving the API field and the cast from
|
|
140
|
+
the string every filter control writes to the type the field holds
|
|
141
|
+
(`Number`, an enum code). A column missing from the table is one the API
|
|
142
|
+
cannot query: the mapping drops the filter rather than sending a field the
|
|
143
|
+
endpoint would reject.
|
|
144
|
+
- **An operator table** from `TMDataGridFilterOperator` to the API's operators.
|
|
145
|
+
Several grid operators collapse onto one - a date `before` and a number
|
|
146
|
+
`lessThan` are both `lt` once the value is cast. Declare it as a `Record`, not
|
|
147
|
+
a `Partial`, so an operator added by a later grid version fails the build
|
|
148
|
+
here rather than reaching the server unmapped.
|
|
149
|
+
- **`toRow`** from the API's record to the grid's row type.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const QUERY_FIELDS: Record<string, { field: string; cast: (raw: string) => string | number }> = {
|
|
153
|
+
id: { field: "orderRef", cast: Number },
|
|
154
|
+
amount: { field: "totalAmount", cast: Number },
|
|
155
|
+
status: { field: "status", cast: (raw) => STATUS_CODES[raw] ?? raw },
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const PREDICATE_OPS: Record<TMDataGridFilterOperator, PredicateOp> = {
|
|
159
|
+
contains: "like",
|
|
160
|
+
between: "range",
|
|
161
|
+
before: "lt",
|
|
162
|
+
lessThan: "lt",
|
|
163
|
+
isAnyOf: "in",
|
|
164
|
+
isEmpty: "isNull",
|
|
165
|
+
// ...one line for every remaining operator.
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### The three value shapes
|
|
170
|
+
|
|
171
|
+
`TMDataGridFilterValue` is `{ operator, value }`, and the operator decides what
|
|
172
|
+
`value` holds. Branch on all four cases, in this order:
|
|
173
|
+
|
|
174
|
+
| Operator | `value` | Sent as |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `isEmpty`, `isNotEmpty` | Not used | `{ field, op }` |
|
|
177
|
+
| `isAnyOf`, `isNoneOf` | `ReadonlyArray<string>` | `{ field, op, values }` |
|
|
178
|
+
| `between` | `[min, max]`, either end possibly `""` | `{ field, op, from?, to? }` |
|
|
179
|
+
| Everything else | `string` | `{ field, op, value }` |
|
|
180
|
+
|
|
181
|
+
An empty end of a `between` pair leaves that side open: an absent bound, not an
|
|
182
|
+
empty string. Run `activeColumnFilters` over the slice first so a half-typed
|
|
183
|
+
filter is not sent as a predicate that narrows the result to nothing.
|
|
184
|
+
|
|
185
|
+
### An endpoint that answers only some operators
|
|
186
|
+
|
|
187
|
+
Most endpoints do not have every operator the grid has - `like` and `eq` but no
|
|
188
|
+
prefix match is common. Do not offer what you would have to drop.
|
|
189
|
+
`meta.filter.operators` narrows the column to the operators the query can
|
|
190
|
+
express, and the mapping table is declared over exactly that list, so one
|
|
191
|
+
cannot be offered without a mapping or mapped without being offered:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const TEXT_OPERATORS = [
|
|
195
|
+
"contains",
|
|
196
|
+
"equals",
|
|
197
|
+
"isEmpty",
|
|
198
|
+
"isNotEmpty",
|
|
199
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
200
|
+
|
|
201
|
+
const TEXT_OPS: Record<(typeof TEXT_OPERATORS)[number], PredicateOp> = {
|
|
202
|
+
contains: "like",
|
|
203
|
+
equals: "eq",
|
|
204
|
+
isEmpty: "isNull",
|
|
205
|
+
isNotEmpty: "isNotNull",
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
columnHelper.accessor("customer", {
|
|
209
|
+
header: "Customer",
|
|
210
|
+
meta: { filter: { operators: TEXT_OPERATORS } },
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
A fresh filter opens on `meta.filter.defaultOperator` when set, else on the
|
|
215
|
+
type's default when the list holds it, else on the first entry. The lookup at
|
|
216
|
+
the boundary still returns `undefined` for an unmapped operator: a filter
|
|
217
|
+
restored by `persist` from before the list was narrowed can carry one.
|
|
218
|
+
|
|
219
|
+
### Keying the fetch on the request
|
|
220
|
+
|
|
221
|
+
Serialize the request with `JSON.stringify` inside `useMemo` over
|
|
222
|
+
`columnFilters`, `sorting` and `pagination`, and key the fetch effect (or the
|
|
223
|
+
TanStack Query `queryKey`) on that string. Opening the panel and adding an
|
|
224
|
+
empty row moves `columnFilters` but leaves the request unchanged, so nothing is
|
|
225
|
+
sent. The effect owes the server a debounce (the value input updates on every
|
|
226
|
+
keystroke) and a cancel (a `cancelled` flag in the cleanup, or an
|
|
227
|
+
`AbortController` on a real `fetch`).
|
|
228
|
+
|
|
229
|
+
### Paging against a page envelope
|
|
230
|
+
|
|
231
|
+
| The API's | The grid's | Written as |
|
|
232
|
+
| --- | --- | --- |
|
|
233
|
+
| `page.number`, counted from 1 | `pagination.pageIndex`, counted from 0 | `number: pageIndex + 1` |
|
|
234
|
+
| `page.totalItems`, the matched count | `rowCount` | `rowCount: page?.totalItems ?? 0` |
|
|
235
|
+
| `page.totalPages` | `state.pageCount`, derived from `rowCount / pageSize` | Nothing; the grid computes it |
|
|
236
|
+
|
|
237
|
+
Forward `totalPages` only when the server pages by something other than the
|
|
238
|
+
size the grid asked for. `pageCount: -1` when the total is unknown. Show the
|
|
239
|
+
page number through the Footer's `renderPagination` slot with
|
|
240
|
+
`<Controls.PageSize /><Controls.PageNumber /><Controls.Pager />`.
|
|
241
|
+
`meta.totalRowCount` is the unfiltered total, which no filtered response
|
|
242
|
+
carries: take it from a separate count call.
|
|
243
|
+
|
|
99
244
|
## Persistence
|
|
100
245
|
|
|
101
246
|
`persist` works unchanged. `dataKey` restores filters, sorting and pagination
|
|
@@ -126,18 +271,26 @@ Without `rowCount` the table derives the total from the rows it was handed -
|
|
|
126
271
|
one page - so `getPageCount()` returns 1. The footer shows "1–25 of 25" and the
|
|
127
272
|
next-page button is disabled, with no error. Pass the server total.
|
|
128
273
|
|
|
129
|
-
### Filters sent without
|
|
274
|
+
### Filters sent without activeColumnFilters
|
|
130
275
|
|
|
131
276
|
An empty filter value stays in `columnFilters` while the user is still typing.
|
|
132
277
|
Forwarded verbatim it becomes `operator: "contains", value: ""` at the API,
|
|
133
|
-
which most backends translate into a real predicate.
|
|
134
|
-
`
|
|
278
|
+
which most backends translate into a real predicate. Map over
|
|
279
|
+
`activeColumnFilters(columnFilters)` instead of over the slice.
|
|
135
280
|
|
|
136
281
|
### SummaryCount without meta.totalRowCount
|
|
137
282
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
283
|
+
Without it there is no denominator to show: the pre-filtered row count is the
|
|
284
|
+
current page, so the grid renders the matched count alone rather than a
|
|
285
|
+
plausible-looking wrong total. Pass the unfiltered total as
|
|
286
|
+
`meta.totalRowCount` to get the "42 / 5000" form back.
|
|
287
|
+
|
|
288
|
+
### Offering operators the endpoint cannot answer
|
|
289
|
+
|
|
290
|
+
The panel offers every operator of the column's type, and a mapping that drops
|
|
291
|
+
`startsWith` leaves the user with a filter that silently does nothing. Declare
|
|
292
|
+
`meta.filter.operators` on the column with the operators the endpoint answers,
|
|
293
|
+
and type the operator table over that same list.
|
|
141
294
|
|
|
142
295
|
### Unstable getRowId across pages
|
|
143
296
|
|