@jielga/tmdatagrid 2.0.0-beta.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -212
- package/dist/index.d.ts +1323 -796
- package/dist/index.js +4719 -3193
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/adding-rows.md +132 -0
- package/docs/anatomy.md +119 -0
- package/docs/card-view.md +108 -0
- package/docs/cell-selection.md +194 -0
- package/docs/column-layout.md +182 -0
- package/docs/column-menu.md +66 -0
- package/docs/columns.md +268 -0
- package/docs/components.md +311 -0
- package/docs/draft-store.md +242 -0
- package/docs/editing.md +303 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +319 -0
- package/docs/filtering.md +362 -0
- package/docs/getting-started.md +123 -0
- package/docs/grouping.md +165 -0
- package/docs/loading-and-empty.md +92 -0
- package/docs/localization.md +79 -0
- package/docs/menu.md +143 -0
- package/docs/migrating-to-2.md +163 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +114 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +179 -0
- package/docs/quick-search.md +84 -0
- package/docs/row-details.md +115 -0
- package/docs/row-interaction.md +149 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +136 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +112 -0
- package/docs/server-query.md +246 -0
- package/docs/server-side.md +206 -0
- package/docs/sorting.md +101 -0
- package/docs/styling.md +126 -0
- package/docs/summary-row.md +76 -0
- package/docs/testing.md +744 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +22 -46
- package/skills/appearance/SKILL.md +72 -19
- package/skills/cell-selection/SKILL.md +69 -78
- package/skills/columns/SKILL.md +90 -34
- package/skills/data/SKILL.md +86 -16
- package/skills/editing/SKILL.md +83 -50
- package/skills/editing/references/common-mistakes.md +77 -69
- package/skills/editing/references/editing-api.md +31 -23
- package/skills/editing/references/editors-and-validation.md +24 -17
- package/skills/filtering/SKILL.md +148 -40
- package/skills/getting-started/SKILL.md +17 -15
- package/skills/grouping/SKILL.md +31 -16
- package/skills/options/SKILL.md +8 -8
- package/skills/rows/SKILL.md +22 -18
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +150 -32
- package/skills/testing-components/SKILL.md +230 -0
- package/skills/testing-editing/SKILL.md +240 -0
- package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +7 -19
- package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +7 -1
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +73 -7
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
- package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +9 -55
- package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +23 -63
- package/src/components/TMDataGridEntryRows.tsx +354 -0
- package/src/components/TMDataGridExportPicker.module.css +77 -0
- package/src/components/TMDataGridExportPicker.tsx +234 -0
- package/src/components/TMDataGridFilterPanel.module.css +54 -0
- package/src/components/TMDataGridFilterPanel.tsx +348 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +15 -8
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +9 -72
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +357 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +15 -53
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +88 -65
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +579 -165
- package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -0
- package/src/components/TMDataGridToolbar.tsx +181 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/editorShared.ts +16 -2
- package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
- package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
- package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
- package/src/components/filters/controlLayout.ts +32 -0
- package/src/components/filters/filterControlFor.ts +65 -0
- package/src/components/generatedColumns.tsx +187 -0
- package/src/{tmdatagrid/components → components}/icons.ts +1 -0
- package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
- package/src/components/useHideableColumns.ts +52 -0
- package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
- package/src/{tmdatagrid/core → core}/capabilities.ts +5 -5
- package/src/{tmdatagrid/core → core}/columnOptions.ts +60 -8
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/{tmdatagrid/core → core}/editEngine.ts +1172 -388
- package/src/{tmdatagrid/core → core}/editorFocus.ts +8 -4
- package/src/core/export.ts +704 -0
- package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
- package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
- package/src/core/filterSurface.ts +99 -0
- package/src/{tmdatagrid/core → core}/grouping.ts +21 -0
- package/src/{tmdatagrid/core → core}/labels.ts +51 -6
- package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
- package/src/core/pageReset.ts +120 -0
- package/src/core/pagination.ts +81 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/{tmdatagrid/core → core}/summary.ts +20 -4
- package/src/{tmdatagrid/index.ts → index.ts} +70 -36
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +534 -123
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +0 -298
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
- /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
- /package/src/{tmdatagrid/core → core}/controlledState.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
- /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
- /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
- /package/src/{tmdatagrid/core → core}/resizePreview.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
- /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
- /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Filtering
|
|
2
|
+
|
|
3
|
+
Per-column filters, built out of an operator and a value. Users add them from
|
|
4
|
+
the filter panel, the column headers or a column menu - `filters` decides
|
|
5
|
+
which. You control what each column offers through `meta.type`, and can
|
|
6
|
+
replace the value control when the default input is not the right one.
|
|
7
|
+
|
|
8
|
+
For one box across every column instead, see [Quick search](/docs/quick-search).
|
|
9
|
+
|
|
10
|
+
```demo
|
|
11
|
+
file: columns/Filtering.tsx
|
|
12
|
+
hint: Every column type offers its own operators - Salary opens on “between” because its meta says so.
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## How a filter is stored
|
|
16
|
+
|
|
17
|
+
All columns share one filter function. The operator lives *in the value*
|
|
18
|
+
rather than being selected through `filterFn`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
type TMDataGridFilterValue = {
|
|
22
|
+
operator: TMDataGridFilterOperator;
|
|
23
|
+
value: string | ReadonlyArray<string>;
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
So the filter model is plain JSON, and can be forwarded to a server without
|
|
28
|
+
transformation - dates as ISO strings, booleans as `"true"` / `"false"`, and an
|
|
29
|
+
array only under `isAnyOf` / `isNoneOf` (the set the cell is tested against) and
|
|
30
|
+
`between` (a `[min, max]` pair, an empty string leaving that end open). See
|
|
31
|
+
[Server-side data](/docs/server-side#sending-filters).
|
|
32
|
+
|
|
33
|
+
A filter with an empty value stays in state, so a panel row survives while the
|
|
34
|
+
user types. (A header filter control has no row to keep alive and drops its
|
|
35
|
+
entry instead - see [inHeader](#inheader).) It matches every row, does not set the header's filter
|
|
36
|
+
indicator, and produces no pill. `isFilterActive(value)` tests for that
|
|
37
|
+
state, and `activeColumnFilters` applies it across the whole slice, handing
|
|
38
|
+
back the entries that narrow the grid with their values typed as
|
|
39
|
+
`TMDataGridFilterValue` rather than as `unknown`. Entries in any other value
|
|
40
|
+
shape are dropped, so a custom control's raw filter values do not come back
|
|
41
|
+
from it.
|
|
42
|
+
|
|
43
|
+
## Operators
|
|
44
|
+
|
|
45
|
+
`meta.type` selects which operators a column offers.
|
|
46
|
+
|
|
47
|
+
| Operator | Label | Column type |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `contains` | contains | `string` |
|
|
50
|
+
| `equals` | equals | `string`, `number`, `boolean`, `date` |
|
|
51
|
+
| `notEquals` | does not equal | `string`, `number`, `boolean`, `date` |
|
|
52
|
+
| `startsWith` | starts with | `string` |
|
|
53
|
+
| `endsWith` | ends with | `string` |
|
|
54
|
+
| `greaterThan` | is greater than | `number` |
|
|
55
|
+
| `greaterThanOrEqual` | is greater than or equal to | `number` |
|
|
56
|
+
| `lessThan` | is less than | `number` |
|
|
57
|
+
| `lessThanOrEqual` | is less than or equal to | `number` |
|
|
58
|
+
| `between` | is between | `number`, `date` |
|
|
59
|
+
| `before` | is before | `date` |
|
|
60
|
+
| `after` | is after | `date` |
|
|
61
|
+
| `onOrBefore` | is on or before | `date` |
|
|
62
|
+
| `onOrAfter` | is on or after | `date` |
|
|
63
|
+
| `isAnyOf` | is any of | `select`, `multiSelect` |
|
|
64
|
+
| `isNoneOf` | is none of | `select`, `multiSelect` |
|
|
65
|
+
| `isEmpty` | is empty | every type |
|
|
66
|
+
| `isNotEmpty` | is not empty | every type |
|
|
67
|
+
|
|
68
|
+
String comparisons are case-insensitive. Date comparisons are by calendar day,
|
|
69
|
+
so `equals` on a `date` column matches the same day. On a `multiSelect`
|
|
70
|
+
column, whose cells hold arrays, `isAnyOf` is an intersection test and
|
|
71
|
+
`isNoneOf` its complement, and an empty cell array counts as empty for
|
|
72
|
+
`isEmpty`. `between` is inclusive at both ends; the panel renders a From/To
|
|
73
|
+
pair and either end may stay empty to leave the interval open on that side.
|
|
74
|
+
|
|
75
|
+
`meta.filter.defaultOperator` sets which one a fresh filter opens on: a salary
|
|
76
|
+
column can start on `between` rather than `equals`. It must be one of the
|
|
77
|
+
operators that type offers.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
columnHelper.accessor("salary", {
|
|
81
|
+
header: "Salary",
|
|
82
|
+
meta: { type: "number", filter: { defaultOperator: "between" } },
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`meta.filter.operators` narrows the list a column offers to a subset of its type's.
|
|
87
|
+
The panel's operator dropdown and the header row's funnel menu then show only those, in the type's order.
|
|
88
|
+
An operator the type does not offer is ignored, and a list that leaves nothing falls back to the type's full set.
|
|
89
|
+
Without `defaultOperator`, a fresh filter opens on the type's default when it is offered and on the first offered operator otherwise.
|
|
90
|
+
Declare it on a column whose backend answers only some operators, so the user is never offered one the query cannot express - see [A server-backed search](/docs/server-query#offering-only-what-the-endpoint-answers).
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
columnHelper.accessor("customer", {
|
|
94
|
+
header: "Customer",
|
|
95
|
+
meta: { filter: { operators: ["contains", "equals", "isEmpty", "isNotEmpty"] } },
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`getColumnOperators(column)` returns the resolved list, and `getColumnDefaultOperator(column)` the operator a fresh filter on it opens on.
|
|
100
|
+
|
|
101
|
+
## The filters option
|
|
102
|
+
|
|
103
|
+
`filters` on `useTMDataGrid` decides where the filter controls render.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
useTMDataGrid({ data, columns, filters: { surface: "sidebar", inHeader: true } });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Option | Type | Default | What it does |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `surface` | `"popup" \| "sidebar" \| "none"` | `"popup"` | The surface `TMDataGrid.Table` renders and `TMDataGrid.FilterButton` toggles. |
|
|
112
|
+
| `sidebarSide` | `"left" \| "right"` | `"right"` | Which side the sidebar sits on. Ignored by the other surfaces. |
|
|
113
|
+
| `sidebarWidth` | `string` | `"280px"` | Width of the sidebar, any CSS length. Ignored by the other surfaces. |
|
|
114
|
+
| `defaultOpen` | `boolean` | `true` under `"sidebar"`, `false` otherwise | Whether the surface starts open. Read once, at mount. Under `"none"`, the starting value of `ui.state.filterPanelOpen`. |
|
|
115
|
+
| `inHeader` | `boolean` | `false` | A second header row of per-column controls. Independent of `surface`. |
|
|
116
|
+
|
|
117
|
+
`surface` and `inHeader` are two separate choices, not one list: `inHeader` composes with all three surfaces.
|
|
118
|
+
`{ surface: "none", inHeader: true }` is header filters and nothing else; `{ inHeader: true }` keeps the popup as well, for the multi-column work a header row has no room for.
|
|
119
|
+
|
|
120
|
+
The option is read field by field, so a literal is fine - unlike `labels` and `persist`, it does not have to be referentially stable.
|
|
121
|
+
|
|
122
|
+
### surface: "popup"
|
|
123
|
+
|
|
124
|
+
The default.
|
|
125
|
+
The panel floats over the first body rows, anchored under the header.
|
|
126
|
+
A pointerdown outside closes it, so does Escape, and so does emptying it - by removing the last filter row or by **Clear all**.
|
|
127
|
+
`TMDataGrid.FilterButton` is exempt from the click-away, which is what keeps it a toggle.
|
|
128
|
+
Closing only hides the popup; the filters stay.
|
|
129
|
+
|
|
130
|
+
### surface: "sidebar"
|
|
131
|
+
|
|
132
|
+
The same panel beside the rows, inside the grid frame and under the toolbar.
|
|
133
|
+
It is a column of the frame rather than a layer over it: the rows give up the width instead of being covered, a click in the table does not dismiss it, and clearing the filters leaves it standing with its **Add filter** button.
|
|
134
|
+
Escape closes it.
|
|
135
|
+
It starts open (`defaultOpen` defaults to `true` here) and renders the panel with `layout="stacked"`, because 280px has no room for the side-by-side triple.
|
|
136
|
+
|
|
137
|
+
```demo
|
|
138
|
+
file: columns/FilterSidebar.tsx
|
|
139
|
+
hint: The funnel button in the toolbar closes the sidebar and gives the width back to the rows.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### surface: "none"
|
|
143
|
+
|
|
144
|
+
The grid renders no surface of its own and `TMDataGrid.FilterButton` renders nothing.
|
|
145
|
+
Use it for header filters alone, or to place the panel yourself - see [TMDataGrid.FilterPanel](#tmdatagridfilterpanel).
|
|
146
|
+
|
|
147
|
+
### inHeader
|
|
148
|
+
|
|
149
|
+
`inHeader: true` adds a second header row holding one value control per filterable column, always visible.
|
|
150
|
+
The row shares the column tracks and pinned lanes of the header above it, so resizing, reordering and pinning move each control with its column.
|
|
151
|
+
|
|
152
|
+
A header cell has room for a value and not much else, so the panel's column and operator dropdowns are not there: the column is the one the cell sits over, and the operator is a funnel button beside the input, tinted whenever the column is on anything but its default operator.
|
|
153
|
+
The control itself is the same one the panel would render, `meta.filter.control` included - it receives `layout: "header"` and drops its field label for an `aria-label`.
|
|
154
|
+
|
|
155
|
+
Two pieces of column chrome come off with header filters on: the column menu's **Filter** item and the funnel indicator on a filtered header.
|
|
156
|
+
Both existed only to reveal a control that is now always on screen; the filtered column's tinted title stays.
|
|
157
|
+
|
|
158
|
+
Clearing a header control removes the column's `columnFilters` entry rather than leaving an empty one behind - unless the user also picked a non-default operator, which is kept, being the part of the filter an empty control cannot show.
|
|
159
|
+
Panel rows still keep their empty filters; see [How a filter is stored](#how-a-filter-is-stored).
|
|
160
|
+
|
|
161
|
+
A narrow column clips its control.
|
|
162
|
+
Give a column that has to hold a date range or a multi-select a `minSize` wide enough for it.
|
|
163
|
+
|
|
164
|
+
`TMDataGrid.FilterButton` still toggles whatever `surface` names, and the panel it opens holds the same filters.
|
|
165
|
+
`openColumnFilter` does not: with `inHeader` on it always scrolls the column's header control into view and focuses it, leaving the surface closed.
|
|
166
|
+
|
|
167
|
+
```demo
|
|
168
|
+
file: columns/HeaderFilters.tsx
|
|
169
|
+
hint: Department offers its faceted values; the funnel beside an input changes that column's operator.
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## TMDataGrid.FilterButton
|
|
173
|
+
|
|
174
|
+
The toolbar button that toggles the filter surface, tinted with the count of active filters.
|
|
175
|
+
Opening an empty panel seeds a filter row on the first filterable column; with filters already in state it opens on those.
|
|
176
|
+
It renders nothing when no column can be filtered (`enableColumnFilters: false`), and nothing under `surface: "none"`, where there is no surface to toggle.
|
|
177
|
+
It is part of every demo on this page.
|
|
178
|
+
|
|
179
|
+
No props.
|
|
180
|
+
|
|
181
|
+
## TMDataGrid.FilterPanel
|
|
182
|
+
|
|
183
|
+
The panel of filter rows - one column / operator / value triple per filter, over an "Add filter" / "Clear all" footer.
|
|
184
|
+
It is 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.
|
|
185
|
+
It must be inside `<TMDataGrid>`, since it reads the grid from context, and it only reads and writes the table's `columnFilters` state, so a `manualFiltering` grid gets the same panel for free.
|
|
186
|
+
|
|
187
|
+
Pair it with `surface: "none"` so it is the only panel on the page, and drive it off `ui.state.filterPanelOpen` if it belongs behind a control of your own - `defaultOpen` sets where that state starts.
|
|
188
|
+
|
|
189
|
+
```demo
|
|
190
|
+
file: columns/FilterPanelPlaced.tsx
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
| Prop | Type | Default | Description |
|
|
194
|
+
| --- | --- | --- | --- |
|
|
195
|
+
| `layout` | `"row" \| "stacked"` | `"row"` | `"row"` lays the three fields side by side and wants about 550px. `"stacked"` puts them one under the other, each filling the width, for a drawer or a narrow column. Passed to every value control as its `layout`, so a `meta.filter.control` sizes itself to the same decision. |
|
|
196
|
+
| Mantine `BoxProps` | | – | Style props (`p`, `w`, …), `className` and `style`, set on the panel block. |
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
<Drawer opened={open} onClose={close}>
|
|
200
|
+
<TMDataGrid.FilterPanel layout="stacked" />
|
|
201
|
+
</Drawer>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## TMDataGrid.FilterPills
|
|
205
|
+
|
|
206
|
+
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).
|
|
207
|
+
Half-typed filters produce no pill.
|
|
208
|
+
The label spells the operator out unless it is the column type's default: `Age is greater than 30`, but `First name: Sofia`.
|
|
209
|
+
|
|
210
|
+
It takes the grid as an `api` prop rather than reading context, so active filters can live in a page header, above the toolbar, or anywhere else on the page.
|
|
211
|
+
It is also exported as `TMDataGridFilterPills`, for a file where nothing else touches `TMDataGrid`.
|
|
212
|
+
|
|
213
|
+
```demo
|
|
214
|
+
file: columns/FilterPills.tsx
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
| Prop | Type | Default | Description |
|
|
218
|
+
| --- | --- | --- | --- |
|
|
219
|
+
| `api` | `TMDataGridApi<TData>` | - | The object returned by `useTMDataGrid`. |
|
|
220
|
+
| `size` | `TMDataGridSize` | `"sm"` | Pill size. |
|
|
221
|
+
| `showClearAll` | `boolean` | `true` | "Clear all", shown once two filters are active. |
|
|
222
|
+
| `onPillClick` | `(columnId: string) => void` | - | Replaces the default click behaviour. |
|
|
223
|
+
| Mantine `BoxProps` | | - | Style props (`mb`, `hiddenFrom`, …), `className` and `style`, set on the wrapper. |
|
|
224
|
+
|
|
225
|
+
## openColumnFilter
|
|
226
|
+
|
|
227
|
+
`openColumnFilter(api, columnId)` sends the user to a column's filter control - what a pill's label and the column menu's **Filter** item both call, available for a control of your own.
|
|
228
|
+
It seeds an empty filter on the column if it has none yet, then opens the surface on that column's panel row.
|
|
229
|
+
Under `filters.inHeader` it instead scrolls the column's header control into view and focuses it, leaving the surface closed.
|
|
230
|
+
|
|
231
|
+
## Replacing the value control
|
|
232
|
+
|
|
233
|
+
The built-in control follows the type and operator: a text input for strings, a
|
|
234
|
+
number pair for `between`, a native date input for dates, a Yes/No dropdown for
|
|
235
|
+
booleans, a multi-select for option columns.
|
|
236
|
+
|
|
237
|
+
Four ready-made alternatives ship as named exports:
|
|
238
|
+
|
|
239
|
+
| Export | For | Renders |
|
|
240
|
+
| --- | --- | --- |
|
|
241
|
+
| `DgRangeSliderFilter` | `number` | A range slider seeded from the data's min/max, writing the `between` pair. |
|
|
242
|
+
| `DgDateRangeFilter` | `date` | A From/To pair of native date inputs, writing the `between` pair. |
|
|
243
|
+
| `DgAutocompleteFilter` | `string` | Free text with the faceted (or declared) values as suggestions. |
|
|
244
|
+
| `DgTriStateFilter` | `boolean` | All / Yes / No segments - All clears the filter. |
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
meta: {
|
|
248
|
+
type: "number",
|
|
249
|
+
filter: { control: DgRangeSliderFilter, defaultOperator: "between" },
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Pair the range-shaped ones with `defaultOperator: "between"` so the filter
|
|
254
|
+
opens on them. For an operator they do not cover, every built-in falls back to
|
|
255
|
+
`TMDataGridFilterValueInput`, the default control, which is exported so custom
|
|
256
|
+
controls can fall back the same way.
|
|
257
|
+
|
|
258
|
+
```demo
|
|
259
|
+
file: columns/BuiltInFilterControls.tsx
|
|
260
|
+
hint: Open the filter panel and compare each row's control with the plain input the other demos show.
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Writing your own
|
|
264
|
+
|
|
265
|
+
`meta.filter.control` is a **component**, rendered as JSX, so hooks may be used
|
|
266
|
+
inside. It receives `TMDataGridFilterControlArgs` and handles the value only:
|
|
267
|
+
it reads `operator` to shape itself and writes the bare value through
|
|
268
|
+
`onChange`, and the grid stores the `{ operator, value }` pair around it. The
|
|
269
|
+
column and operator dropdowns remain the panel's.
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
const SalaryFilter: TMDataGridFilterControlComponent = ({
|
|
273
|
+
operator,
|
|
274
|
+
value,
|
|
275
|
+
onChange,
|
|
276
|
+
}) =>
|
|
277
|
+
operator === "between" ? (
|
|
278
|
+
<RangeSlider /* value is the [min, max] pair */ />
|
|
279
|
+
) : (
|
|
280
|
+
<NumberInput /* a single bound */ />
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
meta: { filter: { control: SalaryFilter, defaultOperator: "between" } }
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Define controls at module scope so their identity is stable. `args.options`
|
|
287
|
+
arrives pre-resolved through `resolveColumnOptions` for a column that declares
|
|
288
|
+
`meta.options` or is select-shaped. `args.table` is available to a control that
|
|
289
|
+
needs more than that.
|
|
290
|
+
|
|
291
|
+
`args.layout` says how much room the control has and whether it has to name
|
|
292
|
+
itself:
|
|
293
|
+
|
|
294
|
+
| `layout` | Where | The field |
|
|
295
|
+
| --- | --- | --- |
|
|
296
|
+
| `"row"` | A panel row laid out side by side | Labelled, fixed width |
|
|
297
|
+
| `"stacked"` | A panel row in a narrow host - the sidebar | Labelled, fills the width |
|
|
298
|
+
| `"header"` | One header cell, under `filters.inHeader` | No label; `aria-label` instead, fills the column |
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
const SalaryFilter: TMDataGridFilterControlComponent = ({ layout, ...rest }) =>
|
|
302
|
+
layout === "header" ? <NumberInput {...} /> : <RangeSlider {...} />;
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
A control that ignores `layout` still works - it will simply look the same
|
|
306
|
+
everywhere, which is fine until it has to fit a header cell. Every built-in
|
|
307
|
+
control honours it.
|
|
308
|
+
|
|
309
|
+
```demo
|
|
310
|
+
file: columns/CustomFilterControl.tsx
|
|
311
|
+
hint: Open the filter panel - Status offers chips, every other column the built-in control.
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Custom matching
|
|
315
|
+
|
|
316
|
+
To give a column its own matching logic instead of its own control, set
|
|
317
|
+
`filterFn` on the column definition. The grid provides only `"tmDataGrid"`,
|
|
318
|
+
which is the default.
|
|
319
|
+
|
|
320
|
+
## Turning it off
|
|
321
|
+
|
|
322
|
+
`enableColumnFilters: false` removes the filter menu item, the `FilterButton`,
|
|
323
|
+
the panel and the header filter row. `enableColumnFilter: false` on a column
|
|
324
|
+
removes that column's menu item, its entry in the panel's column list and its
|
|
325
|
+
header control - the header cell stays, empty.
|
|
326
|
+
|
|
327
|
+
## Reference
|
|
328
|
+
|
|
329
|
+
| Name | Kind | Type | Default | What it does |
|
|
330
|
+
| --- | --- | --- | --- | --- |
|
|
331
|
+
| `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the panel, the button, the header row and the menu item. |
|
|
332
|
+
| `filters.surface` | Table option | `"popup" \| "sidebar" \| "none"` | `"popup"` | Which surface the table renders. |
|
|
333
|
+
| `filters.sidebarSide` | Table option | `"left" \| "right"` | `"right"` | Which side the sidebar sits on. |
|
|
334
|
+
| `filters.sidebarWidth` | Table option | `string` | `"280px"` | Width of the sidebar. |
|
|
335
|
+
| `filters.defaultOpen` | Table option | `boolean` | `true` under `"sidebar"` | Whether the surface starts open. |
|
|
336
|
+
| `filters.inHeader` | Table option | `boolean` | `false` | A second header row of per-column controls. |
|
|
337
|
+
| `enableColumnFilter` | Column option | `boolean` | `true` | `false` takes one column out of filtering. |
|
|
338
|
+
| `meta.type` | Column meta | `"string" \| "number" \| "boolean" \| "date" \| "select" \| "multiSelect"` | `"string"` | Selects the operators and the value control. |
|
|
339
|
+
| `meta.filter.operators` | Column meta | `readonly TMDataGridFilterOperator[]` | The type's list | The operators this column offers, a subset of its type's. |
|
|
340
|
+
| `meta.filter.defaultOperator` | Column meta | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter opens on. |
|
|
341
|
+
| `meta.filter.control` | Column meta | `TMDataGridFilterControlComponent` | By type and operator | Replaces the value control. |
|
|
342
|
+
| `filterFn` | Column option | name \| fn | `"tmDataGrid"` | Custom matching for one column. |
|
|
343
|
+
| `TMDataGrid.FilterPanel` | Component | `layout: "row" \| "stacked"` | `"row"` | The panel of filter rows, as a plain block. |
|
|
344
|
+
| `TMDataGrid.FilterButton` | Component | – | – | Toolbar button toggling the surface, with an active count. Seeds a filter row only when the panel is empty. |
|
|
345
|
+
| `TMDataGrid.FilterPills` | Component | takes `api` | – | Active filters as removable pills, renderable anywhere. |
|
|
346
|
+
| `TMDataGridFilterPillsProps` | Type | – | – | The props of `TMDataGrid.FilterPills`. |
|
|
347
|
+
| `openColumnFilter` | Export | `(api, columnId) => void` | – | Sends the user to a column's filter control, seeding an empty filter. |
|
|
348
|
+
| `TMDataGridFilterControlArgs` | Type | `layout: "row" \| "stacked" \| "header"` | – | What a value control is handed, `layout` saying how much room it has. |
|
|
349
|
+
| `TMDataGridFilterControlLayout` | Type | `"row" \| "stacked" \| "header"` | – | The type of `layout` on `TMDataGridFilterControlArgs`. |
|
|
350
|
+
| `isFilterActive` | Export | `(value) => boolean` | – | Whether a filter value narrows anything. |
|
|
351
|
+
| `activeColumnFilters` | Export | `(columnFilters \| table) => Array<{ id, value }>` | – | The filters in the grid's own value shape that narrow anything, typed. |
|
|
352
|
+
| `TMDataGridColumnFilter` | Type | `{ id, value }` | – | One entry of `columnFilters`, typed. What `activeColumnFilters` returns a list of. |
|
|
353
|
+
| `getOperatorsForType` | Export | `(type) => operators` | – | The operator list a type offers. |
|
|
354
|
+
| `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. |
|
|
355
|
+
| `FILTER_OPERATOR_LABELS` | Export | record | – | The label shown for each operator. |
|
|
356
|
+
| `TMDataGridFilterValueInput` | Export | component | – | The default value control, for falling back to. |
|
|
357
|
+
| `formatFilterLabel` | Export | `({ label, type, filter }) => string` | – | The one-line description used on the pills. |
|
|
358
|
+
| `emptyValueForOperator` · `operatorNeedsValue` · `operatorTakesArrayValue` · `operatorTakesRangeValue` · `filterValueShape` | Exports | – | – | What shape of value an operator expects. |
|
|
359
|
+
| `TMDataGridFilterValueShape` | Type | `"scalar" \| "set" \| "range"` | – | What `filterValueShape` returns for an operator. |
|
|
360
|
+
| `TMDataGridFiltersOptions` · `TMDataGridFiltersSettings` · `TMDataGridFilterSurface` · `TMDataGridFilterSidebarSide` | Types | – | – | The `filters` option, and the resolved form of it on `api.filters`. |
|
|
361
|
+
| `TMDataGridFilterPanelProps` · `TMDataGridFilterPanelLayout` | Types | – | – | For wrapping `TMDataGrid.FilterPanel` in a component of your own. |
|
|
362
|
+
| `DgRangeSliderFilter` · `DgDateRangeFilter` · `DgAutocompleteFilter` · `DgTriStateFilter` | Exports | components | – | The four ready-made controls. |
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
A React data grid built on TanStack Table v9 and Mantine. Always virtualized,
|
|
4
|
+
with resizable, reorderable, sortable, filterable, hideable and pinnable
|
|
5
|
+
columns.
|
|
6
|
+
|
|
7
|
+
`useTMDataGrid` creates the table, `TMDataGrid` provides it through context, and
|
|
8
|
+
the parts you render inside read what they need from that context.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @jielga/tmdatagrid
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Peer dependencies: `react` and `react-dom` (19.1 or later), `@mantine/core`,
|
|
17
|
+
`@tanstack/react-table` (v9), `@tanstack/react-store`, `@tanstack/store`,
|
|
18
|
+
`@tanstack/react-virtual` and `@tabler/icons-react`. Editing adds
|
|
19
|
+
`@tanstack/react-form`.
|
|
20
|
+
|
|
21
|
+
The grid must be rendered inside a Mantine `MantineProvider`.
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import "@jielga/tmdatagrid/styles.css";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Import it once. A layered stylesheet is also published; see
|
|
28
|
+
[Styling](/docs/styling#the-stylesheet).
|
|
29
|
+
|
|
30
|
+
> **TanStack Table v9 is still in beta.** The grid is built against
|
|
31
|
+
> `^9.0.0-beta.21` and uses its feature-registry API, which beta releases may
|
|
32
|
+
> change without a major bump. Pin `@tanstack/react-table` and
|
|
33
|
+
> `@tanstack/table-core` to an exact version if you need reproducible installs.
|
|
34
|
+
|
|
35
|
+
## Your first grid
|
|
36
|
+
|
|
37
|
+
```demo
|
|
38
|
+
file: getting-started/Minimal.tsx
|
|
39
|
+
extraSources: data/employees.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Rows are virtualized by default.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import {
|
|
46
|
+
createTMDataGridColumnHelper,
|
|
47
|
+
TMDataGrid,
|
|
48
|
+
useTMDataGrid,
|
|
49
|
+
} from "@jielga/tmdatagrid";
|
|
50
|
+
|
|
51
|
+
type Employee = {
|
|
52
|
+
id: number;
|
|
53
|
+
firstName: string;
|
|
54
|
+
lastName: string;
|
|
55
|
+
department: string;
|
|
56
|
+
salary: number;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const columnHelper = createTMDataGridColumnHelper<Employee>();
|
|
60
|
+
|
|
61
|
+
const columns = columnHelper.columns([
|
|
62
|
+
columnHelper.accessor("firstName", { header: "First name" }),
|
|
63
|
+
columnHelper.accessor("lastName", { header: "Last name" }),
|
|
64
|
+
columnHelper.accessor("department", { header: "Department" }),
|
|
65
|
+
columnHelper.accessor("salary", {
|
|
66
|
+
header: "Salary",
|
|
67
|
+
meta: { type: "number", align: "right" },
|
|
68
|
+
}),
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
export function Employees({ data }: { data: Employee[] }) {
|
|
72
|
+
const grid = useTMDataGrid({
|
|
73
|
+
data,
|
|
74
|
+
columns,
|
|
75
|
+
getRowId: (row) => String(row.id),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<TMDataGrid {...grid} style={{ flex: 1, minHeight: 0 }}>
|
|
80
|
+
<TMDataGrid.Table<Employee> />
|
|
81
|
+
</TMDataGrid>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Define `columns` at module scope. A new array on every render rebuilds the
|
|
87
|
+
column model and resets the user's column widths and order.
|
|
88
|
+
|
|
89
|
+
Give the grid a bounded height: `style={{ flex: 1, minHeight: 0 }}` inside a
|
|
90
|
+
flex parent, or a fixed height. See [Layout](/docs/styling#layout).
|
|
91
|
+
|
|
92
|
+
## Adding a toolbar and footer
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<TMDataGrid {...grid}>
|
|
96
|
+
<TMDataGrid.Toolbar>
|
|
97
|
+
<TMDataGrid.SummaryCount />
|
|
98
|
+
<TMDataGrid.Menu>
|
|
99
|
+
<TMDataGrid.Menu.Columns />
|
|
100
|
+
</TMDataGrid.Menu>
|
|
101
|
+
</TMDataGrid.Toolbar>
|
|
102
|
+
<TMDataGrid.Table />
|
|
103
|
+
<TMDataGrid.Footer pageSizeOptions={[10, 25, 50]} />
|
|
104
|
+
</TMDataGrid>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```demo
|
|
108
|
+
file: getting-started/ToolbarAndFooter.tsx
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`Toolbar` and `Footer` are ordinary composition. See
|
|
112
|
+
[Grid anatomy](/docs/anatomy) for what each part is, and
|
|
113
|
+
[Toolbar](/docs/toolbar) for adding your own buttons among them.
|
|
114
|
+
|
|
115
|
+
## Where to go next
|
|
116
|
+
|
|
117
|
+
- **[Defining columns](/docs/columns)** - accessors, `meta.type`, and what each
|
|
118
|
+
type configures.
|
|
119
|
+
- **[Grid anatomy](/docs/anatomy)** - the hook's return value, and every
|
|
120
|
+
component you can render.
|
|
121
|
+
- **[Editing](/docs/editing)** - three modes, from single cells to a whole row.
|
|
122
|
+
- **[Server-side data](/docs/server-side)** - when the server does the work.
|
|
123
|
+
- **[The playground](/playground)** - every feature at once, behind switches.
|
package/docs/grouping.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Grouping
|
|
2
|
+
|
|
3
|
+
Grouping collapses the rows into a tree: one row per distinct value, with the
|
|
4
|
+
records that share it folded underneath.
|
|
5
|
+
|
|
6
|
+
It is on by default. Nothing changes until a column is grouped, which users do
|
|
7
|
+
from **Group by …** in any column menu.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
const grid = useTMDataGrid({ data, columns });
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
To open already grouped, seed the state:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
const grid = useTMDataGrid({
|
|
17
|
+
data,
|
|
18
|
+
columns,
|
|
19
|
+
initialState: { grouping: ["department"] },
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```demo
|
|
24
|
+
file: rows/Grouping.tsx
|
|
25
|
+
hint: “Group by …” lives in every column menu. Group by Location as well and the tree nests.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Grouping is not hierarchical data
|
|
29
|
+
|
|
30
|
+
The tree here is built from column values. It is not the same thing as data
|
|
31
|
+
that is already a tree, which in TanStack is `getSubRows`.
|
|
32
|
+
|
|
33
|
+
`getSubRows` is TanStack's own option and the grid passes it through: the
|
|
34
|
+
nested rows reach the row model, render with `data-depth`, and count in the
|
|
35
|
+
[summary row](/docs/summary-row) alongside their parents. What the grid does
|
|
36
|
+
not add is any UI for them. There is no expander control on a parent row, and
|
|
37
|
+
`expanded` is the state [row details](/docs/row-details) uses, so a grid with
|
|
38
|
+
`renderDetails` set opens a panel where you would want children. Drive the
|
|
39
|
+
expansion from your own control and your own `expanded` state, or flatten the
|
|
40
|
+
data before it reaches the grid.
|
|
41
|
+
|
|
42
|
+
## What grouping does to the grid
|
|
43
|
+
|
|
44
|
+
Grouping a column **removes it**, since its values have moved into the tree
|
|
45
|
+
lane, and a generated **Group** column appears at the front, pinned beside the
|
|
46
|
+
checkbox lane. Each group row shows its value, how many records are under it,
|
|
47
|
+
and a chevron. Group again from a second column's menu to nest.
|
|
48
|
+
|
|
49
|
+
Because a grouped column is no longer in the grid, **Ungroup** lives on the
|
|
50
|
+
tree column's menu, one item per grouped column. **Expand all groups** and
|
|
51
|
+
**Collapse all groups** are in every column menu while a grouping is active.
|
|
52
|
+
|
|
53
|
+
To keep a grouped column in the grid instead of removing it, pass
|
|
54
|
+
`groupedColumnMode: "reorder"`, which is TanStack's own default and moves
|
|
55
|
+
grouped columns to the front. Note that the kept column's data cells render as
|
|
56
|
+
TanStack's grouped-cell placeholder - blank - with the value only on group
|
|
57
|
+
rows, so it repeats what the tree lane already shows and cannot be typed into.
|
|
58
|
+
To set the grouped field on a new row, seed it through `edit.addRow(values)` -
|
|
59
|
+
see [Editing](/docs/adding-rows#adding-rows).
|
|
60
|
+
|
|
61
|
+
## Aggregation
|
|
62
|
+
|
|
63
|
+
Off by default. A group row leaves every cell blank except the tree lane. Give
|
|
64
|
+
a column an `aggregationFn` and its group cells fill in.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
columnHelper.accessor("salary", {
|
|
68
|
+
header: "Salary",
|
|
69
|
+
aggregationFn: "sum",
|
|
70
|
+
meta: { type: "number", align: "right" },
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`"sum"`, `"min"`, `"max"`, `"extent"`, `"mean"`, `"median"`, `"unique"`,
|
|
75
|
+
`"uniqueCount"` and `"count"` are registered, as is `"auto"` - which picks
|
|
76
|
+
`sum` for numbers and `extent` for dates. A function is accepted too, with
|
|
77
|
+
TanStack's signature `(columnId, leafRows, childRows)`; `leafRows` are the
|
|
78
|
+
group's data rows, each record on `row.original`. Pass
|
|
79
|
+
`aggregatedCell` to render the group row's value differently from the data
|
|
80
|
+
rows.
|
|
81
|
+
|
|
82
|
+
> TanStack's grouping feature defaults every column to `aggregationFn: "auto"`.
|
|
83
|
+
> The grid clears that default so grouping does not silently start summing
|
|
84
|
+
> numeric columns. Setting `aggregationFn: "auto"` yourself restores it.
|
|
85
|
+
|
|
86
|
+
For a total across the whole grid rather than per group, give the column a
|
|
87
|
+
`footer` instead. See [Summary row](/docs/summary-row).
|
|
88
|
+
|
|
89
|
+
### Sorting a grouped grid
|
|
90
|
+
|
|
91
|
+
Grouping runs before sorting, so sorting sorts the rows *inside* each group and
|
|
92
|
+
orders the groups by their aggregated value. A column with no aggregation has
|
|
93
|
+
no value on a group row, so sorting on it reorders the rows within each group
|
|
94
|
+
but leaves the groups where they are.
|
|
95
|
+
|
|
96
|
+
## Selection
|
|
97
|
+
|
|
98
|
+
A group row's checkbox selects every record under it, at any depth, including
|
|
99
|
+
records inside collapsed sub-groups. It shows a tick once all of them are
|
|
100
|
+
selected and a dash while only some are.
|
|
101
|
+
|
|
102
|
+
Only the records are written to `rowSelection`. A group row is never in it, so
|
|
103
|
+
`getSelectedRowModel()` and the toolbar count do not depend on how the tree is
|
|
104
|
+
arranged.
|
|
105
|
+
|
|
106
|
+
Under `enableMultiRowSelection: false` group rows carry no checkbox.
|
|
107
|
+
|
|
108
|
+
## Group rows
|
|
109
|
+
|
|
110
|
+
A group row is built on its first child's record rather than on one of its own,
|
|
111
|
+
so it does not fire `onRowClick`, cannot be highlighted, cannot be
|
|
112
|
+
[pinned](/docs/row-pinning) and has no details panel.
|
|
113
|
+
|
|
114
|
+
`rowStyle` and `rowClassName` are the exception: they are called for group rows too, with that same child's record as `original`.
|
|
115
|
+
Guard a callback that reads `original` with `row.getIsGrouped()`, and set `--dg-row-group-bg` to colour the group rows themselves.
|
|
116
|
+
|
|
117
|
+
`data-grouped` is present, with the value `"true"`, only on group rows, so
|
|
118
|
+
`[data-grouped]` and `[data-grouped="true"]` are equivalent. `data-depth`
|
|
119
|
+
carries the nesting level, and `--dg-row-group-bg` sets a group row's
|
|
120
|
+
background.
|
|
121
|
+
|
|
122
|
+
## Grouping and pagination
|
|
123
|
+
|
|
124
|
+
While a column is grouped the grid renders the whole tree and relies on
|
|
125
|
+
virtualization. `TMDataGrid.Footer` greys its pager out and replaces the range
|
|
126
|
+
with `Grouped · all N rows`. Ungroup and paging resumes where it left off.
|
|
127
|
+
|
|
128
|
+
To page a grouped grid, group and page on the server: feed the grid one page of
|
|
129
|
+
a tree at a time with `manualPagination` and `manualGrouping`.
|
|
130
|
+
|
|
131
|
+
`isPagingActive(table, features)` is exported, so a custom pager can grey itself
|
|
132
|
+
out the same way:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
<TMDataGrid.Footer
|
|
136
|
+
renderPagination={({ state, actions }) => (
|
|
137
|
+
<MyPager {...state} {...actions} disabled={!state.isPagingActive} />
|
|
138
|
+
)}
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Server-side grids
|
|
143
|
+
|
|
144
|
+
`manualPagination: true` turns grouping off: the client holds one page, and
|
|
145
|
+
grouping it would build groups out of an arbitrary slice. A grid that groups
|
|
146
|
+
server-side can set `enableGrouping: true` alongside `manualGrouping: true`. See
|
|
147
|
+
[Server-side data](/docs/server-side).
|
|
148
|
+
|
|
149
|
+
## Reference
|
|
150
|
+
|
|
151
|
+
| Name | Kind | Type | Default | What it does |
|
|
152
|
+
| --- | --- | --- | --- | --- |
|
|
153
|
+
| `enableGrouping` | Table option | `boolean` | `true` | Group by and Ungroup menu items. Also a column option. |
|
|
154
|
+
| `groupedColumnMode` | Table option | `"reorder" \| "remove" \| false` | `"remove"` | Whether a grouped column leaves the grid or moves to the front. |
|
|
155
|
+
| `manualGrouping` | Table option | `boolean` | `false` | The rows arrive grouped. Required to group a server-paged grid. |
|
|
156
|
+
| `initialState.grouping` | Table option | `string[]` | `[]` | Column ids to group on at mount. A settings slice, so it persists. |
|
|
157
|
+
| `aggregationFn` | Column option | `TMDataGridAggregationName \| fn` | – | How a column fills in its group cells. Unset leaves them blank. |
|
|
158
|
+
| `aggregatedCell` | Column option | `(ctx) => ReactNode` | The `cell` renderer | Renders a group row's value differently from a data row's. |
|
|
159
|
+
| `GROUP_COLUMN_ID` | Export | `"__group__"` | – | Id of the generated tree column. |
|
|
160
|
+
| `formatGroupValue` | Export | `(value) => string` | – | How the tree lane renders a group's value. |
|
|
161
|
+
| `getGroupDataRows` | Export | `(row) => Row[]` | – | Every record under a group row, at any depth. |
|
|
162
|
+
| `isPagingActive` | Export | `(table, features) => boolean` | – | Whether the pager is slicing anything. `false` while grouped. |
|
|
163
|
+
| `--dg-row-group-bg` | CSS variable | colour | Themed | Group row background. |
|
|
164
|
+
| `data-grouped` | Data attribute | `"true"` | – | `"true"` on group rows. Absent on the rest. |
|
|
165
|
+
| `data-depth` | Data attribute | `number` | – | Nesting level, on every row. |
|