@jielga/tmdatagrid 2.0.0-beta.13 → 2.0.0-beta.14
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 +4 -4
- package/dist/index.d.ts +24 -6
- package/dist/index.js +645 -636
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +1 -1
- package/skills/cell-selection/SKILL.md +1 -1
- package/skills/columns/SKILL.md +12 -11
- package/skills/data/SKILL.md +1 -1
- package/skills/editing/SKILL.md +1 -1
- package/skills/filtering/SKILL.md +27 -5
- package/skills/getting-started/SKILL.md +1 -1
- package/skills/grouping/SKILL.md +1 -1
- package/skills/options/SKILL.md +1 -1
- package/skills/rows/SKILL.md +1 -1
- package/skills/server-side/SKILL.md +129 -5
- package/skills/testing/SKILL.md +1 -1
- package/src/tmdatagrid/components/TMDataGrid.tsx +21 -20
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +5 -1
- package/src/tmdatagrid/components/TMDataGridHeaderFilterRow.tsx +2 -4
- package/src/tmdatagrid/components/TMDataGridMenu.tsx +11 -10
- package/src/tmdatagrid/core/columnUtils.ts +25 -5
- package/src/tmdatagrid/core/filterControls.ts +10 -1
- package/src/tmdatagrid/index.ts +1 -0
- package/src/tmdatagrid/useTMDataGrid.tsx +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jielga/tmdatagrid",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.14",
|
|
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",
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0-beta.
|
|
17
|
+
library_version: '2.0.0-beta.14'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/TMDataGrid:src/docs/cell-selection.md'
|
|
20
20
|
- 'Jielga/TMDataGrid:src/tmdatagrid/core/cellNavigation.ts'
|
package/skills/columns/SKILL.md
CHANGED
|
@@ -4,19 +4,19 @@ description: >
|
|
|
4
4
|
Define and arrange TMDataGrid columns. Covers createTMDataGridColumnHelper,
|
|
5
5
|
every column meta field (label, type, options, flex, align, autoSize,
|
|
6
6
|
enableOrdering, and the meta.filter and meta.edit namespaces holding
|
|
7
|
-
defaultOperator, control, enabled, field, editor, validate and
|
|
8
|
-
six column types, fluid minmax sizing versus fixed width with
|
|
9
|
-
maxSize / size, autosizing and autosizeColumn, hiding through
|
|
10
|
-
the columns panel, pinning and why a pinned column becomes
|
|
11
|
-
ordering with enableColumnOrdering, meta.enableOrdering,
|
|
12
|
-
moveColumnByStep, getStepTargetColumn and the pinned regions,
|
|
13
|
-
sorting with multi-sort through isMultiSortEvent and a custom
|
|
14
|
-
generated lanes. Load when adding or changing columns,
|
|
15
|
-
hiding, pinning, reordering or sorting them.
|
|
7
|
+
operators, defaultOperator, control, enabled, field, editor, validate and
|
|
8
|
+
mapValue), the six column types, fluid minmax sizing versus fixed width with
|
|
9
|
+
minSize / maxSize / size, autosizing and autosizeColumn, hiding through
|
|
10
|
+
enableHiding and the columns panel, pinning and why a pinned column becomes
|
|
11
|
+
fixed-width, ordering with enableColumnOrdering, meta.enableOrdering,
|
|
12
|
+
moveColumn, moveColumnByStep, getStepTargetColumn and the pinned regions,
|
|
13
|
+
resetSettings, sorting with multi-sort through isMultiSortEvent and a custom
|
|
14
|
+
sortFn, and the generated lanes. Load when adding or changing columns,
|
|
15
|
+
controlling widths, hiding, pinning, reordering or sorting them.
|
|
16
16
|
metadata:
|
|
17
17
|
type: core
|
|
18
18
|
library: '@jielga/tmdatagrid'
|
|
19
|
-
library_version: '2.0.0-beta.
|
|
19
|
+
library_version: '2.0.0-beta.14'
|
|
20
20
|
sources:
|
|
21
21
|
- 'Jielga/TMDataGrid:src/docs/columns.md'
|
|
22
22
|
- 'Jielga/TMDataGrid:src/docs/column-layout.md'
|
|
@@ -97,7 +97,8 @@ which is why they are in neither.
|
|
|
97
97
|
|
|
98
98
|
| Field | Type | Default | What it does |
|
|
99
99
|
| --- | --- | --- | --- |
|
|
100
|
-
| `
|
|
100
|
+
| `operators` | `readonly TMDataGridFilterOperator[]` | The type's list | The operators this column offers, a subset of its type's. For a backend that answers only some. |
|
|
101
|
+
| `defaultOperator` | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter on this column starts with. |
|
|
101
102
|
| `control` | `TMDataGridFilterControlComponent` | By `meta.type` | Replaces the value control in this column's filter row. Module scope. |
|
|
102
103
|
|
|
103
104
|
`meta.edit`:
|
package/skills/data/SKILL.md
CHANGED
package/skills/editing/SKILL.md
CHANGED
|
@@ -3,9 +3,10 @@ name: filtering
|
|
|
3
3
|
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
|
-
meta.type offers each, meta.filter.
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
meta.type offers each, meta.filter.operators to offer a column only a subset
|
|
7
|
+
of them, meta.filter.defaultOperator, isFilterActive and the half-typed
|
|
8
|
+
filter, the filters option and its surfaces (popup, sidebar, none, plus
|
|
9
|
+
inHeader for header filters), TMDataGrid.FilterPanel and its layout prop,
|
|
9
10
|
TMDataGrid.FilterButton, TMDataGrid.FilterPills with its api prop,
|
|
10
11
|
openColumnFilter, replacing a value control with DgRangeSliderFilter /
|
|
11
12
|
DgDateRangeFilter / DgAutocompleteFilter / DgTriStateFilter or a
|
|
@@ -17,7 +18,7 @@ description: >
|
|
|
17
18
|
metadata:
|
|
18
19
|
type: core
|
|
19
20
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '2.0.0-beta.
|
|
21
|
+
library_version: '2.0.0-beta.14'
|
|
21
22
|
sources:
|
|
22
23
|
- 'Jielga/TMDataGrid:src/docs/filtering.md'
|
|
23
24
|
- 'Jielga/TMDataGrid:src/docs/quick-search.md'
|
|
@@ -84,6 +85,25 @@ columnHelper.accessor("salary", {
|
|
|
84
85
|
});
|
|
85
86
|
```
|
|
86
87
|
|
|
88
|
+
`meta.filter.operators` narrows the list a column offers to a subset of its
|
|
89
|
+
type's - for a column whose backend answers only some operators, so the user is
|
|
90
|
+
never offered one the query cannot express. The panel dropdown and the header
|
|
91
|
+
funnel show only those, in the type's order. An operator the type does not
|
|
92
|
+
offer is ignored; a list that leaves nothing falls back to the type's full set.
|
|
93
|
+
Without `defaultOperator`, a fresh filter opens on the type's default when it is
|
|
94
|
+
offered, else on the first offered operator.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
columnHelper.accessor("customer", {
|
|
98
|
+
header: "Customer",
|
|
99
|
+
meta: { filter: { operators: ["contains", "equals", "isEmpty", "isNotEmpty"] } },
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`getColumnOperators(column)` returns the resolved list and
|
|
104
|
+
`getColumnDefaultOperator(column)` the operator a fresh filter opens on. The
|
|
105
|
+
`server-side` skill shows the list typed together with the API mapping table.
|
|
106
|
+
|
|
87
107
|
## The filters option
|
|
88
108
|
|
|
89
109
|
`filters` on `useTMDataGrid` picks the surface. It is read field by field, so a
|
|
@@ -400,7 +420,8 @@ Source: `src/docs/quick-search.md` (Fuzzy by default).
|
|
|
400
420
|
| `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the panel, the button and the menu item. |
|
|
401
421
|
| `enableColumnFilter` | Column option | `boolean` | `true` | `false` takes one column out of filtering. |
|
|
402
422
|
| `meta.type` | Column meta | `TMDataGridColumnType` | `"string"` | Selects the operators and the value control. |
|
|
403
|
-
| `meta.filter.
|
|
423
|
+
| `meta.filter.operators` | Column meta | `readonly TMDataGridFilterOperator[]` | The type's list | The operators this column offers, a subset of its type's. |
|
|
424
|
+
| `meta.filter.defaultOperator` | Column meta | `TMDataGridFilterOperator` | The type's default, else the first offered | The operator a fresh filter opens on. |
|
|
404
425
|
| `meta.filter.control` | Column meta | `TMDataGridFilterControlComponent` | By type and operator | Replaces the value control. Module scope. |
|
|
405
426
|
| `filterFn` | Column option | name or fn | `"tmDataGrid"` | Custom matching for one column. |
|
|
406
427
|
| `quickSearchMode` | Option | `"fuzzy" \| "contains"` | `"fuzzy"` | How the quick search matches. |
|
|
@@ -418,6 +439,7 @@ Source: `src/docs/quick-search.md` (Fuzzy by default).
|
|
|
418
439
|
| `isFilterActive` | Export | `(value) => boolean` | – | Whether a filter value narrows anything. |
|
|
419
440
|
| `activeColumnFilters` | Export | `(columnFilters \| table) => Array<{ id, value }>` | – | The filters in the grid's own value shape that narrow anything, typed. |
|
|
420
441
|
| `getOperatorsForType` | Export | `(type) => operators` | – | The operator list a type offers. |
|
|
442
|
+
| `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. |
|
|
421
443
|
| `FILTER_OPERATOR_LABELS` | Export | record | – | The label shown for each operator. |
|
|
422
444
|
| `formatFilterLabel` | Export | `({ label, type, filter }) => string` | – | The one-line description used on the pills. |
|
|
423
445
|
| `emptyValueForOperator` · `operatorNeedsValue` · `operatorTakesArrayValue` · `operatorTakesRangeValue` | Exports | – | – | What shape of value an operator expects. |
|
package/skills/grouping/SKILL.md
CHANGED
package/skills/options/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0-beta.
|
|
17
|
+
library_version: '2.0.0-beta.14'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
|
|
20
20
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
package/skills/rows/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ description: >
|
|
|
17
17
|
metadata:
|
|
18
18
|
type: core
|
|
19
19
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '2.0.0-beta.
|
|
20
|
+
library_version: '2.0.0-beta.14'
|
|
21
21
|
sources:
|
|
22
22
|
- 'Jielga/TMDataGrid:src/docs/row-selection.md'
|
|
23
23
|
- 'Jielga/TMDataGrid:src/docs/row-interaction.md'
|
|
@@ -4,16 +4,21 @@ description: >
|
|
|
4
4
|
Drive TMDataGrid from a server with TanStack manual modes - manualPagination,
|
|
5
5
|
manualSorting, manualFiltering, rowCount, controlled state and onXChange
|
|
6
6
|
callbacks. Covers the loading and totalRowCount meta fields, forwarding the
|
|
7
|
-
plain-JSON columnFilters model to an API with activeColumnFilters,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
plain-JSON columnFilters model to an API with activeColumnFilters, mapping
|
|
8
|
+
filters, sorting and the page index onto an endpoint's own query language
|
|
9
|
+
(field table, operator table, the three value shapes, meta.filter.operators
|
|
10
|
+
for an endpoint that answers only some operators, keying the fetch on the
|
|
11
|
+
request, paging against a page envelope), the first-page reset on a query
|
|
12
|
+
change, persistence interaction, and row selection across pages. Load when the
|
|
13
|
+
grid is backed by a paginated API rather than a local array, or when
|
|
14
|
+
translating grid filters into server-side queries.
|
|
11
15
|
metadata:
|
|
12
16
|
type: core
|
|
13
17
|
library: '@jielga/tmdatagrid'
|
|
14
|
-
library_version: '2.0.0-beta.
|
|
18
|
+
library_version: '2.0.0-beta.14'
|
|
15
19
|
sources:
|
|
16
20
|
- 'Jielga/TMDataGrid:src/docs/server-side.md'
|
|
21
|
+
- 'Jielga/TMDataGrid:src/docs/server-query.md'
|
|
17
22
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
|
18
23
|
---
|
|
19
24
|
|
|
@@ -124,6 +129,118 @@ value - a custom filter control writing raw values - is dropped.
|
|
|
124
129
|
|
|
125
130
|
Debounce requests. The filter value input updates on every keystroke.
|
|
126
131
|
|
|
132
|
+
## Mapping onto the endpoint's query language
|
|
133
|
+
|
|
134
|
+
An API takes a request body of its own: its own field names, its own operator
|
|
135
|
+
set, its own status codes, and pages counted from 1. The layer between the
|
|
136
|
+
grid's state and that body is one function over two lookup tables, plus one
|
|
137
|
+
function on the way back:
|
|
138
|
+
|
|
139
|
+
- **A field table** keyed by column id, giving the API field and the cast from
|
|
140
|
+
the string every filter control writes to the type the field holds
|
|
141
|
+
(`Number`, an enum code). A column missing from the table is one the API
|
|
142
|
+
cannot query: the mapping drops the filter rather than sending a field the
|
|
143
|
+
endpoint would reject.
|
|
144
|
+
- **An operator table** from `TMDataGridFilterOperator` to the API's operators.
|
|
145
|
+
Several grid operators collapse onto one - a date `before` and a number
|
|
146
|
+
`lessThan` are both `lt` once the value is cast. Declare it as a `Record`, not
|
|
147
|
+
a `Partial`, so an operator added by a later grid version fails the build
|
|
148
|
+
here rather than reaching the server unmapped.
|
|
149
|
+
- **`toRow`** from the API's record to the grid's row type.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const QUERY_FIELDS: Record<string, { field: string; cast: (raw: string) => string | number }> = {
|
|
153
|
+
id: { field: "orderRef", cast: Number },
|
|
154
|
+
amount: { field: "totalAmount", cast: Number },
|
|
155
|
+
status: { field: "status", cast: (raw) => STATUS_CODES[raw] ?? raw },
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const PREDICATE_OPS: Record<TMDataGridFilterOperator, PredicateOp> = {
|
|
159
|
+
contains: "like",
|
|
160
|
+
between: "range",
|
|
161
|
+
before: "lt",
|
|
162
|
+
lessThan: "lt",
|
|
163
|
+
isAnyOf: "in",
|
|
164
|
+
isEmpty: "isNull",
|
|
165
|
+
// ...one line for every remaining operator.
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### The three value shapes
|
|
170
|
+
|
|
171
|
+
`TMDataGridFilterValue` is `{ operator, value }`, and the operator decides what
|
|
172
|
+
`value` holds. Branch on all four cases, in this order:
|
|
173
|
+
|
|
174
|
+
| Operator | `value` | Sent as |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `isEmpty`, `isNotEmpty` | Not used | `{ field, op }` |
|
|
177
|
+
| `isAnyOf`, `isNoneOf` | `ReadonlyArray<string>` | `{ field, op, values }` |
|
|
178
|
+
| `between` | `[min, max]`, either end possibly `""` | `{ field, op, from?, to? }` |
|
|
179
|
+
| Everything else | `string` | `{ field, op, value }` |
|
|
180
|
+
|
|
181
|
+
An empty end of a `between` pair leaves that side open: an absent bound, not an
|
|
182
|
+
empty string. Run `activeColumnFilters` over the slice first so a half-typed
|
|
183
|
+
filter is not sent as a predicate that narrows the result to nothing.
|
|
184
|
+
|
|
185
|
+
### An endpoint that answers only some operators
|
|
186
|
+
|
|
187
|
+
Most endpoints do not have every operator the grid has - `like` and `eq` but no
|
|
188
|
+
prefix match is common. Do not offer what you would have to drop.
|
|
189
|
+
`meta.filter.operators` narrows the column to the operators the query can
|
|
190
|
+
express, and the mapping table is declared over exactly that list, so one
|
|
191
|
+
cannot be offered without a mapping or mapped without being offered:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const TEXT_OPERATORS = [
|
|
195
|
+
"contains",
|
|
196
|
+
"equals",
|
|
197
|
+
"isEmpty",
|
|
198
|
+
"isNotEmpty",
|
|
199
|
+
] as const satisfies readonly TMDataGridFilterOperator[];
|
|
200
|
+
|
|
201
|
+
const TEXT_OPS: Record<(typeof TEXT_OPERATORS)[number], PredicateOp> = {
|
|
202
|
+
contains: "like",
|
|
203
|
+
equals: "eq",
|
|
204
|
+
isEmpty: "isNull",
|
|
205
|
+
isNotEmpty: "isNotNull",
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
columnHelper.accessor("customer", {
|
|
209
|
+
header: "Customer",
|
|
210
|
+
meta: { filter: { operators: TEXT_OPERATORS } },
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
A fresh filter opens on `meta.filter.defaultOperator` when set, else on the
|
|
215
|
+
type's default when the list holds it, else on the first entry. The lookup at
|
|
216
|
+
the boundary still returns `undefined` for an unmapped operator: a filter
|
|
217
|
+
restored by `persist` from before the list was narrowed can carry one.
|
|
218
|
+
|
|
219
|
+
### Keying the fetch on the request
|
|
220
|
+
|
|
221
|
+
Serialize the request with `JSON.stringify` inside `useMemo` over
|
|
222
|
+
`columnFilters`, `sorting` and `pagination`, and key the fetch effect (or the
|
|
223
|
+
TanStack Query `queryKey`) on that string. Opening the panel and adding an
|
|
224
|
+
empty row moves `columnFilters` but leaves the request unchanged, so nothing is
|
|
225
|
+
sent. The effect owes the server a debounce (the value input updates on every
|
|
226
|
+
keystroke) and a cancel (a `cancelled` flag in the cleanup, or an
|
|
227
|
+
`AbortController` on a real `fetch`).
|
|
228
|
+
|
|
229
|
+
### Paging against a page envelope
|
|
230
|
+
|
|
231
|
+
| The API's | The grid's | Written as |
|
|
232
|
+
| --- | --- | --- |
|
|
233
|
+
| `page.number`, counted from 1 | `pagination.pageIndex`, counted from 0 | `number: pageIndex + 1` |
|
|
234
|
+
| `page.totalItems`, the matched count | `rowCount` | `rowCount: page?.totalItems ?? 0` |
|
|
235
|
+
| `page.totalPages` | `state.pageCount`, derived from `rowCount / pageSize` | Nothing; the grid computes it |
|
|
236
|
+
|
|
237
|
+
Forward `totalPages` only when the server pages by something other than the
|
|
238
|
+
size the grid asked for. `pageCount: -1` when the total is unknown. Show the
|
|
239
|
+
page number through the Footer's `renderPagination` slot with
|
|
240
|
+
`<Controls.PageSize /><Controls.PageNumber /><Controls.Pager />`.
|
|
241
|
+
`meta.totalRowCount` is the unfiltered total, which no filtered response
|
|
242
|
+
carries: take it from a separate count call.
|
|
243
|
+
|
|
127
244
|
## Persistence
|
|
128
245
|
|
|
129
246
|
`persist` works unchanged. `dataKey` restores filters, sorting and pagination
|
|
@@ -168,6 +285,13 @@ current page, so the grid renders the matched count alone rather than a
|
|
|
168
285
|
plausible-looking wrong total. Pass the unfiltered total as
|
|
169
286
|
`meta.totalRowCount` to get the "42 / 5000" form back.
|
|
170
287
|
|
|
288
|
+
### Offering operators the endpoint cannot answer
|
|
289
|
+
|
|
290
|
+
The panel offers every operator of the column's type, and a mapping that drops
|
|
291
|
+
`startsWith` leaves the user with a filter that silently does nothing. Declare
|
|
292
|
+
`meta.filter.operators` on the column with the operators the endpoint answers,
|
|
293
|
+
and type the operator table over that same list.
|
|
294
|
+
|
|
171
295
|
### Unstable getRowId across pages
|
|
172
296
|
|
|
173
297
|
Defaulting to the row index means row 0 of page 2 shares an id with row 0 of
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -11,7 +11,7 @@ description: >
|
|
|
11
11
|
metadata:
|
|
12
12
|
type: core
|
|
13
13
|
library: '@jielga/tmdatagrid'
|
|
14
|
-
library_version: '2.0.0-beta.
|
|
14
|
+
library_version: '2.0.0-beta.14'
|
|
15
15
|
sources:
|
|
16
16
|
- 'Jielga/TMDataGrid:src/docs/testing.md'
|
|
17
17
|
- 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGrid.tsx'
|
|
@@ -55,26 +55,7 @@ export type TMDataGridProps<TData extends RowData> = TMDataGridApi<TData> & {
|
|
|
55
55
|
"data-testid"?: string;
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
* Root of the grid. Takes the object returned by `useTMDataGrid` - spread it -
|
|
60
|
-
* and publishes it to the compound components below it:
|
|
61
|
-
*
|
|
62
|
-
* ```tsx
|
|
63
|
-
* const grid = useTMDataGrid({ data, columns });
|
|
64
|
-
*
|
|
65
|
-
* <TMDataGrid {...grid}>
|
|
66
|
-
* <TMDataGrid.Toolbar>
|
|
67
|
-
* <TMDataGrid.SummaryCount />
|
|
68
|
-
* <TMDataGrid.Spacer />
|
|
69
|
-
* <TMDataGrid.Menu>
|
|
70
|
-
* <TMDataGrid.Menu.Columns />
|
|
71
|
-
* </TMDataGrid.Menu>
|
|
72
|
-
* </TMDataGrid.Toolbar>
|
|
73
|
-
* <TMDataGrid.Table />
|
|
74
|
-
* <TMDataGrid.Footer />
|
|
75
|
-
* </TMDataGrid>
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
58
|
+
// Documented on the `TMDataGrid` export below.
|
|
78
59
|
function TMDataGridRoot<TData extends RowData>({
|
|
79
60
|
table,
|
|
80
61
|
ui,
|
|
@@ -150,6 +131,26 @@ function TMDataGridRoot<TData extends RowData>({
|
|
|
150
131
|
);
|
|
151
132
|
}
|
|
152
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Root of the grid. Takes the object returned by `useTMDataGrid` - spread it -
|
|
136
|
+
* and publishes it to the compound components below it:
|
|
137
|
+
*
|
|
138
|
+
* ```tsx
|
|
139
|
+
* const grid = useTMDataGrid({ data, columns });
|
|
140
|
+
*
|
|
141
|
+
* <TMDataGrid {...grid}>
|
|
142
|
+
* <TMDataGrid.Toolbar>
|
|
143
|
+
* <TMDataGrid.SummaryCount />
|
|
144
|
+
* <TMDataGrid.Spacer />
|
|
145
|
+
* <TMDataGrid.Menu>
|
|
146
|
+
* <TMDataGrid.Menu.Columns />
|
|
147
|
+
* </TMDataGrid.Menu>
|
|
148
|
+
* </TMDataGrid.Toolbar>
|
|
149
|
+
* <TMDataGrid.Table />
|
|
150
|
+
* <TMDataGrid.Footer />
|
|
151
|
+
* </TMDataGrid>
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
153
154
|
export const TMDataGrid = Object.assign(TMDataGridRoot, {
|
|
154
155
|
Toolbar: TMDataGridToolbar,
|
|
155
156
|
Spacer: TMDataGridToolbarSpacer,
|
|
@@ -5,6 +5,7 @@ import classes from "./TMDataGridFilterPanel.module.css";
|
|
|
5
5
|
import { useTMDataGridContext } from "../TMDataGridContext";
|
|
6
6
|
import {
|
|
7
7
|
getColumnDefaultOperator,
|
|
8
|
+
getColumnOperators,
|
|
8
9
|
getColumnLabel,
|
|
9
10
|
getColumnType,
|
|
10
11
|
} from "../core/columnUtils";
|
|
@@ -251,7 +252,10 @@ export function TMDataGridFilterPanel({
|
|
|
251
252
|
w={stacked ? "100%" : 170}
|
|
252
253
|
allowDeselect={false}
|
|
253
254
|
data-dg-part="filter-operator"
|
|
254
|
-
data={
|
|
255
|
+
data={(column
|
|
256
|
+
? getColumnOperators(column)
|
|
257
|
+
: getOperatorsForType(type)
|
|
258
|
+
).map((operator) => ({
|
|
255
259
|
value: operator,
|
|
256
260
|
label: labels.operators[operator],
|
|
257
261
|
}))}
|
|
@@ -7,7 +7,7 @@ import { useTMDataGridContext } from "../TMDataGridContext";
|
|
|
7
7
|
import {
|
|
8
8
|
getColumnDefaultOperator,
|
|
9
9
|
getColumnLabel,
|
|
10
|
-
|
|
10
|
+
getColumnOperators,
|
|
11
11
|
isControlColumn,
|
|
12
12
|
} from "../core/columnUtils";
|
|
13
13
|
import {
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
type TMDataGridFilterValue,
|
|
16
16
|
emptyValueForOperator,
|
|
17
17
|
filterValueShape,
|
|
18
|
-
getOperatorsForType,
|
|
19
18
|
isTMDataGridFilterValue,
|
|
20
19
|
} from "../core/filterOperators";
|
|
21
20
|
import { FilterIcon } from "./icons";
|
|
@@ -54,7 +53,6 @@ function HeaderFilterControl({ column }: { column: TMDataGridHeader["column"] })
|
|
|
54
53
|
(state) => state.columnFilters.find((filter) => filter.id === column.id)?.value,
|
|
55
54
|
);
|
|
56
55
|
|
|
57
|
-
const type = getColumnType(column);
|
|
58
56
|
const defaultOperator = getColumnDefaultOperator(column);
|
|
59
57
|
const current = isTMDataGridFilterValue(filterValue) ? filterValue : undefined;
|
|
60
58
|
const operator = current?.operator ?? defaultOperator;
|
|
@@ -104,7 +102,7 @@ function HeaderFilterControl({ column }: { column: TMDataGridHeader["column"] })
|
|
|
104
102
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
105
103
|
[table, column, facetKey],
|
|
106
104
|
);
|
|
107
|
-
const operators =
|
|
105
|
+
const operators = getColumnOperators(column);
|
|
108
106
|
|
|
109
107
|
return (
|
|
110
108
|
<>
|
|
@@ -44,16 +44,7 @@ export type TMDataGridMenuColumnsProps = {
|
|
|
44
44
|
searchable?: boolean;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
* Burger menu in the grid's top-right corner, holding whatever the consumer
|
|
49
|
-
* puts in it. Every Mantine `Menu` prop is accepted and wins over the defaults
|
|
50
|
-
* below.
|
|
51
|
-
*
|
|
52
|
-
* It always renders. It cannot know whether its children render anything, so
|
|
53
|
-
* a menu holding only `TMDataGrid.Menu.Columns` on a grid where nothing can be
|
|
54
|
-
* hidden still shows its button over an empty dropdown - leave the menu out of
|
|
55
|
-
* the toolbar in that case.
|
|
56
|
-
*/
|
|
47
|
+
// Documented on the `TMDataGridMenu` export below.
|
|
57
48
|
function TMDataGridMenuRoot({
|
|
58
49
|
children,
|
|
59
50
|
icon,
|
|
@@ -247,6 +238,16 @@ export function TMDataGridMenuResetLayout() {
|
|
|
247
238
|
);
|
|
248
239
|
}
|
|
249
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Burger menu in the grid's top-right corner, holding whatever the consumer
|
|
243
|
+
* puts in it. Every Mantine `Menu` prop is accepted and wins over the defaults
|
|
244
|
+
* below.
|
|
245
|
+
*
|
|
246
|
+
* It always renders. It cannot know whether its children render anything, so
|
|
247
|
+
* a menu holding only `TMDataGrid.Menu.Columns` on a grid where nothing can be
|
|
248
|
+
* hidden still shows its button over an empty dropdown - leave the menu out of
|
|
249
|
+
* the toolbar in that case.
|
|
250
|
+
*/
|
|
250
251
|
export const TMDataGridMenu = Object.assign(TMDataGridMenuRoot, {
|
|
251
252
|
Columns: TMDataGridMenuColumns,
|
|
252
253
|
ColumnToggles: TMDataGridMenuColumnToggles,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Row } from "@tanstack/react-table";
|
|
2
2
|
import {
|
|
3
3
|
getDefaultOperator,
|
|
4
|
+
getOperatorsForType,
|
|
4
5
|
type TMDataGridColumnType,
|
|
5
6
|
type TMDataGridFilterOperator,
|
|
6
7
|
} from "./filterOperators";
|
|
@@ -37,17 +38,36 @@ export function getColumnType(column: ColumnLike): TMDataGridColumnType {
|
|
|
37
38
|
return column.columnDef.meta?.type ?? "string";
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* The operators this column offers: the type's list, narrowed to
|
|
43
|
+
* `meta.filter.operators` when the column declares one. The type's order is
|
|
44
|
+
* kept so the menu reads the same on every column; an operator the type does
|
|
45
|
+
* not offer is dropped, and an allowlist that leaves nothing falls back to the
|
|
46
|
+
* type's full list rather than an empty menu.
|
|
47
|
+
*/
|
|
48
|
+
export function getColumnOperators(
|
|
49
|
+
column: ColumnLike,
|
|
50
|
+
): readonly TMDataGridFilterOperator[] {
|
|
51
|
+
const offered = getOperatorsForType(getColumnType(column));
|
|
52
|
+
const allowed = column.columnDef.meta?.filter?.operators;
|
|
53
|
+
if (!allowed) return offered;
|
|
54
|
+
const narrowed = offered.filter((operator) => allowed.includes(operator));
|
|
55
|
+
return narrowed.length > 0 ? narrowed : offered;
|
|
56
|
+
}
|
|
57
|
+
|
|
40
58
|
/**
|
|
41
59
|
* The operator a fresh filter on this column starts with -
|
|
42
|
-
* `meta.filter.defaultOperator`, else the type's default
|
|
60
|
+
* `meta.filter.defaultOperator`, else the type's default where the column
|
|
61
|
+
* offers it, else the first operator it does offer.
|
|
43
62
|
*/
|
|
44
63
|
export function getColumnDefaultOperator(
|
|
45
64
|
column: ColumnLike,
|
|
46
65
|
): TMDataGridFilterOperator {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
66
|
+
const declared = column.columnDef.meta?.filter?.defaultOperator;
|
|
67
|
+
if (declared) return declared;
|
|
68
|
+
const offered = getColumnOperators(column);
|
|
69
|
+
const typeDefault = getDefaultOperator(getColumnType(column));
|
|
70
|
+
return offered.includes(typeDefault) ? typeDefault : offered[0];
|
|
51
71
|
}
|
|
52
72
|
|
|
53
73
|
/**
|
|
@@ -86,10 +86,19 @@ export type TMDataGridFilterControlComponent =
|
|
|
86
86
|
* declaration of each feeds the filter panel and the cell editor alike.
|
|
87
87
|
*/
|
|
88
88
|
export type TMDataGridColumnFilterOptions = {
|
|
89
|
+
/**
|
|
90
|
+
* The operators this column offers, a subset of the type's own. For a
|
|
91
|
+
* column backed by an endpoint that answers only some of them - `contains`
|
|
92
|
+
* and `equals`, say - so the panel and the header funnel never offer an
|
|
93
|
+
* operator the query cannot express. Kept in the type's order; one the type
|
|
94
|
+
* does not offer is ignored, and a list that leaves nothing falls back to
|
|
95
|
+
* the type's full set.
|
|
96
|
+
*/
|
|
97
|
+
operators?: readonly TMDataGridFilterOperator[];
|
|
89
98
|
/**
|
|
90
99
|
* The operator a fresh filter on this column starts with, instead of the
|
|
91
100
|
* type's default - a salary column can open on `"between"`. Must be one of
|
|
92
|
-
* the
|
|
101
|
+
* the operators the column offers.
|
|
93
102
|
*/
|
|
94
103
|
defaultOperator?: TMDataGridFilterOperator;
|
|
95
104
|
/**
|
package/src/tmdatagrid/index.ts
CHANGED
|
@@ -186,8 +186,8 @@ export type TMDataGridColumnMeta = {
|
|
|
186
186
|
*/
|
|
187
187
|
enableOrdering?: boolean;
|
|
188
188
|
/**
|
|
189
|
-
* How this column filters: the operator a fresh
|
|
190
|
-
* value control the filter panel renders for it.
|
|
189
|
+
* How this column filters: which operators it offers, the operator a fresh
|
|
190
|
+
* filter starts with, and the value control the filter panel renders for it.
|
|
191
191
|
*
|
|
192
192
|
* ```tsx
|
|
193
193
|
* meta: {
|