@jielga/tmdatagrid 2.2.0 → 2.4.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 +302 -11
- package/dist/index.js +2338 -2097
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/columns.md +9 -1
- package/docs/components.md +1 -0
- package/docs/filtering.md +5 -1
- package/docs/grouping.md +1 -1
- package/docs/localization.md +3 -3
- package/docs/persistence.md +189 -2
- package/docs/row-details.md +5 -0
- package/docs/row-interaction.md +1 -1
- package/docs/row-styling.md +17 -0
- package/docs/typed-deep-links.md +169 -0
- package/docs/use-tm-data-grid.md +6 -2
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +1 -1
- package/skills/appearance/references/labels.md +3 -3
- package/skills/cell-selection/SKILL.md +1 -1
- package/skills/columns/SKILL.md +6 -3
- package/skills/columns/references/columns-api.md +1 -0
- package/skills/data/SKILL.md +1 -1
- package/skills/editing/SKILL.md +1 -1
- package/skills/filtering/SKILL.md +26 -13
- package/skills/getting-started/SKILL.md +2 -2
- package/skills/grouping/SKILL.md +2 -2
- package/skills/migrating-to-2/SKILL.md +1 -1
- package/skills/options/SKILL.md +105 -10
- package/skills/options/references/typed-links.md +134 -0
- package/skills/rows/SKILL.md +18 -9
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +1 -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/TMDataGridFilterPills.tsx +15 -1
- package/src/components/TMDataGridGroupColumn.tsx +19 -4
- package/src/components/TMDataGridHeaderFilterRow.tsx +7 -1
- package/src/components/TMDataGridTable.module.css +15 -6
- package/src/components/TMDataGridTable.tsx +23 -11
- package/src/components/sticky.module.css +27 -7
- package/src/core/filterControls.ts +8 -0
- package/src/core/filterOperators.ts +54 -18
- package/src/core/grouping.ts +11 -3
- package/src/core/labels.ts +4 -3
- package/src/core/persistence.ts +330 -26
- package/src/core/urlState.ts +439 -0
- package/src/core/valueText.ts +46 -0
- package/src/index.ts +14 -0
- package/src/useTMDataGrid.tsx +145 -21
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Typed links with TanStack Router
|
|
2
|
+
|
|
3
|
+
Links into a grid, built from the grid's own columns and checked by both the grid's types and the router's.
|
|
4
|
+
The grid's URL param lives in the router's search, and the router encodes it.
|
|
5
|
+
|
|
6
|
+
```demo
|
|
7
|
+
file: recipes/RouterDeepLinks.tsx
|
|
8
|
+
hint: Go to Links and open one. Change a filter or the search, and watch the address follow.
|
|
9
|
+
extraSources: data/employeeLinks.tsx, data/EmployeeLinksGrid.tsx
|
|
10
|
+
height: 640
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Define the links from the columns
|
|
14
|
+
|
|
15
|
+
Build the columns with `createTMDataGridColumnHelper`, and pass the same array to `createTMDataGridUrlState`:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
export const employeesLink = createTMDataGridUrlState({
|
|
19
|
+
key: "emp",
|
|
20
|
+
columns: employeeLinkColumns,
|
|
21
|
+
slices: ["columnFilters", "globalFilter", "sorting"],
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The columns decide what a link may name.
|
|
26
|
+
In this demo, `location` sets `meta.filter.url: false`, so no link can filter it.
|
|
27
|
+
[Typed links](/docs/persistence#typed-links) lists the rules.
|
|
28
|
+
|
|
29
|
+
## Validate the route's search
|
|
30
|
+
|
|
31
|
+
Pass the definition's `validateSearch` to the route that renders the grid:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
const employeesRoute = createRoute({
|
|
35
|
+
getParentRoute: () => rootRoute,
|
|
36
|
+
path: "/employees",
|
|
37
|
+
validateSearch: employeesLink.validateSearch,
|
|
38
|
+
component: EmployeesPage,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A route with search params of its own spreads it into its validator:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
validateSearch: (search) => ({
|
|
46
|
+
...employeesLink.validateSearch(search),
|
|
47
|
+
tab: search.tab === "chart" ? "chart" : "table",
|
|
48
|
+
}),
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Keep the grid's param in the router
|
|
52
|
+
|
|
53
|
+
Pass the router's search to the definition's `persist`:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
const router = useRouter();
|
|
57
|
+
const persist = useMemo(
|
|
58
|
+
() =>
|
|
59
|
+
employeesLink.persist({
|
|
60
|
+
get: (name) => (router.state.location.search as Record<string, unknown>)[name],
|
|
61
|
+
set: (name, value) =>
|
|
62
|
+
void router.navigate({
|
|
63
|
+
to: ".",
|
|
64
|
+
search: (prev: Record<string, unknown>) => ({ ...prev, [name]: value }),
|
|
65
|
+
replace: true,
|
|
66
|
+
}),
|
|
67
|
+
}),
|
|
68
|
+
[router],
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The grid writes through `router.navigate`, so the router sees every change.
|
|
73
|
+
`replace: true` keeps a filter change out of the history, as the grid's own URL writes do.
|
|
74
|
+
With `createHashHistory()`, the param is written after the `#`, with the rest of the router's search.
|
|
75
|
+
|
|
76
|
+
## Link into the grid
|
|
77
|
+
|
|
78
|
+
Pass `search` to the link:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<Link
|
|
82
|
+
to="/employees"
|
|
83
|
+
search={employeesLink.search({
|
|
84
|
+
filters: {
|
|
85
|
+
salary: { operator: "between", value: [40_000, 60_000] },
|
|
86
|
+
status: { operator: "isAnyOf", value: ["Active"] },
|
|
87
|
+
},
|
|
88
|
+
sorting: [{ id: "salary", desc: true }],
|
|
89
|
+
})}
|
|
90
|
+
>
|
|
91
|
+
Active, highest salary first
|
|
92
|
+
</Link>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`search` fails to compile on a column the grid does not have, an operator the column does not offer, or a value of the wrong type.
|
|
96
|
+
The router then checks the result against the route's search.
|
|
97
|
+
|
|
98
|
+
## Readable URLs
|
|
99
|
+
|
|
100
|
+
By default, TanStack Router writes the param as JSON and escapes it with `URLSearchParams`, so every brace and quote is percent-escaped.
|
|
101
|
+
To write it readable, encode the values with [JSURL2](https://github.com/wmertens/jsurl2) and escape them with `encodeURIComponent`, which leaves JSURL2's `(`, `)`, `~` and `!` as they are:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { parse, stringify } from "jsurl2";
|
|
105
|
+
|
|
106
|
+
function toSearchValue(value: unknown): string {
|
|
107
|
+
if (typeof value === "object" && value !== null) return stringify(value);
|
|
108
|
+
if (typeof value !== "string") return String(value);
|
|
109
|
+
try {
|
|
110
|
+
parse(value);
|
|
111
|
+
return stringify(value);
|
|
112
|
+
} catch {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function stringifySearch(search: Record<string, unknown>): string {
|
|
118
|
+
const pairs = Object.entries(search).flatMap(([key, value]) =>
|
|
119
|
+
value === undefined
|
|
120
|
+
? []
|
|
121
|
+
: [`${encodeURIComponent(key)}=${encodeURIComponent(toSearchValue(value))}`],
|
|
122
|
+
);
|
|
123
|
+
return pairs.length === 0 ? "" : `?${pairs.join("&")}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const router = createRouter({
|
|
127
|
+
routeTree,
|
|
128
|
+
parseSearch: parseSearchWith(parse),
|
|
129
|
+
stringifySearch,
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
TanStack Router's own `stringifySearchWith(stringify)` encodes the values as JSURL2 too, but its `URLSearchParams` escapes JSURL2's delimiters again.
|
|
134
|
+
|
|
135
|
+
```demo
|
|
136
|
+
file: recipes/ReadableSearch.tsx
|
|
137
|
+
hint: The same links as above. Compare the two addresses.
|
|
138
|
+
extraSources: data/employeeLinks.tsx, data/EmployeeLinksGrid.tsx
|
|
139
|
+
height: 600
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The serializer applies to every route, and the grid is unaffected.
|
|
143
|
+
JSURL2's `parse` also reads JSON, so a link written before the switch still opens.
|
|
144
|
+
|
|
145
|
+
## Links without a router
|
|
146
|
+
|
|
147
|
+
Without a router, leave out the storage: the grid reads and writes the browser's URL.
|
|
148
|
+
Build a link with `searchParams`:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
const persist = employeesLink.persist();
|
|
152
|
+
|
|
153
|
+
const href = `/employees?${employeesLink.searchParams({
|
|
154
|
+
filters: { department: { operator: "isAnyOf", value: ["Design"] } },
|
|
155
|
+
})}`;
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```demo
|
|
159
|
+
file: recipes/PlainLinks.tsx
|
|
160
|
+
hint: Each link loads this page with the grid's param set.
|
|
161
|
+
extraSources: data/employeeLinks.tsx
|
|
162
|
+
height: 560
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Links to the page the grid is on
|
|
166
|
+
|
|
167
|
+
The grid reads its param once, when it mounts.
|
|
168
|
+
Note that a link to the route the grid is already on changes the URL but not the grid.
|
|
169
|
+
Link to such a route from another route, or remount the grid on the navigation.
|
package/docs/use-tm-data-grid.md
CHANGED
|
@@ -181,13 +181,16 @@ toolbar components can read the same strings as the built-in parts.
|
|
|
181
181
|
## persist
|
|
182
182
|
|
|
183
183
|
Restores table state on mount and writes it back on every change. State is
|
|
184
|
-
split across
|
|
185
|
-
filters, sorting and pagination
|
|
184
|
+
split across three keys: `settingsKey` for the column layout, `dataKey` for
|
|
185
|
+
filters, sorting and pagination, and `urlKey` for data slices kept in the URL
|
|
186
|
+
as a deep link. Each can be cleared without the others.
|
|
186
187
|
|
|
187
188
|
| Field | Type | Default | Description |
|
|
188
189
|
| --- | --- | --- | --- |
|
|
189
190
|
| `dataKey` | `string \| [string, DataSlice[]]` | – | Storage key for the data group. |
|
|
190
191
|
| `settingsKey` | `string \| [string, SettingsSlice[]]` | – | Storage key for the settings group. |
|
|
192
|
+
| `urlKey` | `string \| [string, DataSlice[]]` | – | Search param for data slices kept in the URL. See [Deep links](/docs/persistence#deep-links). |
|
|
193
|
+
| `urlStorage` | `TMDataGridUrlStorage` | `location.search` and `history.replaceState` | Reads and writes the URL param. |
|
|
191
194
|
| `storageMode` | `"localStorage" \| "sessionStorage"` | `"localStorage"` | Storage area. Use `"sessionStorage"` for per-tab state. |
|
|
192
195
|
| `serialize` | `(value) => string` | `JSON.stringify` | Serializes a payload before storing. |
|
|
193
196
|
| `deserialize` | `(value: string) => unknown` | `JSON.parse` | Parses a stored payload. |
|
|
@@ -222,6 +225,7 @@ const persist = {
|
|
|
222
225
|
| Group | Available slices |
|
|
223
226
|
| --- | --- |
|
|
224
227
|
| `dataKey` | `columnFilters`, `globalFilter`, `sorting`, `pagination`, `expanded` |
|
|
228
|
+
| `urlKey` | The data slices. A slice listed here is taken out of `dataKey`. |
|
|
225
229
|
| `settingsKey` | `columnVisibility`, `columnSizing`, `columnOrder`, `columnPinning`, `grouping` |
|
|
226
230
|
|
|
227
231
|
The exported `DATA_STATE_SLICES` and `SETTINGS_STATE_SLICES` arrays contain the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jielga/tmdatagrid",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "A React data grid built on TanStack Table v9 and Mantine - always virtualized, with resizable, reorderable, sortable, filterable, hideable and pinnable columns.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,8 +44,8 @@ The `labels` option takes a partial override, `TMDataGridLabelsOverride`, merged
|
|
|
44
44
|
| `filterFrom` | `From` | The lower bound of a `between` filter: its input, or its slider thumb. |
|
|
45
45
|
| `filterTo` | `To` | The upper bound of a `between` filter: its input, or its slider thumb. |
|
|
46
46
|
| `filterAll` | `All` | The no-filter segment of the tri-state filter. |
|
|
47
|
-
| `booleanTrue` | `Yes` | The `true` choice of a boolean filter value control and of the tri-state filter. |
|
|
48
|
-
| `booleanFalse` | `No` | The `false` choice of a boolean filter value control and of the tri-state filter. |
|
|
47
|
+
| `booleanTrue` | `Yes` | The `true` choice of a boolean filter value control and of the tri-state filter, and a `true` value on a filter pill and in a group row. |
|
|
48
|
+
| `booleanFalse` | `No` | The `false` choice of a boolean filter value control and of the tri-state filter, and a `false` value on a filter pill and in a group row. |
|
|
49
49
|
| `addFilter` | `Add filter` | Button in `TMDataGrid.FilterPanel`. |
|
|
50
50
|
| `clearAllFilters` | `Clear all` | Button in `TMDataGrid.FilterPanel` and in `TMDataGrid.FilterPills`. |
|
|
51
51
|
| `closeFilters` | `Close filters` | `aria-label` of the close button of the filter popup and sidebar. |
|
|
@@ -67,7 +67,7 @@ The `labels` option takes a partial override, `TMDataGridLabelsOverride`, merged
|
|
|
67
67
|
| `moveRight` | `Move right` | Column menu item. |
|
|
68
68
|
| `autosizeColumn` | `Autosize column` | Column menu item. |
|
|
69
69
|
| `hideColumn` | `Hide column` | Column menu item. |
|
|
70
|
-
| `filterOn` | `` `Filter on ${column}` `` | `aria-label` of the header filter button, of
|
|
70
|
+
| `filterOn` | `` `Filter on ${column}` `` | `aria-label` of the header filter button, of the header filter cell of a column that can filter, and of the value control in it. |
|
|
71
71
|
| `sortColumn` | `` `Sort ${column}` `` | `aria-label` of the header sort button. |
|
|
72
72
|
| `columnMenu` | `` `${column} column menu` `` | `aria-label` of the column menu trigger. |
|
|
73
73
|
| `rowsPerPage` | `Rows per page:` | `TMDataGrid.Footer`: the text before the page size select. |
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.
|
|
17
|
+
library_version: '2.4.0'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/react:packages/tmdatagrid/docs/cell-selection.md'
|
|
20
20
|
- 'Jielga/react:packages/tmdatagrid/src/core/cellNavigation.ts'
|
package/skills/columns/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Define and arrange TMDataGrid columns. Covers createTMDataGridColumnHelper,
|
|
5
5
|
every column meta field (label, type, options, flex, align, colSpan, autoSize,
|
|
6
6
|
enableOrdering, and the meta.filter and meta.edit namespaces holding
|
|
7
|
-
operators, defaultOperator, control, enabled, field, editor, validate and
|
|
7
|
+
operators, defaultOperator, control, url, enabled, field, editor, validate and
|
|
8
8
|
mapValue), the six column types, fluid minmax sizing versus fixed width with
|
|
9
9
|
minSize / maxSize / size, autosizing and autosizeColumn, hiding through
|
|
10
10
|
enableHiding and the columns panel, pinning and why a pinned column becomes
|
|
@@ -16,7 +16,7 @@ description: >
|
|
|
16
16
|
metadata:
|
|
17
17
|
type: core
|
|
18
18
|
library: '@jielga/tmdatagrid'
|
|
19
|
-
library_version: '2.
|
|
19
|
+
library_version: '2.4.0'
|
|
20
20
|
sources:
|
|
21
21
|
- 'Jielga/react:packages/tmdatagrid/docs/columns.md'
|
|
22
22
|
- 'Jielga/react:packages/tmdatagrid/docs/column-layout.md'
|
|
@@ -61,6 +61,9 @@ replaces the dots: `accessorKey: "address.city"` makes column id
|
|
|
61
61
|
addresses the column by id - `initialState`, `editing.columns`, `moveColumn`,
|
|
62
62
|
a test selector - takes the underscore form.
|
|
63
63
|
|
|
64
|
+
Helper columns, groups included, carry their id, `meta.type`, value type, `meta.filter.operators` and filter, sort and URL switches in their type (`TMDataGridColumnInfo`).
|
|
65
|
+
`createTMDataGridUrlState` (the `options` skill) reads them to type deep links; only literal values count.
|
|
66
|
+
|
|
64
67
|
`columnHelper.group` nests columns under a shared header; `columns` takes
|
|
65
68
|
another `columnHelper.columns` call, not a bare array:
|
|
66
69
|
|
|
@@ -94,7 +97,7 @@ What the column **is** stays flat; what the filter panel and the edit engine do
|
|
|
94
97
|
with it sits in their namespaces. `type` and `options` are read by both stages,
|
|
95
98
|
which is why they are in neither.
|
|
96
99
|
|
|
97
|
-
`meta.filter` holds `operators`, `defaultOperator` and `
|
|
100
|
+
`meta.filter` holds `operators`, `defaultOperator`, `control` and `url`; `meta.edit`
|
|
98
101
|
holds `enabled`, `field`, `editor`, `validate` and `mapValue`. Their fields are
|
|
99
102
|
in [references/columns-api.md](references/columns-api.md).
|
|
100
103
|
|
|
@@ -11,6 +11,7 @@ Reference for the `columns` skill.
|
|
|
11
11
|
| `operators` | `readonly TMDataGridFilterOperator[]` | The type's list | The operators this column offers, a subset of its type's. For a backend that answers only some. |
|
|
12
12
|
| `defaultOperator` | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter on this column starts with. |
|
|
13
13
|
| `control` | `TMDataGridFilterControlComponent` | By `meta.type` | Replaces the value control in this column's filter row. Module scope. |
|
|
14
|
+
| `url` | `boolean` | `true` | Whether the column's filter is written to the URL under `persist.urlKey`. `false` keeps it out of the link, and out of storage. |
|
|
14
15
|
|
|
15
16
|
`meta.edit` belongs to the `editing` skill, and only acts once `editing` is set:
|
|
16
17
|
|
package/skills/data/SKILL.md
CHANGED
package/skills/editing/SKILL.md
CHANGED
|
@@ -4,21 +4,21 @@ description: >
|
|
|
4
4
|
Narrow the rows a TMDataGrid shows. Covers the shared tmDataGrid filter
|
|
5
5
|
function and its {operator, value} model, the eighteen operators and which
|
|
6
6
|
meta.type offers each, meta.filter.operators to offer a column only a subset
|
|
7
|
-
of them, meta.filter.defaultOperator,
|
|
8
|
-
filter, the filters option and its surfaces
|
|
9
|
-
inHeader for header filters),
|
|
10
|
-
TMDataGrid.
|
|
11
|
-
openColumnFilter, replacing a value
|
|
12
|
-
DgDateRangeFilter / DgAutocompleteFilter /
|
|
13
|
-
meta.filter.control component, per-column filterFn, and
|
|
14
|
-
TMDataGrid.Search, quickSearchMode fuzzy or contains,
|
|
15
|
-
enableMatchHighlighting and enableGlobalFilter. Load when
|
|
16
|
-
choosing operators, building a filter control, showing active
|
|
17
|
-
the grid, or wiring a search box.
|
|
7
|
+
of them, meta.filter.defaultOperator, meta.filter.url for deep links,
|
|
8
|
+
isFilterActive and the half-typed filter, the filters option and its surfaces
|
|
9
|
+
(popup, sidebar, none, plus inHeader for header filters),
|
|
10
|
+
TMDataGrid.FilterPanel and its layout prop, TMDataGrid.FilterButton,
|
|
11
|
+
TMDataGrid.FilterPills with its api prop, openColumnFilter, replacing a value
|
|
12
|
+
control with DgRangeSliderFilter / DgDateRangeFilter / DgAutocompleteFilter /
|
|
13
|
+
DgTriStateFilter or a meta.filter.control component, per-column filterFn, and
|
|
14
|
+
the quick search: TMDataGrid.Search, quickSearchMode fuzzy or contains,
|
|
15
|
+
fuzzyGlobalFilterFn, enableMatchHighlighting and enableGlobalFilter. Load when
|
|
16
|
+
adding filters, choosing operators, building a filter control, showing active
|
|
17
|
+
filters outside the grid, or wiring a search box.
|
|
18
18
|
metadata:
|
|
19
19
|
type: core
|
|
20
20
|
library: '@jielga/tmdatagrid'
|
|
21
|
-
library_version: '2.
|
|
21
|
+
library_version: '2.4.0'
|
|
22
22
|
sources:
|
|
23
23
|
- 'Jielga/react:packages/tmdatagrid/docs/filtering.md'
|
|
24
24
|
- 'Jielga/react:packages/tmdatagrid/docs/quick-search.md'
|
|
@@ -105,6 +105,18 @@ columnHelper.accessor("customer", {
|
|
|
105
105
|
`getColumnDefaultOperator(column)` the operator a fresh filter opens on. The
|
|
106
106
|
`server-side` skill shows the list typed together with the API mapping table.
|
|
107
107
|
|
|
108
|
+
`meta.filter.url` determines whether the column's filter goes into the URL
|
|
109
|
+
search param when `persist.urlKey` carries `columnFilters`. By default it does.
|
|
110
|
+
Set it to `false` for a filter that should not travel in a shared link; the
|
|
111
|
+
filter is then not persisted anywhere. The `options` skill covers `urlKey`.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
columnHelper.accessor("owner", {
|
|
115
|
+
header: "Owner",
|
|
116
|
+
meta: { filter: { url: false } },
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
108
120
|
## The filters option
|
|
109
121
|
|
|
110
122
|
`filters` on `useTMDataGrid` picks the surface. It is read field by field, so an
|
|
@@ -185,6 +197,7 @@ No props.
|
|
|
185
197
|
|
|
186
198
|
One pill per **active** filter, `First name: Sofia ✕`, where the ✕ clears it and a click on the label calls `openColumnFilter`.
|
|
187
199
|
The label spells the operator out unless it is the type's default: `Age is greater than 30`, but `First name: Sofia`.
|
|
200
|
+
A value with an option reads as the option's label, and a boolean value as `labels.booleanTrue` or `labels.booleanFalse`.
|
|
188
201
|
It takes the grid as an `api` prop instead of reading context, so it renders anywhere on the page.
|
|
189
202
|
|
|
190
203
|
```tsx
|
|
@@ -469,7 +482,7 @@ Source: `packages/tmdatagrid/docs/quick-search.md` (Fuzzy by default).
|
|
|
469
482
|
| `getOperatorsForType` | Export | `(type) => operators` | – | The operator list a type offers. |
|
|
470
483
|
| `getColumnOperators` · `getColumnDefaultOperator` | Exports | `(column) => operators` · `(column) => operator` | – | One column's list after `meta.filter.operators`, and the operator a fresh filter on it opens on. |
|
|
471
484
|
| `FILTER_OPERATOR_LABELS` | Export | record | – | The label shown for each operator. |
|
|
472
|
-
| `formatFilterLabel` | Export | `({ label, type, filter }) => string` | – | The one-line description used on the pills. |
|
|
485
|
+
| `formatFilterLabel` | Export | `({ label, type, filter, operatorLabels?, options?, labels? }) => string` | – | The one-line description used on the pills. `operatorLabels` names the operators, `options` gives a value its option's label, and `labels` gives a boolean its `booleanTrue` / `booleanFalse`; each defaults to English or to the value itself. |
|
|
473
486
|
| `emptyValueForOperator` · `operatorNeedsValue` · `operatorTakesArrayValue` · `operatorTakesRangeValue` | Exports | – | – | What shape of value an operator expects. |
|
|
474
487
|
| `TMDataGridFilterValueInput` | Export | component | – | The default value control, for falling back to. |
|
|
475
488
|
| `DgRangeSliderFilter` · `DgDateRangeFilter` · `DgAutocompleteFilter` · `DgTriStateFilter` | Exports | components | – | The four ready-made controls. |
|
|
@@ -13,7 +13,7 @@ description: >
|
|
|
13
13
|
metadata:
|
|
14
14
|
type: core
|
|
15
15
|
library: '@jielga/tmdatagrid'
|
|
16
|
-
library_version: '2.
|
|
16
|
+
library_version: '2.4.0'
|
|
17
17
|
sources:
|
|
18
18
|
- 'Jielga/react:packages/tmdatagrid/docs/getting-started.md'
|
|
19
19
|
- 'Jielga/react:packages/tmdatagrid/docs/anatomy.md'
|
|
@@ -355,7 +355,7 @@ Source: `packages/tmdatagrid/docs/card-view.md`,
|
|
|
355
355
|
| `openColumnFilter(api, columnId)` | Adds an empty filter for a column and opens the panel. |
|
|
356
356
|
| `getColumnLabel(column)` | `meta.label`, a string header, or the column id. |
|
|
357
357
|
| `getColumnType(column)` | `meta.type`, defaulting to `"string"`. |
|
|
358
|
-
| `formatFilterLabel({ label, type, filter })` | The one-line filter description used on the pills. |
|
|
358
|
+
| `formatFilterLabel({ label, type, filter, operatorLabels?, options?, labels? })` | The one-line filter description used on the pills. |
|
|
359
359
|
| `moveColumn({ table, columnId, targetId, side })` | Moves a column next to another. See the `columns` skill. |
|
|
360
360
|
| `moveColumnByStep({ table, columnId, direction })` | Moves a column one position within its region. |
|
|
361
361
|
| `SELECT_COLUMN_ID` | Id of the generated checkbox column. |
|
package/skills/grouping/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.
|
|
17
|
+
library_version: '2.4.0'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/react:packages/tmdatagrid/docs/grouping.md'
|
|
20
20
|
- 'Jielga/react:packages/tmdatagrid/docs/summary-row.md'
|
|
@@ -275,7 +275,7 @@ Source: `packages/tmdatagrid/docs/grouping.md` (Server-side grids).
|
|
|
275
275
|
| `aggregateColumn` | Export | `({ table, columnId, fn }) => unknown` | `fn: "sum"` | Aggregates over every filtered row, all pages. |
|
|
276
276
|
| `TMDataGridAggregationName` | Export | type | – | The registered function names. |
|
|
277
277
|
| `GROUP_COLUMN_ID` | Export | `"__group__"` | – | Id of the generated tree column. |
|
|
278
|
-
| `formatGroupValue` | Export | `(value) => string` | – | How the tree lane
|
|
278
|
+
| `formatGroupValue` | Export | `(value, blankLabel?, { type?, options?, labels? }?) => string` | – | How the tree lane writes a group's value: `blankLabel` for an empty value, an option's label for a value with an option, `labels.booleanTrue` / `booleanFalse` for a boolean, and `toLocaleDateString()` for a `Date`. |
|
|
279
279
|
| `getGroupDataRows` | Export | `(row) => Row[]` | – | Every record under a group row, at any depth. |
|
|
280
280
|
| `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything. `false` while grouped. |
|
|
281
281
|
| `--dg-row-group-bg` | CSS variable | colour | Themed | Group row background. |
|
|
@@ -15,7 +15,7 @@ description: >
|
|
|
15
15
|
metadata:
|
|
16
16
|
type: lifecycle
|
|
17
17
|
library: '@jielga/tmdatagrid'
|
|
18
|
-
library_version: '2.
|
|
18
|
+
library_version: '2.4.0'
|
|
19
19
|
sources:
|
|
20
20
|
- 'Jielga/react:packages/tmdatagrid/docs/migrating-to-2.md'
|
|
21
21
|
- 'Jielga/react:packages/tmdatagrid/src/index.ts'
|
package/skills/options/SKILL.md
CHANGED
|
@@ -9,21 +9,23 @@ description: >
|
|
|
9
9
|
labels, editing, filters, exportOptions, enableColumnOrdering,
|
|
10
10
|
enablePagination, resetPageOnQueryChange, enableRowNumbers, selectionMode,
|
|
11
11
|
showSelectedBackground, cellSelection, renderDetails, overscan,
|
|
12
|
-
quickSearchMode), persist with dataKey/settingsKey slices
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
state from a parent.
|
|
12
|
+
quickSearchMode), persist with dataKey/settingsKey slices, storageMode and
|
|
13
|
+
urlKey and createTMDataGridUrlState deep links, the returned api (table, ui,
|
|
14
|
+
edit, features, labels, resetSettings, scrollToRow), ui actions, and reading
|
|
15
|
+
grid state with useSelector over table.store, since table.getState() does not
|
|
16
|
+
exist in v9. Load when configuring the hook, looking up an option's default,
|
|
17
|
+
persisting layout or filters, or reacting to grid state from a parent.
|
|
18
18
|
metadata:
|
|
19
19
|
type: core
|
|
20
20
|
library: '@jielga/tmdatagrid'
|
|
21
|
-
library_version: '2.
|
|
21
|
+
library_version: '2.4.0'
|
|
22
22
|
sources:
|
|
23
23
|
- 'Jielga/react:packages/tmdatagrid/docs/use-tm-data-grid.md'
|
|
24
24
|
- 'Jielga/react:packages/tmdatagrid/docs/persistence.md'
|
|
25
|
+
- 'Jielga/react:packages/tmdatagrid/docs/typed-deep-links.md'
|
|
25
26
|
- 'Jielga/react:packages/tmdatagrid/src/useTMDataGrid.tsx'
|
|
26
27
|
- 'Jielga/react:packages/tmdatagrid/src/core/persistence.ts'
|
|
28
|
+
- 'Jielga/react:packages/tmdatagrid/src/core/urlState.ts'
|
|
27
29
|
---
|
|
28
30
|
|
|
29
31
|
# TMDataGrid - useTMDataGrid
|
|
@@ -160,14 +162,17 @@ const grid = useTMDataGrid({
|
|
|
160
162
|
## persist
|
|
161
163
|
|
|
162
164
|
Restores table state on mount and writes it back on every change. State is split
|
|
163
|
-
across
|
|
164
|
-
stays valid indefinitely,
|
|
165
|
-
changes
|
|
165
|
+
across three keys because the groups have different lifetimes: column configuration
|
|
166
|
+
stays valid indefinitely, filters and pagination go stale as the data
|
|
167
|
+
changes, and a deep link has to travel with the page URL. Separate keys let one
|
|
168
|
+
group be cleared without touching the others.
|
|
166
169
|
|
|
167
170
|
| Field | Type | Default | Description |
|
|
168
171
|
| --- | --- | --- | --- |
|
|
169
172
|
| `dataKey` | `TMDataGridPersistKey<TMDataGridDataSlice>`: `string \| [string, DataSlice[]]` | – | Storage key for the data group. |
|
|
170
173
|
| `settingsKey` | `TMDataGridPersistKey<TMDataGridSettingsSlice>`: `string \| [string, SettingsSlice[]]` | – | Storage key for the settings group. |
|
|
174
|
+
| `urlKey` | `TMDataGridPersistKey<TMDataGridDataSlice>`: `string \| [string, DataSlice[]]` | – | Search param for data slices kept in the URL. See [Deep links](#deep-links). |
|
|
175
|
+
| `urlStorage` | `TMDataGridUrlStorage`: `{ get(name): string \| null; set(name, value: string \| null): void }` | `location.search` and `history.replaceState` | Reads and writes the URL param. |
|
|
171
176
|
| `storageMode` | `TMDataGridStorageMode`: `"localStorage" \| "sessionStorage"` | `"localStorage"` | Storage area. `"sessionStorage"` is per tab. |
|
|
172
177
|
| `serialize` | `(value) => string` | `JSON.stringify` | Serializes before storing. |
|
|
173
178
|
| `deserialize` | `(value: string) => unknown` | `JSON.parse` | Parses a stored payload. |
|
|
@@ -203,6 +208,7 @@ const persist = {
|
|
|
203
208
|
| Group | Available slices |
|
|
204
209
|
| --- | --- |
|
|
205
210
|
| `dataKey` | `columnFilters`, `globalFilter`, `sorting`, `pagination`, `expanded` |
|
|
211
|
+
| `urlKey` | The data slices. A slice listed here is taken out of `dataKey`. |
|
|
206
212
|
| `settingsKey` | `columnVisibility`, `columnSizing`, `columnOrder`, `columnPinning`, `grouping` |
|
|
207
213
|
|
|
208
214
|
`grouping` is a settings slice because it names columns rather than values, so it
|
|
@@ -227,6 +233,95 @@ dropped.
|
|
|
227
233
|
|
|
228
234
|
Source: `packages/tmdatagrid/docs/persistence.md` (Behaviour).
|
|
229
235
|
|
|
236
|
+
### Deep links
|
|
237
|
+
|
|
238
|
+
`urlKey` keeps data slices in one URL search param, so a link to the page opens
|
|
239
|
+
the grid on the same filters. The string is the param name; a bare key takes
|
|
240
|
+
every data slice, a tuple a subset, as with `dataKey`.
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
const persist = {
|
|
244
|
+
settingsKey: "employees.settings",
|
|
245
|
+
// Sorting is remembered per browser; the filters travel in the link.
|
|
246
|
+
dataKey: ["employees.data", ["sorting"]],
|
|
247
|
+
urlKey: ["employees", ["columnFilters", "globalFilter"]],
|
|
248
|
+
} satisfies TMDataGridPersistence;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- A slice lives in one group. A slice listed under `urlKey` is taken out of
|
|
252
|
+
`dataKey`: a bare `dataKey` hands it over silently, while a `dataKey` tuple
|
|
253
|
+
that names the slice explicitly is reported with `console.error` on mount.
|
|
254
|
+
- The param carries only the slices whose value differs from the initial state
|
|
255
|
+
(`initialState` merged over the grid defaults), and only the column filters
|
|
256
|
+
that are active. When nothing differs, the param is removed. A link without the param, or without a slice, opens on the
|
|
257
|
+
initial state; a URL slice never falls back to storage.
|
|
258
|
+
- Writes use `history.replaceState`, never `pushState`. Only the grid's own
|
|
259
|
+
param changes; the path, the hash, other params and `history.state` are kept.
|
|
260
|
+
Two grids on one page use two keys.
|
|
261
|
+
- The URL payload has no version stamp, so a bookmarked link survives a
|
|
262
|
+
release. The shape guards and the realignment against existing columns apply
|
|
263
|
+
as for storage, and `serialize` / `deserialize` apply to the URL payload too.
|
|
264
|
+
- `meta.filter.url: false` on a column keeps its filter out of the param, on
|
|
265
|
+
read and on write. The column's filter is then not persisted anywhere, and a
|
|
266
|
+
filter on it in `initialState` still applies when a link is opened. The
|
|
267
|
+
flag has no effect unless `urlKey` carries `columnFilters`.
|
|
268
|
+
- `urlStorage` replaces the default read and write of the param, for a router
|
|
269
|
+
that owns the URL. `get` returns the param's string or `null`; `set` receives
|
|
270
|
+
the string, or `null` to remove the param. `set` is only called when the
|
|
271
|
+
value changed. Keep `persist` referentially stable here too: react-router
|
|
272
|
+
rebuilds `setSearchParams` on every URL change, so read it through a ref.
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
// react-router: let the router do the replace.
|
|
276
|
+
const [, setSearchParams] = useSearchParams();
|
|
277
|
+
const setSearchParamsRef = useRef(setSearchParams);
|
|
278
|
+
setSearchParamsRef.current = setSearchParams;
|
|
279
|
+
|
|
280
|
+
const persist = useMemo(
|
|
281
|
+
() =>
|
|
282
|
+
({
|
|
283
|
+
urlKey: ["employees", ["columnFilters"]],
|
|
284
|
+
urlStorage: {
|
|
285
|
+
get: (name) => new URLSearchParams(window.location.search).get(name),
|
|
286
|
+
set: (name, value) =>
|
|
287
|
+
setSearchParamsRef.current(
|
|
288
|
+
(prev) => {
|
|
289
|
+
if (value === null) prev.delete(name);
|
|
290
|
+
else prev.set(name, value);
|
|
291
|
+
return prev;
|
|
292
|
+
},
|
|
293
|
+
{ replace: true },
|
|
294
|
+
),
|
|
295
|
+
},
|
|
296
|
+
}) satisfies TMDataGridPersistence,
|
|
297
|
+
[],
|
|
298
|
+
);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Source: `packages/tmdatagrid/docs/persistence.md` (Deep links).
|
|
302
|
+
|
|
303
|
+
### Typed links
|
|
304
|
+
|
|
305
|
+
`createTMDataGridUrlState({ key, columns, slices? })` defines a grid's deep
|
|
306
|
+
links from the columns the grid renders, built with
|
|
307
|
+
`createTMDataGridColumnHelper`. A link can name only the filters, operators,
|
|
308
|
+
values and sorts those columns offer; anything else is a type error. The
|
|
309
|
+
definition gives `urlKey` for `persist`, the link as `searchParams`, `search`
|
|
310
|
+
or `payload`, a route `validateSearch`, and `persist(storage?)`, which keeps
|
|
311
|
+
the param in a router's search such as TanStack Router's.
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
export const employeesUrl = createTMDataGridUrlState({ key: "employees", columns });
|
|
315
|
+
|
|
316
|
+
const persist = { urlKey: employeesUrl.urlKey } satisfies TMDataGridPersistence;
|
|
317
|
+
const href = `/employees?${employeesUrl.searchParams({
|
|
318
|
+
filters: { department: { operator: "isAnyOf", value: ["Design"] } },
|
|
319
|
+
})}`;
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Every rule, every member and the TanStack Router setup are in
|
|
323
|
+
[references/typed-links.md](references/typed-links.md).
|
|
324
|
+
|
|
230
325
|
## Return value
|
|
231
326
|
|
|
232
327
|
| Field | Type | Description |
|