@jielga/tmdatagrid 2.3.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.
Files changed (38) hide show
  1. package/dist/index.d.ts +262 -8
  2. package/dist/index.js +1340 -1218
  3. package/dist/index.js.map +1 -1
  4. package/docs/columns.md +7 -0
  5. package/docs/filtering.md +2 -1
  6. package/docs/grouping.md +1 -1
  7. package/docs/localization.md +3 -3
  8. package/docs/persistence.md +88 -0
  9. package/docs/typed-deep-links.md +169 -0
  10. package/package.json +1 -1
  11. package/skills/appearance/SKILL.md +1 -1
  12. package/skills/appearance/references/labels.md +3 -3
  13. package/skills/cell-selection/SKILL.md +1 -1
  14. package/skills/columns/SKILL.md +4 -1
  15. package/skills/data/SKILL.md +1 -1
  16. package/skills/editing/SKILL.md +1 -1
  17. package/skills/filtering/SKILL.md +3 -2
  18. package/skills/getting-started/SKILL.md +2 -2
  19. package/skills/grouping/SKILL.md +2 -2
  20. package/skills/migrating-to-2/SKILL.md +1 -1
  21. package/skills/options/SKILL.md +30 -6
  22. package/skills/options/references/typed-links.md +134 -0
  23. package/skills/rows/SKILL.md +1 -1
  24. package/skills/server-side/SKILL.md +1 -1
  25. package/skills/testing/SKILL.md +1 -1
  26. package/skills/testing-components/SKILL.md +1 -1
  27. package/skills/testing-editing/SKILL.md +1 -1
  28. package/src/components/TMDataGridFilterPills.tsx +15 -1
  29. package/src/components/TMDataGridGroupColumn.tsx +19 -4
  30. package/src/components/TMDataGridHeaderFilterRow.tsx +7 -1
  31. package/src/core/filterOperators.ts +41 -18
  32. package/src/core/grouping.ts +11 -3
  33. package/src/core/labels.ts +4 -3
  34. package/src/core/persistence.ts +9 -1
  35. package/src/core/urlState.ts +439 -0
  36. package/src/core/valueText.ts +46 -0
  37. package/src/index.ts +13 -0
  38. package/src/useTMDataGrid.tsx +50 -7
package/docs/columns.md CHANGED
@@ -47,6 +47,13 @@ addresses the column by id - `initialState`, `editing.columns`, `moveColumn`,
47
47
  a test selector - takes the underscore form; the dotted form silently matches
48
48
  nothing.
49
49
 
