@jielga/tmdatagrid 2.1.4 → 2.3.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/dist/index.d.ts +78 -5
- package/dist/index.js +2194 -1992
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/cell-selection.md +1 -1
- package/docs/column-layout.md +13 -8
- package/docs/columns.md +59 -3
- package/docs/components.md +1 -0
- package/docs/export.md +2 -2
- package/docs/filtering.md +108 -15
- package/docs/getting-started.md +40 -0
- package/docs/grouping.md +9 -6
- package/docs/loading-and-empty.md +29 -9
- package/docs/localization.md +130 -3
- package/docs/menu.md +1 -1
- package/docs/pagination.md +26 -7
- package/docs/persistence.md +101 -2
- package/docs/row-details.md +41 -4
- package/docs/row-interaction.md +2 -2
- package/docs/row-selection.md +6 -7
- package/docs/row-styling.md +17 -0
- package/docs/scrolling.md +1 -1
- package/docs/server-side.md +3 -1
- package/docs/testing.md +18 -0
- package/docs/use-tm-data-grid.md +75 -36
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +6 -4
- package/skills/appearance/references/labels.md +126 -0
- package/skills/cell-selection/SKILL.md +2 -2
- package/skills/columns/SKILL.md +44 -43
- package/skills/columns/references/columns-api.md +16 -1
- package/skills/data/SKILL.md +77 -50
- package/skills/editing/SKILL.md +1 -1
- package/skills/filtering/SKILL.md +94 -60
- package/skills/filtering/references/custom-filters.md +118 -0
- package/skills/getting-started/SKILL.md +37 -2
- package/skills/grouping/SKILL.md +16 -9
- package/skills/migrating-to-2/SKILL.md +1 -1
- package/skills/options/SKILL.md +206 -55
- package/skills/rows/SKILL.md +70 -39
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +12 -1
- package/skills/testing-components/SKILL.md +1 -1
- package/skills/testing-editing/SKILL.md +1 -1
- package/src/components/TMDataGridFilterPills.module.css +9 -7
- package/src/components/TMDataGridTable.module.css +15 -6
- package/src/components/TMDataGridTable.tsx +253 -41
- package/src/components/sticky.module.css +27 -7
- package/src/core/autosize.ts +19 -7
- package/src/core/cellRange.ts +11 -2
- package/src/core/colSpan.ts +78 -0
- package/src/core/columnUtils.ts +7 -0
- package/src/core/filterControls.ts +8 -0
- package/src/core/filterOperators.ts +29 -1
- package/src/core/persistence.ts +322 -26
- package/src/index.ts +5 -0
- package/src/useTMDataGrid.tsx +133 -14
package/skills/options/SKILL.md
CHANGED
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
name: options
|
|
3
3
|
description: >
|
|
4
4
|
useTMDataGrid options, return value and state persistence. Covers passthrough
|
|
5
|
-
of TanStack TableOptions,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
of TanStack TableOptions, every default the grid changes (globalFilterFn,
|
|
6
|
+
groupedColumnMode, autoResetExpanded, autoResetPageIndex,
|
|
7
|
+
defaultColumn.minSize), the default initialState, the meta object (loading,
|
|
8
|
+
noResultsLabel, rowHeight, totalRowCount), the grid's own options (persist,
|
|
9
|
+
labels, editing, filters, exportOptions, enableColumnOrdering,
|
|
10
|
+
enablePagination, resetPageOnQueryChange, enableRowNumbers, selectionMode,
|
|
11
|
+
showSelectedBackground, cellSelection, renderDetails, overscan,
|
|
12
|
+
quickSearchMode), persist with dataKey/settingsKey slices, storageMode and
|
|
13
|
+
urlKey deep links, the returned api (table, ui, edit, features, labels,
|
|
14
|
+
resetSettings, scrollToRow), ui actions, and reading grid state with
|
|
15
|
+
useSelector over table.store, since table.getState() does not exist in v9.
|
|
16
|
+
Load when configuring the hook, looking up an option's default, persisting
|
|
17
|
+
layout or filters, or reacting to grid state from a parent.
|
|
14
18
|
metadata:
|
|
15
19
|
type: core
|
|
16
20
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.
|
|
21
|
+
library_version: '2.3.0'
|
|
18
22
|
sources:
|
|
19
23
|
- 'Jielga/react:packages/tmdatagrid/docs/use-tm-data-grid.md'
|
|
20
24
|
- 'Jielga/react:packages/tmdatagrid/docs/persistence.md'
|
|
@@ -28,7 +32,8 @@ Creates the table instance and the state used by the grid interface.
|
|
|
28
32
|
|
|
29
33
|
```tsx
|
|
30
34
|
const grid = useTMDataGrid<TData>(options);
|
|
31
|
-
// { table, ui, edit, features, labels,
|
|
35
|
+
// { table, ui, edit, features, labels, filters, exportOptions, renderDetails,
|
|
36
|
+
// renderDetailsEstHeight, overscan, resetSettings, scrollToRow }
|
|
32
37
|
```
|
|
33
38
|
|
|
34
39
|
The signature is
|
|
@@ -42,43 +47,77 @@ All TanStack `TableOptions` are supported and passed through unchanged, includin
|
|
|
42
47
|
flags and `rowCount`. The `features` option is supplied internally and cannot be
|
|
43
48
|
overridden.
|
|
44
49
|
|
|
45
|
-
`persist`, `
|
|
46
|
-
`
|
|
47
|
-
|
|
50
|
+
The grid defines these options itself: `persist`, `labels`, `editing`,
|
|
51
|
+
`filters`, `exportOptions`, `enableColumnOrdering`, `enablePagination`,
|
|
52
|
+
`resetPageOnQueryChange`, `enableRowNumbers`, `selectionMode`,
|
|
53
|
+
`showSelectedBackground`, `defaultHighlightedRowId`, `onHighlightedRowChange`,
|
|
54
|
+
`cellSelection`, `onFocusedCellChange`, `renderDetails`,
|
|
55
|
+
`renderDetailsEstHeight`, `detailsColumnPosition`, `overscan`,
|
|
56
|
+
`quickSearchMode` and `enableMatchHighlighting`. The table below marks them
|
|
57
|
+
"Defined by the grid"; every other row is a TanStack option, listed where the
|
|
58
|
+
grid changes its default or where it decides something visible. A TanStack
|
|
59
|
+
default the grid changes reads, for example, `"remove"` (grid default;
|
|
60
|
+
TanStack: `"reorder"`). A TanStack option that is not listed passes through
|
|
61
|
+
with TanStack's own default.
|
|
48
62
|
|
|
49
63
|
| Option | Type | Default | Description |
|
|
50
64
|
| --- | --- | --- | --- |
|
|
51
|
-
| `data` | `TData[]` | – | Row data.
|
|
52
|
-
| `columns` | `ColumnDef[]` | – | Created with `createTMDataGridColumnHelper`. |
|
|
53
|
-
| `getRowId` | `(row, index) => string` | Row index |
|
|
65
|
+
| `data` | `TData[]` | – | Row data. When the reference changes the table reprocesses the row models, so keep it stable with `useMemo`; a fresh `[]` for the empty case belongs in a module-scope constant. |
|
|
66
|
+
| `columns` | `ColumnDef[]` | – | Created with `createTMDataGridColumnHelper`. Define them outside the component. |
|
|
67
|
+
| `getRowId` | `(row, index, parent?) => string` | Row index | The id that row selection, expansion, row pinning and editing are keyed by. The index points at another record after a sort or a refetch, so return the entity id. Required once `editing` is set. |
|
|
68
|
+
| `defaultColumn` | `Partial<ColumnDef>` | `{ minSize: 80 }` (grid default; TanStack: `{}`) | Defaults merged under every column. The grid sets `filterFn: "tmDataGrid"` and clears `aggregationFn` and `aggregatedCell` on every column, so those three keys have no effect here; a value on the column wins. |
|
|
54
69
|
| `enableRowSelection` | `boolean \| (row) => boolean` | `true` | `false` removes row selection and its checkbox column. |
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
70
|
+
| `enableMultiRowSelection` | `boolean \| (row) => boolean` | `true` | `false` removes the select-all checkbox and limits the selection to one row. |
|
|
71
|
+
| `selectionMode` | `"checkbox" \| "row" \| "checkboxAndHighlight" \| "highlight"` | `"checkbox"` | What selecting looks like and what a bare row click does. Defined by the grid, see the `rows` skill. |
|
|
72
|
+
| `showSelectedBackground` | `boolean` | On for `"row"`, off for `"checkbox"` | Determines whether selected rows take a background tint, coloured by `--dg-row-selected-bg`. Defined by the grid, see the `rows` skill. |
|
|
73
|
+
| `defaultHighlightedRowId` | `string \| null` | – | Row highlighted on mount, under a mode with a highlight. Read once, like `initialState`. Defined by the grid. |
|
|
74
|
+
| `onHighlightedRowChange` | `(rowId: string \| null) => void` | – | Follows the highlighted row, from clicks and from `ui.actions.setHighlightedRow`. Defined by the grid. |
|
|
75
|
+
| `enableSorting` | `boolean` | `true` | `false` removes the sort indicator, the sort menu items and click-to-sort. Also a column option. See the `columns` skill. |
|
|
76
|
+
| `enableMultiSort` | `boolean` | `true` for accessor columns | Shift+click on a header adds a sort instead of replacing it. Also a column option. See the `columns` skill. |
|
|
77
|
+
| `maxMultiSortColCount` | `number` | `Number.MAX_SAFE_INTEGER` | How many sorts are kept; past it the oldest is dropped. |
|
|
78
|
+
| `enableColumnFilters` | `boolean` | `true` | `false` removes the filter menu item, the `FilterButton`, the header filter row and the panel's column list, and disables the panel's "Add filter" button. Filters already in state still apply. See the `filtering` skill. |
|
|
79
|
+
| `enableGlobalFilter` | `boolean` | `true` | `false` removes `Search`. Also a column option, removing one column from the search. See the `filtering` skill. |
|
|
80
|
+
| `globalFilterFn` | filter fn \| name | `"tmDataGridFuzzy"`; `"includesString"` under `quickSearchMode: "contains"` (grid default; TanStack: `"auto"`) | How the quick search matches. An explicit value also switches the fuzzy rank ordering off. |
|
|
81
|
+
| `enableHiding` | `boolean` | `true` | `false` removes the Hide column item, the Manage columns submenu and the column chooser. Also a column option. |
|
|
60
82
|
| `enableColumnPinning` | `boolean` | `true` | Enables pinning for the table. `false` removes the pin menu items; the generated lanes stay pinned. |
|
|
61
|
-
| `enableColumnResizing` | `boolean` | `true` |
|
|
62
|
-
| `enableColumnOrdering` | `boolean` | `true` | Enables header dragging and the move menu items. Defined by the grid. |
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
83
|
+
| `enableColumnResizing` | `boolean` | `true` | With `false` the divider is displayed but cannot be dragged, and double-click autosize and the Autosize menu item are removed. Also a column option, as `enableResizing`. |
|
|
84
|
+
| `enableColumnOrdering` | `boolean` | `true` | Enables header dragging and the move menu items. Defined by the grid, see the `columns` skill. |
|
|
85
|
+
| `enableGrouping` | `boolean` | `true`; `false` under `manualPagination` (grid default; TanStack: `true`) | `false` removes the Group by and Ungroup menu items and the tree lane. Also a column option. See the `grouping` skill. |
|
|
86
|
+
| `groupedColumnMode` | `"remove" \| "reorder" \| false` | `"remove"` (grid default; TanStack: `"reorder"`) | What happens to a column the rows are grouped by: `"remove"` takes it out of the grid, `"reorder"` moves it to the front, `false` leaves it in place. See the `grouping` skill. |
|
|
87
|
+
| `enablePagination` | `boolean` | `false` | Enables client-side paging and the `Footer` pager. Implied by `manualPagination`. Defined by the grid, see the `data` skill. |
|
|
88
|
+
| `resetPageOnQueryChange` | `boolean` | `true` | Returns to the first page when a column filter, the quick search, the sorting or the grouping changes through the table. A `data` change does not count. Defined by the grid, see the `data` skill. |
|
|
89
|
+
| `autoResetPageIndex` | `boolean` | `false` (grid default; TanStack: `!manualPagination`) | TanStack's return to the first page on every `data` change. Off, since `resetPageOnQueryChange` covers the query. |
|
|
90
|
+
| `autoResetExpanded` | `boolean` | `false` (grid default; TanStack: `!manualExpanding`) | TanStack's collapse of every expanded row on a `data` change. Off, so open details panels survive a refetch. |
|
|
91
|
+
| `manualFiltering`, `manualSorting`, `manualGrouping`, `manualPagination` | `boolean` | `false` | The server does that step and the grid shows `data` as given. `manualPagination` also implies `enablePagination`. See the `server-side` skill. |
|
|
92
|
+
| `rowCount` | `number` | – | The total row count under `manualPagination`, read by the pager. |
|
|
93
|
+
| `enableRowNumbers` | `boolean` | `false` | The row-number gutter, outermost left. Defined by the grid, see the `rows` skill. |
|
|
94
|
+
| `enableRowPinning` | `boolean \| (row) => boolean` | `false` (grid default; TanStack: `true`) | Rows can be pinned to sticky edge blocks with `row.pin()`. See the `rows` skill. |
|
|
95
|
+
| `quickSearchMode` | `"fuzzy" \| "contains"` | `"fuzzy"` | How `Search` matches. Fuzzy forgives typos and orders unsorted results by match quality; `"contains"` is plain substring matching. Defined by the grid, see the `filtering` skill. |
|
|
96
|
+
| `enableMatchHighlighting` | `boolean` | `false` | Cells mark the matched text while a contains-family filter or the quick search is active. Defined by the grid, see the `filtering` skill. |
|
|
97
|
+
| `renderDetails` | `({ row, table }) => ReactNode` | – | Panel rendered under an expanded row, spanning every column. Setting it turns row details on and adds the pinned chevron lane. Defined by the grid, see the `rows` skill. |
|
|
98
|
+
| `renderDetailsEstHeight` | `number` | `160` | What the virtualizer assumes for a panel it has not measured yet. Panels are measured once mounted, so an approximation is enough. Defined by the grid. |
|
|
99
|
+
| `detailsColumnPosition` | `"left" \| "right"` | `"left"` | Which edge the details lane is pinned to. `"right"` places it after every column, inside the edit lane. Defined by the grid, see the `rows` skill. |
|
|
100
|
+
| `cellSelection` | `"none" \| "single" \| "range"` | `"none"` | Cell cursor and, under `"range"`, a selectable rectangle with Ctrl+C and CSV export. Defined by the grid, see the `cell-selection` skill. |
|
|
101
|
+
| `onFocusedCellChange` | `(cell: TMDataGridCellPosition \| null) => void` | – | Called whenever the focused cell moves, by key, click or `setFocusedCell`. Defined by the grid. |
|
|
102
|
+
| `overscan` | `number` | `6` | Rows the virtualizer keeps mounted above and below the viewport. Raise it if fast scrolling flashes blank rows, lower it when rows are expensive to render. Defined by the grid. |
|
|
103
|
+
| `columnResizeMode` | `"onChange" \| "onEnd"` | `"onEnd"` | When a resize drag writes `columnSizing`. The grid paints a running drag itself either way; `"onChange"` also publishes a width on every pointer move, which re-renders the grid with each of them. |
|
|
104
|
+
| `initialState` | `Partial<TableState>` | – | Starting state, read once on mount. Merged over the grid defaults below. |
|
|
105
|
+
| `state` | `Partial<TableState>` | – | Controlled state. Each slice requires its `onXChange`. See below. |
|
|
106
|
+
| `atoms` | `Partial<Record<slice, Atom>>` | – | External atoms owning state slices. No callback required. Takes precedence over `state`. |
|
|
107
|
+
| `meta` | `TMDataGridTableMeta` | `{}` | Grid configuration. See below. |
|
|
108
|
+
| `filters` | `TMDataGridFiltersOptions` | `{ surface: "popup" }` | Where the filter controls go - a popup, a sidebar, the column headers, or nowhere. Defined by the grid, see the `filtering` skill. |
|
|
109
|
+
| `exportOptions` | `TMDataGridExportOptions` | `csvExcelFormat()`, `"export"`, headers on, `"visible"` columns | The export's `format`, `fileName`, `includeHeaders` and `columns`. Defined by the grid, see the `data` skill. |
|
|
110
|
+
| `editing` | `TMDataGridEditingOptions` | off | Turns editing on. `mode` picks what counts as a commit and `draft` whether edits collect in the draft store. Requires `getRowId`. Defined by the grid, see the `editing` skill. |
|
|
111
|
+
| `persist` | `TMDataGridPersistence` | – | State persistence. Defined by the grid, see below. |
|
|
112
|
+
| `labels` | `TMDataGridLabelsOverride` | English | Overrides for the grid's strings and `aria-label`s. Defined by the grid, see the `appearance` skill. |
|
|
74
113
|
|
|
75
114
|
### Default initial state
|
|
76
115
|
|
|
77
116
|
| Slice | Default |
|
|
78
117
|
| --- | --- |
|
|
79
|
-
| `pagination` | `{ pageIndex: 0, pageSize: 25 }
|
|
80
|
-
| `columnPinning.start` | The checkbox
|
|
81
|
-
| `
|
|
118
|
+
| `pagination` | `{ pageIndex: 0, pageSize: 25 }`. Inert until pagination is enabled |
|
|
119
|
+
| `columnPinning.start` | The row number, checkbox, tree and details columns, followed by any columns you provide. The details column moves to `end` under `detailsColumnPosition: "right"` |
|
|
120
|
+
| `columnPinning.end` | Any columns you provide, then the details column under `detailsColumnPosition: "right"`, then the edit column |
|
|
82
121
|
|
|
83
122
|
### Controlled state
|
|
84
123
|
|
|
@@ -113,22 +152,25 @@ const grid = useTMDataGrid({
|
|
|
113
152
|
|
|
114
153
|
| Field | Type | Default | Description |
|
|
115
154
|
| --- | --- | --- | --- |
|
|
116
|
-
| `loading` | `boolean` |
|
|
117
|
-
| `noResultsLabel` | `string` | `
|
|
155
|
+
| `loading` | `boolean` | `false` | While `true` and the view is empty, a loader replaces the empty state; `renderEmptyState` is not called. Rows on screen stay. See the `data` skill. |
|
|
156
|
+
| `noResultsLabel` | `string` | `labels.noResults` | The text shown when a filter or the search leaves no rows. |
|
|
118
157
|
| `rowHeight` | `number` | From `size` | Row height in px. Overrides the size scale. |
|
|
119
|
-
| `totalRowCount` | `number` | – | Unfiltered total used by `SummaryCount`.
|
|
158
|
+
| `totalRowCount` | `number` | – | Unfiltered total used by `SummaryCount`. Without it a `manualFiltering` or `manualPagination` grid shows the shown count alone. |
|
|
120
159
|
|
|
121
160
|
## persist
|
|
122
161
|
|
|
123
162
|
Restores table state on mount and writes it back on every change. State is split
|
|
124
|
-
across
|
|
125
|
-
stays valid indefinitely,
|
|
126
|
-
changes
|
|
163
|
+
across three keys because the groups have different lifetimes: column configuration
|
|
164
|
+
stays valid indefinitely, filters and pagination go stale as the data
|
|
165
|
+
changes, and a deep link has to travel with the page URL. Separate keys let one
|
|
166
|
+
group be cleared without touching the others.
|
|
127
167
|
|
|
128
168
|
| Field | Type | Default | Description |
|
|
129
169
|
| --- | --- | --- | --- |
|
|
130
170
|
| `dataKey` | `TMDataGridPersistKey<TMDataGridDataSlice>`: `string \| [string, DataSlice[]]` | – | Storage key for the data group. |
|
|
131
171
|
| `settingsKey` | `TMDataGridPersistKey<TMDataGridSettingsSlice>`: `string \| [string, SettingsSlice[]]` | – | Storage key for the settings group. |
|
|
172
|
+
| `urlKey` | `TMDataGridPersistKey<TMDataGridDataSlice>`: `string \| [string, DataSlice[]]` | – | Search param for data slices kept in the URL. See [Deep links](#deep-links). |
|
|
173
|
+
| `urlStorage` | `TMDataGridUrlStorage`: `{ get(name): string \| null; set(name, value: string \| null): void }` | `location.search` and `history.replaceState` | Reads and writes the URL param. |
|
|
132
174
|
| `storageMode` | `TMDataGridStorageMode`: `"localStorage" \| "sessionStorage"` | `"localStorage"` | Storage area. `"sessionStorage"` is per tab. |
|
|
133
175
|
| `serialize` | `(value) => string` | `JSON.stringify` | Serializes before storing. |
|
|
134
176
|
| `deserialize` | `(value: string) => unknown` | `JSON.parse` | Parses a stored payload. |
|
|
@@ -164,6 +206,7 @@ const persist = {
|
|
|
164
206
|
| Group | Available slices |
|
|
165
207
|
| --- | --- |
|
|
166
208
|
| `dataKey` | `columnFilters`, `globalFilter`, `sorting`, `pagination`, `expanded` |
|
|
209
|
+
| `urlKey` | The data slices. A slice listed here is taken out of `dataKey`. |
|
|
167
210
|
| `settingsKey` | `columnVisibility`, `columnSizing`, `columnOrder`, `columnPinning`, `grouping` |
|
|
168
211
|
|
|
169
212
|
`grouping` is a settings slice because it names columns rather than values, so it
|
|
@@ -188,38 +231,131 @@ dropped.
|
|
|
188
231
|
|
|
189
232
|
Source: `packages/tmdatagrid/docs/persistence.md` (Behaviour).
|
|
190
233
|
|
|
234
|
+
### Deep links
|
|
235
|
+
|
|
236
|
+
`urlKey` keeps data slices in one URL search param, so a link to the page opens
|
|
237
|
+
the grid on the same filters. The string is the param name; a bare key takes
|
|
238
|
+
every data slice, a tuple a subset, as with `dataKey`.
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
const persist = {
|
|
242
|
+
settingsKey: "employees.settings",
|
|
243
|
+
// Sorting is remembered per browser; the filters travel in the link.
|
|
244
|
+
dataKey: ["employees.data", ["sorting"]],
|
|
245
|
+
urlKey: ["employees", ["columnFilters", "globalFilter"]],
|
|
246
|
+
} satisfies TMDataGridPersistence;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- A slice lives in one group. A slice listed under `urlKey` is taken out of
|
|
250
|
+
`dataKey`: a bare `dataKey` hands it over silently, while a `dataKey` tuple
|
|
251
|
+
that names the slice explicitly is reported with `console.error` on mount.
|
|
252
|
+
- The param carries only the slices whose value differs from the initial state
|
|
253
|
+
(`initialState` merged over the grid defaults), and only the column filters
|
|
254
|
+
that are active. When nothing differs, the param is removed. A link without the param, or without a slice, opens on the
|
|
255
|
+
initial state; a URL slice never falls back to storage.
|
|
256
|
+
- Writes use `history.replaceState`, never `pushState`. Only the grid's own
|
|
257
|
+
param changes; the path, the hash, other params and `history.state` are kept.
|
|
258
|
+
Two grids on one page use two keys.
|
|
259
|
+
- The URL payload has no version stamp, so a bookmarked link survives a
|
|
260
|
+
release. The shape guards and the realignment against existing columns apply
|
|
261
|
+
as for storage, and `serialize` / `deserialize` apply to the URL payload too.
|
|
262
|
+
- `meta.filter.url: false` on a column keeps its filter out of the param, on
|
|
263
|
+
read and on write. The column's filter is then not persisted anywhere, and a
|
|
264
|
+
filter on it in `initialState` still applies when a link is opened. The
|
|
265
|
+
flag has no effect unless `urlKey` carries `columnFilters`.
|
|
266
|
+
- `urlStorage` replaces the default read and write of the param, for a router
|
|
267
|
+
that owns the URL. `get` returns the param's string or `null`; `set` receives
|
|
268
|
+
the string, or `null` to remove the param. `set` is only called when the
|
|
269
|
+
value changed. Keep `persist` referentially stable here too: react-router
|
|
270
|
+
rebuilds `setSearchParams` on every URL change, so read it through a ref.
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
// react-router: let the router do the replace.
|
|
274
|
+
const [, setSearchParams] = useSearchParams();
|
|
275
|
+
const setSearchParamsRef = useRef(setSearchParams);
|
|
276
|
+
setSearchParamsRef.current = setSearchParams;
|
|
277
|
+
|
|
278
|
+
const persist = useMemo(
|
|
279
|
+
() =>
|
|
280
|
+
({
|
|
281
|
+
urlKey: ["employees", ["columnFilters"]],
|
|
282
|
+
urlStorage: {
|
|
283
|
+
get: (name) => new URLSearchParams(window.location.search).get(name),
|
|
284
|
+
set: (name, value) =>
|
|
285
|
+
setSearchParamsRef.current(
|
|
286
|
+
(prev) => {
|
|
287
|
+
if (value === null) prev.delete(name);
|
|
288
|
+
else prev.set(name, value);
|
|
289
|
+
return prev;
|
|
290
|
+
},
|
|
291
|
+
{ replace: true },
|
|
292
|
+
),
|
|
293
|
+
},
|
|
294
|
+
}) satisfies TMDataGridPersistence,
|
|
295
|
+
[],
|
|
296
|
+
);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Source: `packages/tmdatagrid/docs/persistence.md` (Deep links).
|
|
300
|
+
|
|
191
301
|
## Return value
|
|
192
302
|
|
|
193
303
|
| Field | Type | Description |
|
|
194
304
|
| --- | --- | --- |
|
|
195
305
|
| `table` | `Table<TMDataGridFeatures, TData>` | The TanStack table instance. |
|
|
196
306
|
| `ui` | `TMDataGridUiStore`: `Store<TMDataGridUiState, TMDataGridUiActions>` | State of the filter and column panels. |
|
|
307
|
+
| `filters` | `TMDataGridFiltersSettings` | The `filters` option with its defaults filled in. |
|
|
308
|
+
| `exportOptions` | `TMDataGridExportSettings` | The `exportOptions` option with its defaults filled in. |
|
|
197
309
|
| `edit` | `TMDataGridEditApi` | The edit engine, inert until `editing` is set. See the `editing` skill. |
|
|
198
310
|
| `features` | `TMDataGridFeatureFlags` | Table-level feature switches, re-read on each render. |
|
|
199
311
|
| `labels` | `TMDataGridLabels` | The resolved label set, overrides merged over English. |
|
|
312
|
+
| `renderDetails` | `TMDataGridDetailsRenderer<TData> \| undefined` | The detail renderer, passed through for `TMDataGrid.Table` to call. |
|
|
313
|
+
| `renderDetailsEstHeight` | `number` | The estimate, resolved to its default when the option was not set. |
|
|
314
|
+
| `overscan` | `number` | The overscan, resolved to its default when the option was not set. |
|
|
200
315
|
| `resetSettings` | `() => void` | Puts the settings state back to a clean first visit. |
|
|
201
316
|
| `scrollToRow` | `({ rowId, align? }) => boolean` | Scrolls a row into view through the virtualizer. `false` when the row is not in the current view. |
|
|
202
317
|
|
|
203
|
-
|
|
204
|
-
|
|
318
|
+
`table` is `TMDataGridTable<TData>`, TanStack's core `Table` type. It has no
|
|
319
|
+
`getState()` (that method does not exist in TanStack Table v9) and `table.state`
|
|
320
|
+
is not part of the type; state lives in `table.store`.
|
|
321
|
+
|
|
322
|
+
### Reading state
|
|
323
|
+
|
|
324
|
+
`table.store` and `ui` are TanStack stores. Read a slice with `useSelector`
|
|
325
|
+
from `@tanstack/react-store`, which subscribes the component to what the
|
|
326
|
+
selector returns:
|
|
205
327
|
|
|
206
328
|
```tsx
|
|
207
329
|
import { useSelector } from "@tanstack/react-store";
|
|
208
330
|
|
|
209
331
|
const selectedCount = useSelector(
|
|
210
332
|
grid.table.store,
|
|
211
|
-
(
|
|
333
|
+
() => grid.table.getSelectedRowModel().rows.length,
|
|
212
334
|
);
|
|
213
|
-
|
|
335
|
+
const isGrouped = useSelector(grid.table.store, (state) =>
|
|
336
|
+
state.grouping.includes("department"),
|
|
337
|
+
);
|
|
338
|
+
const pageIndex = useSelector(grid.table.store, (state) => state.pagination.pageIndex);
|
|
214
339
|
const filterPanelOpen = useSelector(grid.ui, (state) => state.filterPanelOpen);
|
|
215
340
|
```
|
|
216
341
|
|
|
342
|
+
`grid.table.store.state` is a plain read and does not subscribe the component.
|
|
343
|
+
A bare read is stale in any component that does not call `useTMDataGrid`
|
|
344
|
+
itself, and under the React Compiler also in the one that does; `useSelector`
|
|
345
|
+
subscribes either way. A table or column getter such as
|
|
346
|
+
`table.getSelectedRowModel()` or `column.getIsGrouped()` is a plain read as
|
|
347
|
+
well; call it inside a selector, as above. `getTMDataGridPaginationApi(table)`
|
|
348
|
+
is a snapshot too; wrap it in `useSelector` or use the `state` the Footer's
|
|
349
|
+
`renderPagination` slot receives.
|
|
350
|
+
|
|
217
351
|
### ui actions
|
|
218
352
|
|
|
219
353
|
| Action | Signature |
|
|
220
354
|
| --- | --- |
|
|
221
355
|
| `openFilterPanel` | `(columnId?: string \| null) => void` |
|
|
222
356
|
| `closeFilterPanel` | `() => void` |
|
|
357
|
+
| `openExportPicker` | `(request: TMDataGridExportPickerRequest) => void` |
|
|
358
|
+
| `closeExportPicker` | `() => void` |
|
|
223
359
|
| `focusPanelFilter` | `(columnId: string \| null) => void` |
|
|
224
360
|
| `focusHeaderFilter` | `(columnId: string \| null) => void` |
|
|
225
361
|
| `startColumnDrag` | `(columnId: string) => void` |
|
|
@@ -232,29 +368,41 @@ const filterPanelOpen = useSelector(grid.ui, (state) => state.filterPanelOpen);
|
|
|
232
368
|
The last two move the cell cursor and the selected rectangle under
|
|
233
369
|
`cellSelection`; DOM focus follows `focusedCell` and scrolls its row into view.
|
|
234
370
|
|
|
235
|
-
`startColumnDrag` / `endColumnDrag` are called by the header cells while a
|
|
236
|
-
`ui.draggedColumnId` holds the column being
|
|
237
|
-
unreadable until the drop.
|
|
371
|
+
`startColumnDrag` / `endColumnDrag` are called by the header cells while a
|
|
372
|
+
column is being dragged, and `ui.state.draggedColumnId` holds the column being
|
|
373
|
+
moved - browsers keep `dataTransfer` unreadable until the drop.
|
|
238
374
|
|
|
239
375
|
`openColumnFilter(grid, columnId)` combines the two steps the column menu uses:
|
|
240
|
-
add an empty filter row for the column if none exists, then open the panel
|
|
376
|
+
add an empty filter row for the column if none exists, then open the panel on
|
|
377
|
+
it, or focus the column's header control under `filters.inHeader`.
|
|
241
378
|
|
|
242
379
|
## Common mistakes
|
|
243
380
|
|
|
244
381
|
### Reading store state during render
|
|
245
382
|
|
|
246
|
-
`grid.table.store.state` is a plain read - it does not subscribe the component
|
|
247
|
-
|
|
383
|
+
`grid.table.store.state` is a plain read - it does not subscribe the component.
|
|
384
|
+
A bare read is stale in any component that does not call `useTMDataGrid`
|
|
385
|
+
itself, and under the React Compiler also in the one that does; `useSelector`
|
|
386
|
+
subscribes either way. The same holds for every table and column getter called
|
|
387
|
+
outside a selector.
|
|
248
388
|
|
|
249
389
|
```tsx
|
|
250
390
|
// Wrong - never re-renders when the selection changes.
|
|
251
391
|
const count = Object.keys(grid.table.store.state.rowSelection).length;
|
|
392
|
+
// Wrong - a getter outside a selector is a snapshot.
|
|
393
|
+
const grouped = grid.table.getColumn("department")!.getIsGrouped();
|
|
394
|
+
// Wrong - `getState()` does not exist in TanStack Table v9.
|
|
395
|
+
const sorting = grid.table.getState().sorting;
|
|
252
396
|
|
|
253
397
|
// Right.
|
|
254
398
|
const count = useSelector(
|
|
255
399
|
grid.table.store,
|
|
256
400
|
(state) => Object.keys(state.rowSelection).length,
|
|
257
401
|
);
|
|
402
|
+
const grouped = useSelector(grid.table.store, (state) =>
|
|
403
|
+
state.grouping.includes("department"),
|
|
404
|
+
);
|
|
405
|
+
const sorting = useSelector(grid.table.store, (state) => state.sorting);
|
|
258
406
|
```
|
|
259
407
|
|
|
260
408
|
### persist object built during render
|
|
@@ -273,4 +421,7 @@ Include a tenant or user identifier in the key.
|
|
|
273
421
|
|
|
274
422
|
A new array identity on each render re-runs the row model. `data={rows ?? []}`
|
|
275
423
|
allocates a fresh `[]` every render when `rows` is undefined - hoist the empty
|
|
276
|
-
array to a module-scope constant or `useMemo` the expression.
|
|
424
|
+
array to a module-scope constant or `useMemo` the expression. Under `editing`
|
|
425
|
+
the cost is higher: every new `data` identity drops the open drafts of the rows
|
|
426
|
+
it no longer contains, so a transient `[]` while refetching discards every
|
|
427
|
+
draft.
|
package/skills/rows/SKILL.md
CHANGED
|
@@ -8,16 +8,16 @@ description: >
|
|
|
8
8
|
reading a selection through the table store, onRowClick / onCellClick /
|
|
9
9
|
onCellDoubleClick / onCellContextMenu, the renderRowContextMenu slot with its
|
|
10
10
|
internalItems handback, renderColumnMenuItems and rowContextMenuProps, per-row
|
|
11
|
-
styling with rowStyle, rowClassName, striped and the
|
|
12
|
-
details panel through renderDetails,
|
|
13
|
-
DETAILS_COLUMN_ID, row pinning with enableRowPinning
|
|
14
|
-
row-number gutter through enableRowNumbers. Load when
|
|
15
|
-
to a click, opening a detail panel, colouring rows by
|
|
16
|
-
to an edge, or numbering them.
|
|
11
|
+
styling with rowStyle, rowClassName, striped, highlightOnHover and the
|
|
12
|
+
--row-bg rule, the row details panel through renderDetails,
|
|
13
|
+
detailsColumnPosition and DETAILS_COLUMN_ID, row pinning with enableRowPinning
|
|
14
|
+
and row.pin, and the row-number gutter through enableRowNumbers. Load when
|
|
15
|
+
selecting rows, reacting to a click, opening a detail panel, colouring rows by
|
|
16
|
+
their data, pinning rows to an edge, or numbering them.
|
|
17
17
|
metadata:
|
|
18
18
|
type: core
|
|
19
19
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '2.
|
|
20
|
+
library_version: '2.3.0'
|
|
21
21
|
sources:
|
|
22
22
|
- 'Jielga/react:packages/tmdatagrid/docs/row-selection.md'
|
|
23
23
|
- 'Jielga/react:packages/tmdatagrid/docs/row-interaction.md'
|
|
@@ -37,8 +37,8 @@ cell cursors and ranges are `cell-selection`.
|
|
|
37
37
|
## Selection
|
|
38
38
|
|
|
39
39
|
`selectionMode` sets what selecting looks like **and** what a bare row click
|
|
40
|
-
does.
|
|
41
|
-
|
|
40
|
+
does. A bare click either toggles the selection or moves the highlight, never
|
|
41
|
+
both.
|
|
42
42
|
|
|
43
43
|
| Mode | Checkbox column | Row click |
|
|
44
44
|
| --- | --- | --- |
|
|
@@ -51,8 +51,8 @@ multi-selection or move a highlight, but not both.
|
|
|
51
51
|
const grid = useTMDataGrid({ data, columns, selectionMode: "row" });
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
The first two write to TanStack's `rowSelection`, so
|
|
55
|
-
`
|
|
54
|
+
The first two write to TanStack's `rowSelection`, so `getSelectedRowModel()`
|
|
55
|
+
and `onRowSelectionChange` behave the same either way. Under
|
|
56
56
|
`"row"` the click follows desktop-list conventions: a plain click
|
|
57
57
|
makes the selection this row, Ctrl/Cmd toggles it and leaves the rest, Shift
|
|
58
58
|
selects the range from the anchor, Ctrl+Shift adds that range. Rows are
|
|
@@ -61,7 +61,7 @@ focusable in this mode, and Space or Enter toggles the focused row.
|
|
|
61
61
|
`enableRowSelection: false` removes the checkbox column and row-click selection
|
|
62
62
|
in any mode.
|
|
63
63
|
|
|
64
|
-
### The
|
|
64
|
+
### The highlighted row
|
|
65
65
|
|
|
66
66
|
The highlight is state of its own, not a slice of `rowSelection`. That is what
|
|
67
67
|
lets `"checkboxAndHighlight"` run both at once: tick rows for a bulk action,
|
|
@@ -81,8 +81,9 @@ const grid = useTMDataGrid({
|
|
|
81
81
|
### Reading a selection
|
|
82
82
|
|
|
83
83
|
Read it through the table store rather than by calling the method directly.
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
A bare read is stale in any component that does not call `useTMDataGrid`
|
|
85
|
+
itself, and under the React Compiler also in the one that does; `useSelector`
|
|
86
|
+
subscribes either way.
|
|
86
87
|
|
|
87
88
|
```tsx
|
|
88
89
|
import { useSelector } from "@tanstack/react-store";
|
|
@@ -160,9 +161,10 @@ the row with `data-context-menu` while its menu is open, so an action meant for
|
|
|
160
161
|
a multi-selection reads `table` and falls back to the clicked row.
|
|
161
162
|
|
|
162
163
|
Under `cellSelection: "range"` the grid has its own items for this menu: copy,
|
|
163
|
-
export and include headers. `internalItems`
|
|
164
|
-
over the composition
|
|
165
|
-
it and the grid
|
|
164
|
+
export and include headers. `internalItems` holds those items, and **using it
|
|
165
|
+
takes over the composition**. Place it, and the menu is exactly what you
|
|
166
|
+
returned. Omit it, and the grid renders its items above a divider and yours
|
|
167
|
+
below.
|
|
166
168
|
|
|
167
169
|
```tsx
|
|
168
170
|
renderRowContextMenu={({ row, internalItems }) => (
|
|
@@ -211,8 +213,15 @@ them and they stop showing. `striped` follows position in the view, so it
|
|
|
211
213
|
survives sorting and filtering rather than sticking to records, and pinned rows
|
|
212
214
|
are not striped.
|
|
213
215
|
|
|
216
|
+
The hover tint follows the gestures a row answers: a row takes it under
|
|
217
|
+
`selectionMode: "row"` or a highlight mode, `onRowClick` or
|
|
218
|
+
`renderRowContextMenu`, and a group row never does. A grid with none of those
|
|
219
|
+
shows no hover tint. `highlightOnHover` on the Table overrides that: `true`
|
|
220
|
+
tints every row, `false` none.
|
|
221
|
+
|
|
214
222
|
Rows carry `data-selected`, `data-selected-bg`, `data-highlighted`,
|
|
215
|
-
`data-grouped`, `data-depth`, `data-context-menu
|
|
223
|
+
`data-grouped`, `data-depth`, `data-context-menu`, `data-highlight-on-hover`
|
|
224
|
+
and `data-row-id`, so a
|
|
216
225
|
stylesheet can target any of it without a callback. The boolean attributes are
|
|
217
226
|
present, with the value `"true"`, only while they apply - `data-grouped` only
|
|
218
227
|
on group rows - so `[data-grouped]` and `[data-grouped="true"]` are
|
|
@@ -232,20 +241,31 @@ const grid = useTMDataGrid({
|
|
|
232
241
|
});
|
|
233
242
|
```
|
|
234
243
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
The panel opens below the row's cells, which stay where they are; there is no
|
|
245
|
+
mode in which a panel replaces the row. Each panel is measured, so heights need
|
|
246
|
+
not be uniform. `renderDetailsEstHeight` (default `160`) is what the virtualizer
|
|
247
|
+
assumes for one it has not measured yet; set it to the typical panel height.
|
|
248
|
+
`DETAILS_COLUMN_ID` is a generated chevron lane. It is pinned left, after the
|
|
249
|
+
checkbox and tree lanes; under `detailsColumnPosition: "right"` it is pinned
|
|
250
|
+
right, inside the edit lane. It cannot be hidden, moved, resized or unpinned.
|
|
251
|
+
Its header opens and closes every panel.
|
|
241
252
|
|
|
242
253
|
Which rows are open is TanStack's own `expanded` state, so anything can open
|
|
243
254
|
one - `row.toggleExpanded()` from a menu item, `initialState.expanded`,
|
|
244
255
|
`table.toggleAllRowsExpanded()` - and it persists as a `data` slice. The panel
|
|
245
256
|
is a cell spanning the row, not a row: `aria-rowcount` still counts records, and
|
|
246
|
-
a click inside it does not select the row underneath.
|
|
257
|
+
a click inside it does not select the row underneath. It has no padding: content
|
|
258
|
+
fills the row edge to edge, and content that should line up with the cell text
|
|
259
|
+
pads itself with `padding: var(--dg-padding)`. Group rows have no panel;
|
|
247
260
|
expanding one opens its children.
|
|
248
261
|
|
|
262
|
+
Rows are virtualized. A row that leaves the viewport and the `overscan`
|
|
263
|
+
unmounts, and its panel unmounts with it, so state held inside the panel is
|
|
264
|
+
lost; keep such state outside the grid, keyed by `row.id`. `renderDetails` runs
|
|
265
|
+
on every render of the body, so give a heavy panel a `memo` component and pass
|
|
266
|
+
it `row.original`, not the row. Key a stateful child by `row.id`; that is stable
|
|
267
|
+
only when `getRowId` returns the entity id.
|
|
268
|
+
|
|
249
269
|
## Pinning and numbering
|
|
250
270
|
|
|
251
271
|
Two independent lanes at the edges.
|
|
@@ -264,7 +284,7 @@ There is no built-in pin gesture and no pin icon. Build one from TanStack's own
|
|
|
264
284
|
either as a display column whose cell is a pin button or in the row context
|
|
265
285
|
menu. Read the pinned state through a subscription
|
|
266
286
|
(`useSelector(row.table.store, () => row.getIsPinned())`) rather than calling it
|
|
267
|
-
in a component body
|
|
287
|
+
in a component body. The `row` identity survives a pin, so the React Compiler
|
|
268
288
|
would cache the call and the icon would never change. Whichever control you
|
|
269
289
|
build, include unpin in it. Pinned rows leave the scrolling order and render in
|
|
270
290
|
sticky blocks: top under the header, bottom above the summary row.
|
|
@@ -300,13 +320,13 @@ Correct:
|
|
|
300
320
|
rowStyle={() => ({ "--row-bg": "pink" })}
|
|
301
321
|
```
|
|
302
322
|
|
|
303
|
-
Source: `packages/tmdatagrid/docs/row-styling.md` (Set
|
|
323
|
+
Source: `packages/tmdatagrid/docs/row-styling.md` (Set the row background).
|
|
304
324
|
|
|
305
325
|
### CRITICAL Reading the selection without subscribing
|
|
306
326
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
then never changes.
|
|
327
|
+
A bare read is stale in any component that does not call `useTMDataGrid`
|
|
328
|
+
itself, and under the React Compiler also in the one that does; `useSelector`
|
|
329
|
+
subscribes either way. A stale count renders once and then never changes.
|
|
310
330
|
|
|
311
331
|
Wrong:
|
|
312
332
|
|
|
@@ -327,8 +347,8 @@ Source: `packages/tmdatagrid/docs/row-selection.md` (Acting on a selection).
|
|
|
327
347
|
### HIGH Looking for the highlight in `rowSelection`
|
|
328
348
|
|
|
329
349
|
The highlight is separate state, which is what lets `"checkboxAndHighlight"`
|
|
330
|
-
run both at once. It never appears in `rowSelection
|
|
331
|
-
|
|
350
|
+
run both at once. It never appears in `rowSelection` or
|
|
351
|
+
`getSelectedRowModel()`.
|
|
332
352
|
|
|
333
353
|
Wrong:
|
|
334
354
|
|
|
@@ -343,7 +363,7 @@ Correct:
|
|
|
343
363
|
const current = useSelector(grid.ui, (state) => state.highlightedRowId);
|
|
344
364
|
```
|
|
345
365
|
|
|
346
|
-
Source: `packages/tmdatagrid/docs/row-selection.md` (The
|
|
366
|
+
Source: `packages/tmdatagrid/docs/row-selection.md` (The highlighted row).
|
|
347
367
|
|
|
348
368
|
### HIGH Expecting a click handler to replace the built-in behaviour
|
|
349
369
|
|
|
@@ -382,18 +402,29 @@ written as though every row reaches it silently skips them.
|
|
|
382
402
|
|
|
383
403
|
Source: `packages/tmdatagrid/docs/row-interaction.md`, `packages/tmdatagrid/docs/row-pinning.md`.
|
|
384
404
|
|
|
385
|
-
### MEDIUM
|
|
405
|
+
### MEDIUM Keeping state inside a details panel
|
|
386
406
|
|
|
387
|
-
|
|
388
|
-
|
|
407
|
+
A panel unmounts with its row when the row leaves the virtual window, so a form
|
|
408
|
+
or an accordion inside `renderDetails` loses its state on a long scroll and
|
|
409
|
+
comes back reset.
|
|
389
410
|
|
|
390
|
-
Correct:
|
|
411
|
+
Correct: hold the state outside the grid, keyed by `row.id`, and pass it in.
|
|
391
412
|
|
|
392
413
|
```tsx
|
|
393
|
-
|
|
414
|
+
const [drafts, setDrafts] = useState<Record<string, Draft>>({});
|
|
415
|
+
useTMDataGrid({
|
|
416
|
+
data,
|
|
417
|
+
columns,
|
|
418
|
+
renderDetails: ({ row }) => (
|
|
419
|
+
<DetailsForm draft={drafts[row.id]} onChange={(d) => setDrafts((all) => ({ ...all, [row.id]: d }))} />
|
|
420
|
+
),
|
|
421
|
+
});
|
|
394
422
|
```
|
|
395
423
|
|
|
396
|
-
|
|
424
|
+
`autoResetExpanded` is already `false` by default, so a refetch does not close
|
|
425
|
+
the panels; pass `autoResetExpanded: true` to get TanStack's collapse back.
|
|
426
|
+
|
|
427
|
+
Source: `packages/tmdatagrid/docs/row-details.md` (Panel behaviour).
|
|
397
428
|
|
|
398
429
|
### MEDIUM Expecting pinned rows to persist
|
|
399
430
|
|
|
@@ -15,7 +15,7 @@ description: >
|
|
|
15
15
|
metadata:
|
|
16
16
|
type: core
|
|
17
17
|
library: '@jielga/tmdatagrid'
|
|
18
|
-
library_version: '2.
|
|
18
|
+
library_version: '2.3.0'
|
|
19
19
|
sources:
|
|
20
20
|
- 'Jielga/react:packages/tmdatagrid/docs/server-side.md'
|
|
21
21
|
- 'Jielga/react:packages/tmdatagrid/docs/server-query.md'
|