@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
|
@@ -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.3.0'
|
|
22
22
|
sources:
|
|
23
23
|
- 'Jielga/react:packages/tmdatagrid/docs/filtering.md'
|
|
24
24
|
- 'Jielga/react:packages/tmdatagrid/docs/quick-search.md'
|
|
@@ -50,11 +50,12 @@ cell is tested against) and `between` (a `[min, max]` pair, an empty string
|
|
|
50
50
|
leaving that end open).
|
|
51
51
|
|
|
52
52
|
A filter with an empty value **stays in state** so the panel keeps its row while
|
|
53
|
-
the user types.
|
|
54
|
-
|
|
55
|
-
`
|
|
56
|
-
|
|
57
|
-
`columnFilters`
|
|
53
|
+
the user types. The built-in matcher lets every row through, and the filter
|
|
54
|
+
does not set the header indicator and produces no pill.
|
|
55
|
+
`isFilterActive(value)` tells whether a value narrows anything.
|
|
56
|
+
`activeColumnFilters(columnFilters | table)` returns the active entries, typed,
|
|
57
|
+
and drops entries in any other value shape. An entry in `columnFilters` is not
|
|
58
|
+
necessarily active.
|
|
58
59
|
|
|
59
60
|
## Operators
|
|
60
61
|
|
|
@@ -104,10 +105,22 @@ columnHelper.accessor("customer", {
|
|
|
104
105
|
`getColumnDefaultOperator(column)` the operator a fresh filter opens on. The
|
|
105
106
|
`server-side` skill shows the list typed together with the API mapping table.
|
|
106
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
|
+
|
|
107
120
|
## The filters option
|
|
108
121
|
|
|
109
|
-
`filters` on `useTMDataGrid` picks the surface. It is read field by field, so
|
|
110
|
-
literal
|
|
122
|
+
`filters` on `useTMDataGrid` picks the surface. It is read field by field, so an
|
|
123
|
+
inline object literal needs no `useMemo`.
|
|
111
124
|
|
|
112
125
|
| Option | Type | Default | What it does |
|
|
113
126
|
| --- | --- | --- | --- |
|
|
@@ -132,13 +145,14 @@ close it; `FilterButton` is exempt from the click-away so it stays a toggle.
|
|
|
132
145
|
the toolbar. The rows give up the width rather than being covered, a click in
|
|
133
146
|
the table does not dismiss it, and clearing the filters leaves it standing.
|
|
134
147
|
Escape closes it. It starts open, being a layout choice; its rows are
|
|
135
|
-
`layout="stacked"`, since
|
|
148
|
+
`layout="stacked"`, since the default 280px cannot fit the column, operator and
|
|
149
|
+
value fields side by side.
|
|
136
150
|
|
|
137
151
|
**Header filters** - `inHeader: true` adds a header row of value controls, one
|
|
138
152
|
per filterable column, on the same column tracks as everything else. The
|
|
139
153
|
column and operator dropdowns are not there: the column is the one the cell
|
|
140
|
-
sits over, and the operator is a funnel button beside the input, tinted
|
|
141
|
-
default. The column menu's Filter item and the funnel indicator both come off,
|
|
154
|
+
sits over, and the operator is a funnel button beside the input, tinted when
|
|
155
|
+
the operator is not the type's default. The column menu's Filter item and the funnel indicator both come off,
|
|
142
156
|
having nothing left to reveal; the filtered column's tinted title stays. A
|
|
143
157
|
narrow column clips its control - give it a `minSize`.
|
|
144
158
|
|
|
@@ -158,7 +172,7 @@ hand-placed `TMDataGrid.FilterPanel` is the only one. Read
|
|
|
158
172
|
|
|
159
173
|
The panel of filter rows - one column / operator / value triple per filter, over "Add filter" and "Clear all".
|
|
160
174
|
A plain block with no title, no close button and no open state: it renders wherever it is mounted, and the popup and sidebar surfaces are wrappers around it.
|
|
161
|
-
It must be inside `<TMDataGrid>` (it reads the grid from context) and it only reads and writes `columnFilters`, so
|
|
175
|
+
It must be inside `<TMDataGrid>` (it reads the grid from context) and it only reads and writes `columnFilters`, so it works unchanged under `manualFiltering`.
|
|
162
176
|
Closing a surface only hides it; the filters stay.
|
|
163
177
|
**Clear all** drops every filter, half-typed ones included.
|
|
164
178
|
|
|
@@ -201,7 +215,8 @@ import { TMDataGridFilterPills } from "@jielga/tmdatagrid";
|
|
|
201
215
|
|
|
202
216
|
## openColumnFilter
|
|
203
217
|
|
|
204
|
-
`openColumnFilter(api, columnId)` seeds an empty filter if the column has none, then opens the surface on that column's panel row
|
|
218
|
+
`openColumnFilter(api, columnId)` seeds an empty filter if the column has none, then opens the surface on that column's panel row.
|
|
219
|
+
Under `inHeader` it instead scrolls the column's header control into view and focuses it, leaving the surface closed.
|
|
205
220
|
It is what a pill's label and the column menu's Filter item both call.
|
|
206
221
|
|
|
207
222
|
## Replacing the value control
|
|
@@ -232,41 +247,47 @@ the same way.
|
|
|
232
247
|
inside. It handles the value only: it reads `operator` to shape itself and
|
|
233
248
|
writes the bare value through `onChange`, and the grid stores the
|
|
234
249
|
`{ operator, value }` pair around it. The column and operator dropdowns remain
|
|
235
|
-
the panel's.
|
|
236
|
-
|
|
237
|
-
```tsx
|
|
238
|
-
import type { TMDataGridFilterControlComponent } from "@jielga/tmdatagrid";
|
|
239
|
-
|
|
240
|
-
// Module scope: a new identity per render remounts the control mid-typing.
|
|
241
|
-
const StatusFilter: TMDataGridFilterControlComponent = ({
|
|
242
|
-
operator,
|
|
243
|
-
value,
|
|
244
|
-
options,
|
|
245
|
-
onChange,
|
|
246
|
-
}) => (
|
|
247
|
-
<Chip.Group
|
|
248
|
-
multiple
|
|
249
|
-
value={Array.isArray(value) ? [...value] : []}
|
|
250
|
-
onChange={onChange}
|
|
251
|
-
>
|
|
252
|
-
{options.map((option) => (
|
|
253
|
-
<Chip key={option.value} value={option.value}>
|
|
254
|
-
{option.label}
|
|
255
|
-
</Chip>
|
|
256
|
-
))}
|
|
257
|
-
</Chip.Group>
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
meta: { type: "select", filter: { control: StatusFilter } }
|
|
261
|
-
```
|
|
250
|
+
the panel's. Worked example: `references/custom-filters.md` (A value control of
|
|
251
|
+
your own).
|
|
262
252
|
|
|
263
253
|
`args.options` arrives pre-resolved for a column that declares `meta.options` or
|
|
264
254
|
is select-shaped. `args.table` is available to a control that needs more than
|
|
265
255
|
that.
|
|
266
256
|
|
|
257
|
+
## Writing filters from your own interface
|
|
258
|
+
|
|
259
|
+
The grid's controls write plain TanStack state, so a control of your own does
|
|
260
|
+
the same: `column.setFilterValue({ operator, value })` or
|
|
261
|
+
`table.setColumnFilters([...])` with an envelope, and the matcher, the pills,
|
|
262
|
+
the header indicator and `activeColumnFilters` all read it.
|
|
263
|
+
`setFilterValue(undefined)` removes the entry; an empty envelope stays in state
|
|
264
|
+
and matches every row. `getColumnOperators(column)`,
|
|
265
|
+
`getColumnDefaultOperator(column)` and `emptyValueForOperator(operator)` build
|
|
266
|
+
a valid envelope. To keep the grid's panel and place it yourself, set
|
|
267
|
+
`filters: { surface: "none" }` and render `TMDataGrid.FilterPanel`. To drop
|
|
268
|
+
only the column menu's Filter item, pass `renderColumnMenuItems` to
|
|
269
|
+
`TMDataGrid.Table` and leave out the item keyed `"filter"`. To replace every
|
|
270
|
+
filter control, set `enableColumnFilters: false` and write the envelope
|
|
271
|
+
yourself; filters in state still apply. Worked examples:
|
|
272
|
+
`references/custom-filters.md`.
|
|
273
|
+
|
|
274
|
+
## Custom matching
|
|
275
|
+
|
|
267
276
|
To give a column its own *matching* instead of its own control, set `filterFn`
|
|
268
|
-
on the column definition.
|
|
269
|
-
|
|
277
|
+
on the column definition. A column `filterFn` **replaces the shared matcher**
|
|
278
|
+
for that column, so its third argument is the stored `{ operator, value }`
|
|
279
|
+
envelope, not the unwrapped value. Type it as `TMDataGridFilterFn<TData>` and
|
|
280
|
+
delegate to `tmDataGridFilterFn(row, columnId, filter)` for the operators it
|
|
281
|
+
does not handle; `matchesFilter(cellValue, filter)` applies the built-in
|
|
282
|
+
matching to a derived value, and `filterValueShape(operator)` tells whether
|
|
283
|
+
`value` is a string, a `ReadonlyArray<string>` (`isAnyOf` / `isNoneOf`) or a
|
|
284
|
+
`[min, max]` pair (`between`). A column `filterFn` is also called with
|
|
285
|
+
half-typed envelopes, which the built-in matcher treats as matching every row,
|
|
286
|
+
so return `true` for `!isFilterActive(filter)` first. The column's controls do
|
|
287
|
+
not change and keep reading the envelope. A filter function named by string
|
|
288
|
+
also receives the envelope and does not unwrap it, so TanStack's built-ins such
|
|
289
|
+
as `"includesString"` do not work as a column `filterFn`. Write custom matching
|
|
290
|
+
as a function. Worked example: `references/custom-filters.md`.
|
|
270
291
|
|
|
271
292
|
## Quick search
|
|
272
293
|
|
|
@@ -298,10 +319,20 @@ excluded.
|
|
|
298
319
|
|
|
299
320
|
## Turning it off
|
|
300
321
|
|
|
301
|
-
`enableColumnFilters: false` removes the
|
|
302
|
-
`
|
|
303
|
-
|
|
304
|
-
|
|
322
|
+
`enableColumnFilters: false` removes the Filter item from the column menu, the
|
|
323
|
+
`FilterButton`, the header filter row and the panel's column list, and disables
|
|
324
|
+
the panel's "Add filter" button. It does **not** clear `columnFilters`: an entry already in
|
|
325
|
+
state, or one written with `setFilterValue`, still narrows the rows, still shows
|
|
326
|
+
as a pill and still sets the header indicator. Call `table.resetColumnFilters()`
|
|
327
|
+
when switching the interface off. Under `filters.surface: "sidebar"`, set
|
|
328
|
+
`surface: "none"` as well, as the sidebar does not follow the switch.
|
|
329
|
+
`TMDataGrid.Menu` has no filter items, so the switch changes nothing there.
|
|
330
|
+
|
|
331
|
+
`enableColumnFilter: false` on a column removes that column's menu item, its
|
|
332
|
+
entry in the panel's column list and its header control - the header cell
|
|
333
|
+
stays, empty. `enableGlobalFilter: false` removes the search input, or on a
|
|
334
|
+
column removes its participation in the search. The generated lanes are already
|
|
335
|
+
excluded.
|
|
305
336
|
|
|
306
337
|
## Common mistakes
|
|
307
338
|
|
|
@@ -417,17 +448,20 @@ Source: `packages/tmdatagrid/docs/quick-search.md` (Fuzzy by default).
|
|
|
417
448
|
|
|
418
449
|
| Name | Kind | Type | Default | What it does |
|
|
419
450
|
| --- | --- | --- | --- | --- |
|
|
420
|
-
| `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the
|
|
451
|
+
| `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the menu item, the button, the header row and the panel's column list, and disables the panel's "Add filter" button. Filters already in state still apply. |
|
|
421
452
|
| `enableColumnFilter` | Column option | `boolean` | `true` | `false` takes one column out of filtering. |
|
|
422
453
|
| `meta.type` | Column meta | `TMDataGridColumnType` | `"string"` | Selects the operators and the value control. |
|
|
423
454
|
| `meta.filter.operators` | Column meta | `readonly TMDataGridFilterOperator[]` | The type's list | The operators this column offers, a subset of its type's. |
|
|
424
455
|
| `meta.filter.defaultOperator` | Column meta | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter opens on. |
|
|
425
456
|
| `meta.filter.control` | Column meta | `TMDataGridFilterControlComponent` | By type and operator | Replaces the value control. Module scope. |
|
|
426
|
-
| `filterFn` | Column option |
|
|
457
|
+
| `filterFn` | Column option | `TMDataGridFilterFn<TData>` or name | `"tmDataGrid"` | Custom matching for one column. Receives the `{ operator, value }` envelope. |
|
|
458
|
+
| `TMDataGridFilterFn<TData>` | Type | `(row, columnId, filter: TMDataGridFilterValue, addMeta?) => boolean` | – | The signature of a column's own `filterFn`. |
|
|
459
|
+
| `tmDataGridFilterFn` | Export | `(row, columnId, filterValue: unknown) => boolean`, assignable to `TMDataGridFilterFn<TData>` | – | The shared matcher, for a custom `filterFn` to delegate to. |
|
|
460
|
+
| `matchesFilter` | Export | `(cellValue, filter: TMDataGridFilterValue) => boolean` | – | The built-in matching applied to one value. |
|
|
427
461
|
| `quickSearchMode` | Option | `TMDataGridQuickSearchMode`: `"fuzzy" \| "contains"` | `"fuzzy"` | How the quick search matches. |
|
|
428
462
|
| `enableMatchHighlighting` | Option | `boolean` | `false` | Mark matched text in default-rendered cells. |
|
|
429
463
|
| `enableGlobalFilter` | Table option | `boolean` | `true` | Also a column option. Removes the input, or one column's participation. |
|
|
430
|
-
| `globalFilterFn` | Table option | filter fn |
|
|
464
|
+
| `globalFilterFn` | Table option | filter fn or name | `"tmDataGridFuzzy"`; `"includesString"` under `quickSearchMode: "contains"` | Overrides the matching, and the ranking with it. |
|
|
431
465
|
| `TMDataGrid.FilterPanel` | Component | `layout: "row" \| "stacked"`, Mantine `BoxProps` | `"row"` | The panel of filter rows, as a plain block. Style props set on it. |
|
|
432
466
|
| `TMDataGridFilterPanelProps` · `TMDataGridFilterPanelLayout` | Types | – · `"row" \| "stacked"` | – | The props of `TMDataGrid.FilterPanel`, and the type of its `layout`. For wrapping the panel in a component of your own. |
|
|
433
467
|
| `filters` | Table option | `TMDataGridFiltersOptions` | `{ surface: "popup" }` | Which surface holds the filter controls. |
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Custom filters
|
|
2
|
+
|
|
3
|
+
Worked examples for the three ways to take over part of TMDataGrid's filtering: a value control of your own, writing the filter state from an interface of your own, and matching a column with a function of your own.
|
|
4
|
+
All three keep the `{ operator, value }` envelope, so the panel, the pills, the header indicator and `activeColumnFilters` keep working.
|
|
5
|
+
|
|
6
|
+
## A value control of your own
|
|
7
|
+
|
|
8
|
+
`meta.filter.control` is a component that handles the value only: it reads `operator` to shape itself and writes the bare value through `onChange`.
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import type { TMDataGridFilterControlComponent } from "@jielga/tmdatagrid";
|
|
12
|
+
|
|
13
|
+
// Module scope: a new identity per render remounts the control mid-typing.
|
|
14
|
+
const StatusFilter: TMDataGridFilterControlComponent = ({
|
|
15
|
+
operator,
|
|
16
|
+
value,
|
|
17
|
+
options,
|
|
18
|
+
onChange,
|
|
19
|
+
}) => (
|
|
20
|
+
<Chip.Group
|
|
21
|
+
multiple
|
|
22
|
+
value={Array.isArray(value) ? [...value] : []}
|
|
23
|
+
onChange={onChange}
|
|
24
|
+
>
|
|
25
|
+
{options.map((option) => (
|
|
26
|
+
<Chip key={option.value} value={option.value}>
|
|
27
|
+
{option.label}
|
|
28
|
+
</Chip>
|
|
29
|
+
))}
|
|
30
|
+
</Chip.Group>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
meta: { type: "select", filter: { control: StatusFilter } }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Writing filters from your own interface
|
|
37
|
+
|
|
38
|
+
The grid's controls write plain TanStack state, so a control of your own does the same:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { emptyValueForOperator, type TMDataGridFilterValue } from "@jielga/tmdatagrid";
|
|
42
|
+
|
|
43
|
+
const status = grid.table.getColumn("status")!;
|
|
44
|
+
|
|
45
|
+
// A set filter, as the panel would write it.
|
|
46
|
+
status.setFilterValue({
|
|
47
|
+
operator: "isAnyOf",
|
|
48
|
+
value: ["active", "paused"],
|
|
49
|
+
} satisfies TMDataGridFilterValue);
|
|
50
|
+
|
|
51
|
+
// An empty envelope stays in state and matches every row.
|
|
52
|
+
status.setFilterValue({ operator: "isAnyOf", value: emptyValueForOperator("isAnyOf") });
|
|
53
|
+
|
|
54
|
+
// `undefined` removes the entry.
|
|
55
|
+
status.setFilterValue(undefined);
|
|
56
|
+
|
|
57
|
+
// Several columns at once.
|
|
58
|
+
grid.table.setColumnFilters([
|
|
59
|
+
{ id: "status", value: { operator: "equals", value: "active" } },
|
|
60
|
+
{ id: "salary", value: { operator: "between", value: ["40000", ""] } },
|
|
61
|
+
]);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`getColumnOperators(column)` returns the operators a column offers and `getColumnDefaultOperator(column)` the one a fresh filter opens on.
|
|
65
|
+
`emptyValueForOperator(operator)` returns the empty value of the right shape.
|
|
66
|
+
|
|
67
|
+
To keep the grid's panel and place it yourself, set `filters: { surface: "none" }` and render `TMDataGrid.FilterPanel` where it belongs.
|
|
68
|
+
To keep filtering on and drop only the column menu's Filter item, pass `renderColumnMenuItems` to `TMDataGrid.Table` and leave out the item keyed `"filter"`.
|
|
69
|
+
To replace every filter control, set `enableColumnFilters: false` and write the envelope yourself; filters in state still apply.
|
|
70
|
+
|
|
71
|
+
## Custom matching
|
|
72
|
+
|
|
73
|
+
A column `filterFn` replaces the shared matcher for that column, so its third argument is the stored envelope, not the unwrapped value.
|
|
74
|
+
Type it as `TMDataGridFilterFn<TData>` and delegate to `tmDataGridFilterFn` for the operators it does not handle itself.
|
|
75
|
+
A column `filterFn` is also called with half-typed envelopes, which the built-in matcher treats as matching every row, so return `true` for `!isFilterActive(filter)` first:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import {
|
|
79
|
+
isFilterActive,
|
|
80
|
+
tmDataGridFilterFn,
|
|
81
|
+
type TMDataGridFilterFn,
|
|
82
|
+
} from "@jielga/tmdatagrid";
|
|
83
|
+
|
|
84
|
+
// Reads "contains" as "has this tag", so "ops" matches the tag "ops" and not
|
|
85
|
+
// "devops"; every other operator keeps the built-in matching.
|
|
86
|
+
const matchesTags: TMDataGridFilterFn<Employee> = (row, columnId, filter) => {
|
|
87
|
+
if (!isFilterActive(filter)) return true;
|
|
88
|
+
if (filter.operator === "contains") {
|
|
89
|
+
const wanted = String(filter.value).toLowerCase();
|
|
90
|
+
return row.original.tags.some((tag) => tag.toLowerCase() === wanted);
|
|
91
|
+
}
|
|
92
|
+
return tmDataGridFilterFn(row, columnId, filter);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
columnHelper.accessor((row) => row.tags.join(", "), {
|
|
96
|
+
id: "tags",
|
|
97
|
+
header: "Tags",
|
|
98
|
+
filterFn: matchesTags,
|
|
99
|
+
meta: { type: "string" },
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`filter.value` is a string under the scalar operators, a `ReadonlyArray<string>` under `isAnyOf` / `isNoneOf` and a `[min, max]` pair under `between`; `filterValueShape(operator)` tells which.
|
|
104
|
+
`matchesFilter(cellValue, filter)` applies the built-in matching to a value derived by the function itself:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { isFilterActive, matchesFilter, type TMDataGridFilterFn } from "@jielga/tmdatagrid";
|
|
108
|
+
|
|
109
|
+
// Matches the filter against the owner's full name instead of the stored id.
|
|
110
|
+
const matchesOwner: TMDataGridFilterFn<Task> = (row, _columnId, filter) => {
|
|
111
|
+
if (!isFilterActive(filter)) return true;
|
|
112
|
+
return matchesFilter(ownerName(row.original.ownerId), filter);
|
|
113
|
+
};
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The column's filter controls do not change: the operators still come from `meta.type` and `meta.filter.operators`, and the panel, the pills and the header indicator keep reading the envelope.
|
|
117
|
+
A filter function named by string also receives the envelope and does not unwrap it, so TanStack's built-ins such as `"includesString"` do not work as a column `filterFn`.
|
|
118
|
+
Write custom matching as a function.
|
|
@@ -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.3.0'
|
|
17
17
|
sources:
|
|
18
18
|
- 'Jielga/react:packages/tmdatagrid/docs/getting-started.md'
|
|
19
19
|
- 'Jielga/react:packages/tmdatagrid/docs/anatomy.md'
|
|
@@ -157,7 +157,7 @@ flex column.
|
|
|
157
157
|
| --- | --- | --- |
|
|
158
158
|
| `TMDataGrid` | `table`, `ui`, `features`, `size`, `className`, `style` | Root. Provides context. `style` also takes CSS variables: `--dg-row-selected-bg`, `--dg-row-height`, `--dg-header-height`, `--dg-font-size`, `--dg-padding`. |
|
|
159
159
|
| `TMDataGrid.Table` | `onRowClick(row)`, `renderRowContextMenu`, `renderColumnMenuItems`, `rowContextMenuProps` | Header, virtualized body, and whichever filter surface the `filters` option asks for. `onRowClick` runs in addition to selection under `selectionMode: "row"`. |
|
|
160
|
-
| `TMDataGrid.Footer` | `pageSizeOptions` (default `[10, 25, 50, 100]
|
|
160
|
+
| `TMDataGrid.Footer` | `pageSizeOptions` (default `[10, 25, 50, 100]`, keep the array stable), `renderPagination` slot | Rows-per-page select, range and previous/next buttons. Renders nothing unless pagination is enabled. |
|
|
161
161
|
| `TMDataGrid.Toolbar` | `children` | Flex row above the grid. |
|
|
162
162
|
| `TMDataGrid.Spacer` | - | Pushes later toolbar items right. |
|
|
163
163
|
| `TMDataGrid.SummaryCount` | `children` | Visible rows out of total. |
|
|
@@ -360,6 +360,41 @@ Source: `packages/tmdatagrid/docs/card-view.md`,
|
|
|
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. |
|
|
362
362
|
|
|
363
|
+
## Coming from TanStack Table
|
|
364
|
+
|
|
365
|
+
`useTMDataGrid` takes TanStack's `TableOptions`, so a page built on
|
|
366
|
+
`useReactTable` keeps its `data`, `columns`, `state` and `onXChange` callbacks.
|
|
367
|
+
Go through these differences:
|
|
368
|
+
|
|
369
|
+
- **`getRowId`** - return the entity id. The index default points at another
|
|
370
|
+
record after a sort or a refetch, which breaks selection, expansion and
|
|
371
|
+
editing. Required once `editing` is set.
|
|
372
|
+
- **Stable `data` and `columns`** - define `columns` outside the component and
|
|
373
|
+
keep `data` referentially stable; a fresh `[]` for the empty case belongs in
|
|
374
|
+
a module-scope constant. Under `editing`, a transient `[]` drops every open
|
|
375
|
+
draft.
|
|
376
|
+
- **Sizing** - columns are fluid, `minmax(minSize, flex fr)`. `size` applies
|
|
377
|
+
only to a column pinned at mount, `maxSize` only to resizing and autosizing,
|
|
378
|
+
and `minSize` defaults to `80`. See the `columns` skill.
|
|
379
|
+
- **Defaults the grid changes** - `aggregationFn` is cleared, so grouping does
|
|
380
|
+
not sum anything on its own; `groupedColumnMode` is `"remove"`;
|
|
381
|
+
`autoResetExpanded` and `autoResetPageIndex` are off; `globalFilterFn` is the
|
|
382
|
+
fuzzy matcher. The full list is in the
|
|
383
|
+
`options` skill.
|
|
384
|
+
- **Filters are envelopes** - `columnFilters` holds `{ operator, value }`, and
|
|
385
|
+
a column's own `filterFn` receives that envelope. See the `filtering` skill.
|
|
386
|
+
- **Interface the page already owns** - `enableGlobalFilter: false`, or no
|
|
387
|
+
`TMDataGrid.Search`, for the search; `enableColumnFilters: false`, or
|
|
388
|
+
`filters: { surface: "none" }` to keep filtering without the grid's panel.
|
|
389
|
+
- **Page reset** - `resetPageOnQueryChange` (default `true`) returns to the
|
|
390
|
+
first page when a filter, the search, the sorting or the grouping changes
|
|
391
|
+
through the table. A data source re-filtered outside table state does not
|
|
392
|
+
trigger it; call `table.setPageIndex(0)` yourself, or use `manualFiltering`.
|
|
393
|
+
- **State reads** - there is no `table.getState()`; read slices with
|
|
394
|
+
`useSelector(table.store, ...)`. See the `options` skill.
|
|
395
|
+
- **Tests** - the grid publishes `data-dg-part`, `data-row-id` and
|
|
396
|
+
`data-column-id`, and no `data-testid`. See the `testing` skill.
|
|
397
|
+
|
|
363
398
|
## Common mistakes
|
|
364
399
|
|
|
365
400
|
### Columns or data defined during render
|
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.3.0'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/react:packages/tmdatagrid/docs/grouping.md'
|
|
20
20
|
- 'Jielga/react:packages/tmdatagrid/docs/summary-row.md'
|
|
@@ -96,8 +96,8 @@ would pass a row that looks real but is the wrong one. Group rows therefore:
|
|
|
96
96
|
A group row's checkbox selects every record under it at any depth, including
|
|
97
97
|
records inside collapsed sub-groups, showing a tick once all are selected and a
|
|
98
98
|
dash while only some are. Only the records reach `rowSelection`; a group row is
|
|
99
|
-
never in it, so `getSelectedRowModel()` and
|
|
100
|
-
how the tree is arranged. Under `enableMultiRowSelection: false` group rows carry
|
|
99
|
+
never in it, so `getSelectedRowModel()` and `onRowSelectionChange` do not depend
|
|
100
|
+
on how the tree is arranged. Under `enableMultiRowSelection: false` group rows carry
|
|
101
101
|
no checkbox.
|
|
102
102
|
|
|
103
103
|
`getGroupDataRows(row)` returns every record under a group row, at any depth.
|
|
@@ -116,8 +116,11 @@ page. Rendering the whole tree is the grid's default mode in any case, so only
|
|
|
116
116
|
the pager is lost.
|
|
117
117
|
|
|
118
118
|
`isPagingActive(table, features)` is exported so a custom pager can grey itself
|
|
119
|
-
out the same way.
|
|
120
|
-
|
|
119
|
+
out the same way. There is no option to keep the built-in pager active while
|
|
120
|
+
grouped, and `manualGrouping` does not change that: the pager greys out whenever
|
|
121
|
+
`grouping` is non-empty. To have both, page on the server with
|
|
122
|
+
`manualPagination` and `manualGrouping`, and render the pager through the
|
|
123
|
+
Footer's `renderPagination` slot, ignoring `isPagingActive`.
|
|
121
124
|
|
|
122
125
|
## The summary row
|
|
123
126
|
|
|
@@ -197,7 +200,7 @@ out instead of paging the tree, so a footer count wired to `getPageCount()`
|
|
|
197
200
|
reports a number nobody can navigate to. Read
|
|
198
201
|
`isPagingActive(table, features)` before trusting the pager state.
|
|
199
202
|
|
|
200
|
-
Source: `packages/tmdatagrid/docs/grouping.md` (Grouping
|
|
203
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (Grouping and pagination).
|
|
201
204
|
|
|
202
205
|
### HIGH Handing a group row to a row callback
|
|
203
206
|
|
|
@@ -224,7 +227,7 @@ const records = getGroupDataRows(groupRow).map((row) => row.original);
|
|
|
224
227
|
/>;
|
|
225
228
|
```
|
|
226
229
|
|
|
227
|
-
Source: `packages/tmdatagrid/docs/grouping.md` (Group rows
|
|
230
|
+
Source: `packages/tmdatagrid/docs/grouping.md` (Group rows).
|
|
228
231
|
|
|
229
232
|
### MEDIUM Totalling the page instead of the data
|
|
230
233
|
|
|
@@ -252,18 +255,22 @@ useTMDataGrid({
|
|
|
252
255
|
});
|
|
253
256
|
```
|
|
254
257
|
|
|
258
|
+
The built-in pager still greys out while `grouping` is set, so such a grid
|
|
259
|
+
renders its pager through `TMDataGrid.Footer`'s `renderPagination` slot and
|
|
260
|
+
ignores `state.isPagingActive`.
|
|
261
|
+
|
|
255
262
|
Source: `packages/tmdatagrid/docs/grouping.md` (Server-side grids).
|
|
256
263
|
|
|
257
264
|
## Reference
|
|
258
265
|
|
|
259
266
|
| Name | Kind | Type | Default | What it does |
|
|
260
267
|
| --- | --- | --- | --- | --- |
|
|
261
|
-
| `enableGrouping` | Table option | `boolean` | `true` | Group by and Ungroup menu items. Also a column option. |
|
|
268
|
+
| `enableGrouping` | Table option | `boolean` | `true`; `false` under `manualPagination` | Group by and Ungroup menu items, and the tree lane. Also a column option. |
|
|
262
269
|
| `groupedColumnMode` | Table option | `"reorder" \| "remove" \| false` | `"remove"` | Whether a grouped column leaves the grid or moves to the front. |
|
|
263
270
|
| `manualGrouping` | Table option | `boolean` | `false` | The rows arrive grouped. Required to group a server-paged grid. |
|
|
264
271
|
| `initialState.grouping` | Table option | `string[]` | `[]` | Column ids grouped at mount. A settings slice, so it persists. |
|
|
265
272
|
| `aggregationFn` | Column option | `TMDataGridAggregationName \| fn` | – | How a column fills in its group cells. Unset leaves them blank. |
|
|
266
|
-
| `aggregatedCell` | Column option | `(ctx) => ReactNode` |
|
|
273
|
+
| `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. |
|
|
267
274
|
| `footer` | Column option | `(ctx) => ReactNode` | – | Renders this column's summary cell. Defining one adds the row. |
|
|
268
275
|
| `aggregateColumn` | Export | `({ table, columnId, fn }) => unknown` | `fn: "sum"` | Aggregates over every filtered row, all pages. |
|
|
269
276
|
| `TMDataGridAggregationName` | Export | type | – | The registered function names. |
|
|
@@ -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.3.0'
|
|
19
19
|
sources:
|
|
20
20
|
- 'Jielga/react:packages/tmdatagrid/docs/migrating-to-2.md'
|
|
21
21
|
- 'Jielga/react:packages/tmdatagrid/src/index.ts'
|