50
+ Each column the helper returns also carries, in its type only, its id,
51
+ `meta.type`, value type, `meta.filter.operators`, and its `enableColumnFilter`,
52
+ `enableSorting` and `meta.filter.url` switches.
53
+ [Typed links](/docs/persistence#typed-links) read them, so a link into the grid
54
+ can name only the filters and sorts its columns offer.
55
+ Only literal values count: `enableSorting: isAdmin` reads as not sortable.
56
+
50
57
  ### Columns derived from the other rows
51
58
 
52
59
  `accessorFn` is handed one row, so a value that depends on the rest - a share
package/docs/filtering.md CHANGED
@@ -206,6 +206,7 @@ file: columns/FilterPanelPlaced.tsx
206
206
  ## TMDataGrid.FilterPills
207
207
 
208
208
  One pill per active filter - `First name: Sofia ✕` - where the ✕ clears that filter and a click on the label sends the user to that column's control through [openColumnFilter](#opencolumnfilter).
209
+ A value with an option reads as the option's label, and a boolean value as `labels.booleanTrue` or `labels.booleanFalse`.
209
210
  Half-typed filters produce no pill.
210
211
  The label spells the operator out unless it is the column type's default: `Age is greater than 30`, but `First name: Sofia`.
211
212
 
@@ -447,7 +448,7 @@ stays, empty.
447
448
  | `getColumnOperators` · `getColumnDefaultOperator` | Exports | `(column) => operators` · `(column) => operator` | – | The list one column offers after `meta.filter.operators`, and the operator a fresh filter on it opens on. |
448
449
  | `FILTER_OPERATOR_LABELS` | Export | record | – | The label shown for each operator. |
449
450
  | `TMDataGridFilterValueInput` | Export | component | – | The default value control, for falling back to. |
450
- | `formatFilterLabel` | Export | `({ label, type, filter }) => string` | – | The one-line description used on the pills. |
451
+ | `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. |
451
452
  | `emptyValueForOperator` · `operatorNeedsValue` · `operatorTakesArrayValue` · `operatorTakesRangeValue` · `filterValueShape` | Exports | – | – | What shape of value an operator expects. |
452
453
  | `TMDataGridFilterValueShape` | Type | `"scalar" \| "set" \| "range"` | – | What `filterValueShape` returns for an operator. |
453
454
  | `TMDataGridFiltersOptions` · `TMDataGridFiltersSettings` · `TMDataGridFilterSurface` · `TMDataGridFilterSidebarSide` | Types | – | – | The `filters` option, and the resolved form of it on `api.filters`. |
package/docs/grouping.md CHANGED
@@ -160,7 +160,7 @@ server-side can set `enableGrouping: true` alongside `manualGrouping: true`. See
160
160
  | `aggregationFn` | Column option | `TMDataGridAggregationName \| fn` | – | How a column fills in its group cells. Unset leaves them blank. |
161
161
  | `aggregatedCell` | Column option | `(ctx) => ReactNode` | `cell` over the aggregate when `aggregationFn` is set, else blank | Renders a group row's value differently from a data row's. |
162
162
  | `GROUP_COLUMN_ID` | Export | `"__group__"` | – | Id of the generated tree column. |
163
- | `formatGroupValue` | Export | `(value) => string` | – | How the tree lane renders a group's value. |
163
+ | `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`. |
164
164
  | `getGroupDataRows` | Export | `(row) => Row[]` | – | Every record under a group row, at any depth. |
165
165
  | `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything. `false` while grouped. |
166
166
  | `--dg-row-group-bg` | CSS variable | colour | Themed | Group row background. |
@@ -111,8 +111,8 @@ shows it; a function label shows its template:
111
111
  | `filterFrom` | `From` | The lower bound of a `between` filter: its input, or its slider thumb. |
112
112
  | `filterTo` | `To` | The upper bound of a `between` filter: its input, or its slider thumb. |
113
113
  | `filterAll` | `All` | The no-filter segment of the tri-state filter. |
114
- | `booleanTrue` | `Yes` | The `true` choice of a boolean filter value control and of the tri-state filter. |
115
- | `booleanFalse` | `No` | The `false` choice of a boolean filter value control and of the tri-state filter. |
114
+ | `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. |
115
+ | `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. |
116
116
  | `addFilter` | `Add filter` | Button in `TMDataGrid.FilterPanel`. |
117
117
  | `clearAllFilters` | `Clear all` | Button in `TMDataGrid.FilterPanel` and in `TMDataGrid.FilterPills`. |
118
118
  | `closeFilters` | `Close filters` | `aria-label` of the close button of the filter popup and sidebar. |
@@ -134,7 +134,7 @@ shows it; a function label shows its template:
134
134
  | `moveRight` | `Move right` | Column menu item. |
135
135
  | `autosizeColumn` | `Autosize column` | Column menu item. |
136
136
  | `hideColumn` | `Hide column` | Column menu item. |
137
- | `filterOn` | `` `Filter on ${column}` `` | `aria-label` of the header filter button, of a header filter cell, and of the value control in it. |
137
+ | `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. |
138
138
  | `sortColumn` | `` `Sort ${column}` `` | `aria-label` of the header sort button. |
139
139
  | `columnMenu` | `` `${column} column menu` `` | `aria-label` of the column menu trigger. |
140
140
  | `rowsPerPage` | `Rows per page:` | `TMDataGrid.Footer`: the text before the page size select. |
@@ -116,6 +116,55 @@ The column's filter is then not persisted anywhere. A filter on it in
116
116
  `initialState` still applies when a link is opened. The flag has no effect
117
117
  unless `urlKey` carries `columnFilters`.
118
118
 
119
+ ### Typed links
120
+
121
+ `createTMDataGridUrlState` defines a grid's deep links from its columns.
122
+ Pass it the array the grid renders, built with `createTMDataGridColumnHelper`:
123
+
124
+ ```tsx
125
+ const columns = columnHelper.columns([
126
+ columnHelper.accessor("department", { header: "Department", meta: { type: "select" } }),
127
+ columnHelper.accessor("salary", { header: "Salary", meta: { type: "number" } }),
128
+ columnHelper.accessor("email", { header: "Email", enableColumnFilter: false }),
129
+ ]);
130
+
131
+ export const employeesUrl = createTMDataGridUrlState({
132
+ key: "employees",
133
+ columns,
134
+ slices: ["columnFilters", "sorting"],
135
+ });
136
+
137
+ // The grid
138
+ const persist = { urlKey: employeesUrl.urlKey } satisfies TMDataGridPersistence;
139
+
140
+ // A link from anywhere in the app
141
+ const href = `/employees?${employeesUrl.searchParams({
142
+ filters: { department: { operator: "isAnyOf", value: ["Design"] } },
143
+ sorting: [{ id: "salary", desc: true }],
144
+ })}`;
145
+ ```
146
+
147
+ A link may name only what the grid's columns offer:
148
+
149
+ - `filters` takes the columns that can be filtered: `enableColumnFilter` is not `false` and `meta.filter.url` is not `false`.
150
+ - The operators are the ones the column's `meta.type` offers, narrowed by `meta.filter.operators`.
151
+ - The value has the column's type: a `number` for a number column, a `Date` or a `YYYY-MM-DD` string for a date column, the column's own values for a select column, and no value for `isEmpty` and `isNotEmpty`.
152
+ - `sorting` takes the columns whose `enableSorting` is not `false`.
153
+ - Only the slices in `slices` are accepted. By default, every data slice is.
154
+
155
+ Anything else is a type error.
156
+ The column helper reads these switches from literal values, so `enableSorting: isAdmin` reads as not sortable.
157
+ A column with an accessor function is named by its `id`; without an `id` it cannot be named.
158
+ Columns built without the helper offer nothing to a link.
159
+
160
+ The definition builds the link in three forms:
161
+
162
+ - `searchParams(input)` - `URLSearchParams` for a plain URL, encoded the way the default `serialize` encodes the param
163
+ - `search(input)` - `{ [key]: payload }`, for a router that keeps objects in its search
164
+ - `payload(input)` - the param's value alone, in the grid's state shape
165
+
166
+ Pass `searchParams` the same `serialize` as `persist` when you set one.
167
+
119
168
  ### Routers that own the URL
120
169
 
121
170
  `urlStorage` replaces the default read and write of the param, for a router
@@ -152,6 +201,31 @@ const persist = useMemo(
152
201
  );
153
202
  ```
154
203
 
204
+ A router that keeps objects in its search params and encodes them itself,
205
+ such as TanStack Router, takes the payload as an object.
206
+ Pass its search to `persist` on a typed link definition:
207
+
208
+ ```tsx
209
+ const router = useRouter();
210
+ const persist = useMemo(
211
+ () =>
212
+ employeesUrl.persist({
213
+ get: (name) => (router.state.location.search as Record<string, unknown>)[name],
214
+ set: (name, value) =>
215
+ void router.navigate({
216
+ to: ".",
217
+ search: (prev: Record<string, unknown>) => ({ ...prev, [name]: value }),
218
+ replace: true,
219
+ }),
220
+ }),
221
+ [router],
222
+ );
223
+ ```
224
+
225
+ `get` returns the param's value, or `undefined`; `set` receives the payload, or `undefined` to remove the param.
226
+ Leave `serialize` and `deserialize` at their defaults with `persist(storage)`, since the router does the encoding.
227
+ [Typed links with TanStack Router](/docs/typed-deep-links) puts the route, the grid and the links together.
228
+
155
229
  ## Behaviour
156
230
 
157
231
  **Restoring happens once**, on mount, through `initialState`. Writing is a
@@ -201,6 +275,20 @@ its `StorageType`.
201
275
  | `urlKey` | persist field | `string \| [string, DataSlice[]]` | – | Search param for data slices kept in the URL. See [Deep links](#deep-links). |
202
276
  | `urlStorage` | persist field | `TMDataGridUrlStorage` | `location.search` and `history.replaceState` | Reads and writes the URL param. |
203
277
  | `TMDataGridUrlStorage` | Type | `{ get(name): string \| null; set(name, value: string \| null): void }` | – | The type of `urlStorage`. |
278
+ | `createTMDataGridUrlState` | Export | `({ key, columns, slices? }) => TMDataGridUrlState` | – | Defines typed links into the grid from its columns. See [Typed links](#typed-links). |
279
+ | `key` | Definition field | `string` | – | The URL search param. |
280
+ | `slices` | Definition field | `TMDataGridDataSlice[]` | Every data slice | The data slices the param carries. |
281
+ | `urlKey` | Definition field | `TMDataGridPersistKey` | – | For `persist.urlKey`: the key, or `[key, slices]` when `slices` is set. |
282
+ | `searchParams` | Definition method | `(input, { serialize? }) => URLSearchParams` | `serialize`: `JSON.stringify` | The link as search params. Empty when the input sets nothing. |
283
+ | `search` | Definition method | `(input) => { [key]: TMDataGridUrlPayload \| undefined }` | – | The link as a router search object. |
284
+ | `payload` | Definition method | `(input) => TMDataGridUrlPayload \| undefined` | – | The param's value, or `undefined` when the input sets nothing. |
285
+ | `validateSearch` | Definition method | `(search) => { [key]?: TMDataGridUrlPayload }` | – | A route search validator. Keeps the param when it is an object. |
286
+ | `persist` | Definition method | `(storage?: TMDataGridUrlSearchStorage) => { urlKey, urlStorage? }` | – | Spread into `persist`. If not given a storage, the grid uses the browser's URL. |
287
+ | `TMDataGridUrlInput` | Type | `{ filters?, globalFilter?, sorting?, pagination? }` | – | What a link may set, typed from the columns and `slices`. |
288
+ | `TMDataGridUrlPayload` | Type | `{ columnFilters?, globalFilter?, sorting?, pagination?, expanded? }` | – | The param's value, in the grid's state shape. |
289
+ | `TMDataGridUrlSearchStorage` | Type | `{ get(name): unknown; set(name, value: TMDataGridUrlPayload \| undefined): void }` | – | A router search read and written as values. |
290
+ | `TMDataGridColumnInfo` · `TMDataGridColumnInfoOf` · `TMDataGridTypedColumn` | Types | – | – | What a helper column tells a link about itself: id, type, value, operators, and its filter, sort and URL switches. |
291
+ | `TMDataGridUrlFiltersInput` · `TMDataGridUrlSortableId` · `TMDataGridDateInput` | Types | – | – | The parts of `TMDataGridUrlInput`. |
204
292
  | `meta.filter.url` | Column meta | `boolean` | `true` | Determines whether the column's filter is written to the URL under `urlKey`. |
205
293
  | `TMDataGridPersistKey` | Type | `string \| [string, slices]` | – | The type of `dataKey`, `settingsKey` and `urlKey`. |
206
294
  | `storageMode` | persist field | `"localStorage" \| "sessionStorage"` | `"localStorage"` | Storage area. `"sessionStorage"` is per tab. |
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jielga/tmdatagrid",
3
- "version": "2.3.0",
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",
@@ -18,7 +18,7 @@ description: >
18
18
  metadata:
19
19
  type: core
20
20
  library: '@jielga/tmdatagrid'
21
- library_version: '2.3.0'
21
+ library_version: '2.4.0'
22
22
  sources:
23
23
  - 'Jielga/react:packages/tmdatagrid/docs/styling.md'
24
24
  - 'Jielga/react:packages/tmdatagrid/docs/toolbar.md'
@@ -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 a header filter cell, and of the value control in it. |
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.3.0'
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'
@@ -16,7 +16,7 @@ description: >
16
16
  metadata:
17
17
  type: core
18
18
  library: '@jielga/tmdatagrid'
19
- library_version: '2.3.0'
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
 
@@ -18,7 +18,7 @@ description: >
18
18
  metadata:
19
19
  type: core
20
20
  library: '@jielga/tmdatagrid'
21
- library_version: '2.3.0'
21
+ library_version: '2.4.0'
22
22
  sources:
23
23
  - 'Jielga/react:packages/tmdatagrid/docs/pagination.md'
24
24
  - 'Jielga/react:packages/tmdatagrid/docs/scrolling.md'
@@ -18,7 +18,7 @@ description: >
18
18
  metadata:
19
19
  type: core
20
20
  library: '@jielga/tmdatagrid'
21
- library_version: '2.3.0'
21
+ library_version: '2.4.0'
22
22
  sources:
23
23
  - 'Jielga/react:packages/tmdatagrid/docs/editing.md'
24
24
  - 'Jielga/react:packages/tmdatagrid/docs/draft-store.md'
@@ -18,7 +18,7 @@ description: >
18
18
  metadata:
19
19
  type: core
20
20
  library: '@jielga/tmdatagrid'
21
- library_version: '2.3.0'
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'
@@ -197,6 +197,7 @@ No props.
197
197
 
198
198
  One pill per **active** filter, `First name: Sofia ✕`, where the ✕ clears it and a click on the label calls `openColumnFilter`.
199
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`.
200
201
  It takes the grid as an `api` prop instead of reading context, so it renders anywhere on the page.
201
202
 
202
203
  ```tsx
@@ -481,7 +482,7 @@ Source: `packages/tmdatagrid/docs/quick-search.md` (Fuzzy by default).
481
482
  | `getOperatorsForType` | Export | `(type) => operators` | – | The operator list a type offers. |
482
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. |
483
484
  | `FILTER_OPERATOR_LABELS` | Export | record | – | The label shown for each operator. |
484
- | `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. |
485
486
  | `emptyValueForOperator` · `operatorNeedsValue` · `operatorTakesArrayValue` · `operatorTakesRangeValue` | Exports | – | – | What shape of value an operator expects. |
486
487
  | `TMDataGridFilterValueInput` | Export | component | – | The default value control, for falling back to. |
487
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.3.0'
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. |
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.3.0'
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 renders a group's value. |
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.3.0'
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'
@@ -10,20 +10,22 @@ description: >
10
10
  enablePagination, resetPageOnQueryChange, enableRowNumbers, selectionMode,
11
11
  showSelectedBackground, cellSelection, renderDetails, overscan,
12
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.
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.3.0'
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
@@ -298,6 +300,28 @@ const persist = useMemo(
298
300
 
299
301
  Source: `packages/tmdatagrid/docs/persistence.md` (Deep links).
300
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
+
301
325
  ## Return value
302
326
 
303
327
  | Field | Type | Description |