@jielga/tmdatagrid 1.1.0 → 2.0.0-beta.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 +184 -75
- package/dist/index.js +1821 -1743
- 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 +42 -17
- package/skills/data/SKILL.md +1 -1
- package/skills/editing/SKILL.md +56 -56
- package/skills/editing/references/editing-api.md +8 -5
- package/skills/editing/references/editors-and-validation.md +55 -7
- package/skills/filtering/SKILL.md +13 -14
- 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 +10 -5
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +2 -2
- package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +49 -16
- package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +42 -11
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +5 -3
- package/src/tmdatagrid/components/TMDataGridTable.tsx +107 -37
- package/src/tmdatagrid/components/editors/TMDataGridBooleanEditor.tsx +0 -2
- package/src/tmdatagrid/components/editors/TMDataGridDateEditor.tsx +1 -3
- package/src/tmdatagrid/components/editors/TMDataGridMultiSelectEditor.tsx +0 -2
- package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +13 -11
- package/src/tmdatagrid/components/editors/TMDataGridSelectEditor.tsx +0 -2
- package/src/tmdatagrid/components/editors/TMDataGridStringEditor.tsx +13 -9
- package/src/tmdatagrid/components/editors/editorShared.ts +47 -0
- package/src/tmdatagrid/components/filters/DgAutocompleteFilter.tsx +1 -1
- package/src/tmdatagrid/components/filters/DgDateRangeFilter.tsx +2 -2
- package/src/tmdatagrid/components/filters/DgRangeSliderFilter.tsx +2 -2
- package/src/tmdatagrid/components/filters/DgTriStateFilter.tsx +1 -1
- package/src/tmdatagrid/components/filters/TMDataGridFilterValueInput.tsx +1 -1
- package/src/tmdatagrid/core/columnUtils.ts +44 -3
- package/src/tmdatagrid/core/editEngine.ts +112 -37
- package/src/tmdatagrid/core/editorFocus.ts +29 -0
- package/src/tmdatagrid/core/filterControls.ts +28 -1
- package/src/tmdatagrid/index.ts +6 -0
- package/src/tmdatagrid/useTMDataGrid.tsx +37 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jielga/tmdatagrid",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.0",
|
|
4
4
|
"description": "A React data grid built on TanStack Table v9 and Mantine - always virtualized, with resizable, reorderable, sortable, filterable, hideable and pinnable columns.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '
|
|
17
|
+
library_version: '2.0.0-beta.0'
|
|
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
|
@@ -3,19 +3,20 @@ name: columns
|
|
|
3
3
|
description: >
|
|
4
4
|
Define and arrange TMDataGrid columns. Covers createTMDataGridColumnHelper,
|
|
5
5
|
every column meta field (label, type, options, flex, align, autoSize,
|
|
6
|
-
enableOrdering,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
enableOrdering, and the meta.filter and meta.edit namespaces holding
|
|
7
|
+
defaultOperator, control, enabled, field, editor, validate and mapValue), the
|
|
8
|
+
six column types, fluid minmax sizing versus fixed width with minSize /
|
|
9
|
+
maxSize / size, autosizing and autosizeColumn, hiding through enableHiding and
|
|
10
|
+
the columns panel, pinning and why a pinned column becomes fixed-width,
|
|
11
|
+
ordering with enableColumnOrdering, meta.enableOrdering, moveColumn,
|
|
12
|
+
moveColumnByStep, getStepTargetColumn and the pinned regions, resetSettings,
|
|
13
|
+
sorting with multi-sort through isMultiSortEvent and a custom sortFn, and the
|
|
14
|
+
generated lanes. Load when adding or changing columns, controlling widths,
|
|
15
|
+
hiding, pinning, reordering or sorting them.
|
|
15
16
|
metadata:
|
|
16
17
|
type: core
|
|
17
18
|
library: '@jielga/tmdatagrid'
|
|
18
|
-
library_version: '
|
|
19
|
+
library_version: '2.0.0-beta.0'
|
|
19
20
|
sources:
|
|
20
21
|
- 'Jielga/TMDataGrid:src/docs/columns.md'
|
|
21
22
|
- 'Jielga/TMDataGrid:src/docs/column-layout.md'
|
|
@@ -65,17 +66,41 @@ column model.
|
|
|
65
66
|
| `align` | `"left" \| "right" \| "center"` | `"left"` | Applied to both header and cells. |
|
|
66
67
|
| `autoSize` | `boolean` | `false` | Size to the widest mounted content once, after the first rows render. |
|
|
67
68
|
| `enableOrdering` | `boolean` | `true` | `false` keeps the column where it is. |
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
| `filter` | `TMDataGridColumnFilterOptions` | – | How this column filters. See below. |
|
|
70
|
+
| `edit` | `TMDataGridColumnEditOptions` | – | How this column is edited. See below. |
|
|
71
|
+
|
|
72
|
+
What the column **is** stays flat; what a stage **does** with it sits in that
|
|
73
|
+
stage's namespace. `type` and `options` are the shared ground both stages read,
|
|
74
|
+
which is why they are not inside either.
|
|
75
|
+
|
|
76
|
+
`meta.filter`:
|
|
77
|
+
|
|
78
|
+
| Field | Type | Default | What it does |
|
|
79
|
+
| --- | --- | --- | --- |
|
|
80
|
+
| `defaultOperator` | `TMDataGridFilterOperator` | The type's default | The operator a fresh filter on this column starts with. |
|
|
81
|
+
| `control` | `TMDataGridFilterControlComponent` | By `meta.type` | Replaces the value control in this column's filter row. Module scope. |
|
|
82
|
+
|
|
83
|
+
`meta.edit`:
|
|
84
|
+
|
|
85
|
+
| Field | Type | Default | What it does |
|
|
86
|
+
| --- | --- | --- | --- |
|
|
87
|
+
| `enabled` | `boolean \| ((row) => boolean)` | editable where a field maps | Whether this column's cells edit. |
|
|
88
|
+
| `field` | `string` | The `accessorKey` | The data path an edit writes to. The only way an `accessorFn` column edits. |
|
|
72
89
|
| `editor` | `TMDataGridEditorComponent` | By `meta.type` | Replaces the cell editor. Module scope. |
|
|
73
90
|
| `validate` | `TMDataGridFieldValidate` | – | Field-level validation. A bare schema means `onChange`. |
|
|
91
|
+
| `mapValue` | `TMDataGridEditValueMap` | – | Maps each value an editor writes, on every keystroke. |
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
meta: {
|
|
95
|
+
type: "number",
|
|
96
|
+
filter: { defaultOperator: "between", control: DgRangeSliderFilter },
|
|
97
|
+
edit: { enabled: (row) => !row.original.locked },
|
|
98
|
+
}
|
|
99
|
+
```
|
|
74
100
|
|
|
75
101
|
`enableOrdering` lives in `meta` because column ordering is the one feature
|
|
76
|
-
TanStack defines no column option for.
|
|
77
|
-
|
|
78
|
-
`defaultFilterOperator` belong to the `filtering` skill.
|
|
102
|
+
TanStack defines no column option for. `meta.edit` only acts once `editMode` is
|
|
103
|
+
set: see the `editing` skill. `meta.filter` belongs to the `filtering` skill.
|
|
79
104
|
|
|
80
105
|
### Column types
|
|
81
106
|
|
package/skills/data/SKILL.md
CHANGED
package/skills/editing/SKILL.md
CHANGED
|
@@ -4,19 +4,19 @@ description: >
|
|
|
4
4
|
Edit cells and rows in TMDataGrid. Covers editMode with its four policies
|
|
5
5
|
(cell, cellConfirm, row, batch), the required getRowId, onEditCommit and
|
|
6
6
|
onEditCommitBatch, why the grid never mutates data, per-column gating with
|
|
7
|
-
meta.
|
|
8
|
-
meta.type, custom editors through meta.editor,
|
|
9
|
-
meta.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
will not open
|
|
7
|
+
meta.edit.enabled and meta.edit.field, the six built-in editors picked by
|
|
8
|
+
meta.type, custom editors through meta.edit.editor, per-keystroke value
|
|
9
|
+
mapping with meta.edit.mapValue, field validation with meta.edit.validate and
|
|
10
|
+
cross-field rules with rowValidators (Standard Schema and Zod), adding and
|
|
11
|
+
deleting rows with edit.addRow, newRowDefaults, onRowAdd and onRowDelete, the
|
|
12
|
+
generated edit lane, TMDataGrid.EditActions with its renderActions slot, and
|
|
13
|
+
the public edit engine (begin, commit, cancel, submitAll, getForm, store).
|
|
14
|
+
Load when making a grid editable, choosing an edit mode, wiring a save,
|
|
15
|
+
writing a cell editor, validating an edit, or when cells will not open.
|
|
16
16
|
metadata:
|
|
17
17
|
type: core
|
|
18
18
|
library: '@jielga/tmdatagrid'
|
|
19
|
-
library_version: '
|
|
19
|
+
library_version: '2.0.0-beta.0'
|
|
20
20
|
sources:
|
|
21
21
|
- 'Jielga/TMDataGrid:src/docs/editing.md'
|
|
22
22
|
- 'Jielga/TMDataGrid:src/docs/query-builder.md'
|
|
@@ -90,7 +90,15 @@ An editor opens on double-click, or with the cell cursor on the cell: Enter, F2,
|
|
|
90
90
|
or just typing, where the character replaces the value. Delete or Backspace
|
|
91
91
|
clears the value and commits without opening an editor. Editing brings cell
|
|
92
92
|
selection with it: `cellSelection` defaults to `"single"` while `editMode` is
|
|
93
|
-
set.
|
|
93
|
+
set. Every open gesture puts the caret in the cell it named, and `edit.addRow()`
|
|
94
|
+
in the new row's first editable cell. The grid places it rather than the editor,
|
|
95
|
+
so a `meta.edit.editor` gets it without doing anything to earn it.
|
|
96
|
+
|
|
97
|
+
Under `"row"` the pencil - or a double-click on any cell, which puts the caret
|
|
98
|
+
in the cell clicked - opens every editable cell of the row, and ✓ saves them as
|
|
99
|
+
one commit. Rows accumulate: opening a second row leaves the first open, and
|
|
100
|
+
each row's ✓ and ✕ act on that row alone, so no row is closed or saved to make
|
|
101
|
+
space for the next.
|
|
94
102
|
|
|
95
103
|
Under `"batch"` nothing leaves the grid until `submitAll`. Enter and Tab park
|
|
96
104
|
the draft, dirty-marked, Escape drops that one draft, and drafts accumulate
|
|
@@ -114,14 +122,14 @@ the error on the row, which is how a failed save stays visible.
|
|
|
114
122
|
## Which cells edit
|
|
115
123
|
|
|
116
124
|
A column is editable when it maps to a data path: its `accessorKey`, or
|
|
117
|
-
`meta.
|
|
125
|
+
`meta.edit.field` for a column built on `accessorFn`. Dot paths reach into nested
|
|
118
126
|
records, so `accessorKey: "address.city"` edits `values.address.city`.
|
|
119
127
|
|
|
120
128
|
| Gate | Effect |
|
|
121
129
|
| --- | --- |
|
|
122
|
-
| `meta.
|
|
123
|
-
| `meta.
|
|
124
|
-
| `meta.
|
|
130
|
+
| `meta.edit.enabled: false` | The column never edits |
|
|
131
|
+
| `meta.edit.enabled: (row) => boolean` | Per row, per column |
|
|
132
|
+
| `meta.edit.field: "lastName"` | The path an `accessorFn` column writes to |
|
|
125
133
|
| `isRowEditable: (row) => boolean` | The whole row, in every mode |
|
|
126
134
|
|
|
127
135
|
Group rows and the generated lanes (checkbox, row number, details, edit) never
|
|
@@ -132,29 +140,23 @@ edit.
|
|
|
132
140
|
columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
|
|
133
141
|
id: "fullName",
|
|
134
142
|
header: "Full name",
|
|
135
|
-
meta: { label: "Full name",
|
|
143
|
+
meta: { label: "Full name", edit: { field: "lastName" } },
|
|
136
144
|
});
|
|
137
145
|
columnHelper.accessor("salary", {
|
|
138
146
|
header: "Salary",
|
|
139
|
-
meta: {
|
|
147
|
+
meta: { edit: { enabled: (row) => row.original.status !== "Terminated" } },
|
|
140
148
|
});
|
|
141
149
|
```
|
|
142
150
|
|
|
143
151
|
## Editors and validation
|
|
144
152
|
|
|
145
|
-
`meta.type` picks the editor
|
|
146
|
-
|
|
153
|
+
`meta.type` picks the editor - `"string"` (the default), `"number"`,
|
|
154
|
+
`"boolean"`, `"date"`, `"select"` and `"multiSelect"` - and `meta.options` feeds
|
|
155
|
+
the two select editors from the same declaration the filter panel reads. Each
|
|
156
|
+
one, with the export that wraps it, is in
|
|
157
|
+
[references/editors-and-validation.md](references/editors-and-validation.md#the-built-in-editors).
|
|
147
158
|
|
|
148
|
-
|
|
149
|
-
| --- | --- |
|
|
150
|
-
| `"string"` (default) | Text input |
|
|
151
|
-
| `"number"` | Number input |
|
|
152
|
-
| `"boolean"` | Checkbox |
|
|
153
|
-
| `"date"` | Native `<input type="date">`, ISO `YYYY-MM-DD` |
|
|
154
|
-
| `"select"` | Searchable select from `meta.options`; commits on pick under `"cell"` |
|
|
155
|
-
| `"multiSelect"` | Multi-select, same source |
|
|
156
|
-
|
|
157
|
-
`meta.editor` replaces one, `meta.validate` guards the field, and
|
|
159
|
+
`meta.edit.editor` replaces one, `meta.edit.validate` guards the field, and
|
|
158
160
|
`rowValidators` carries cross-field rules. The validators are TanStack Form's
|
|
159
161
|
own, Standard Schema included, so a Zod schema passes straight through:
|
|
160
162
|
|
|
@@ -162,7 +164,7 @@ own, Standard Schema included, so a Zod schema passes straight through:
|
|
|
162
164
|
import { z } from "zod";
|
|
163
165
|
|
|
164
166
|
// Field level, on the column. A bare schema means { onChange: schema }.
|
|
165
|
-
meta: { validate: z.string().min(2, "At least two characters") }
|
|
167
|
+
meta: { edit: { validate: z.string().min(2, "At least two characters") } }
|
|
166
168
|
|
|
167
169
|
// Form level, on the hook. Cross-field rules, under "row" or "batch".
|
|
168
170
|
rowValidators: {
|
|
@@ -174,11 +176,19 @@ rowValidators: {
|
|
|
174
176
|
}
|
|
175
177
|
```
|
|
176
178
|
|
|
177
|
-
Pathed issues land on the matching cells, pathless ones on the row
|
|
178
|
-
mark
|
|
179
|
+
Pathed issues land on the matching cells, pathless ones on the row, and cell
|
|
180
|
+
corners mark both: blue for a dirty draft, red for a validation error.
|
|
181
|
+
|
|
182
|
+
`meta.edit.mapValue` rewrites a value rather than rejecting it: uppercase a
|
|
183
|
+
code, strip spaces from an IBAN, clamp a number. It runs on every write an
|
|
184
|
+
editor makes, so a text input maps per keystroke, and what it returns is what
|
|
185
|
+
the validators judge and what commits.
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
|
|
187
|
+
```tsx
|
|
188
|
+
meta: { edit: { mapValue: ({ value }) => String(value).toUpperCase() } }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Detail for all three: [references/editors-and-validation.md](references/editors-and-validation.md).
|
|
182
192
|
|
|
183
193
|
## Adding and deleting rows
|
|
184
194
|
|
|
@@ -242,17 +252,11 @@ while editing is off, and works under any mode, not only batch.
|
|
|
242
252
|
|
|
243
253
|
## The engine: `edit`
|
|
244
254
|
|
|
245
|
-
`grid.edit` is public, and everything the chrome does goes through it
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
| `edit.commit(rowId)` | Commits one row. `Promise<boolean>`, `false` when blocked |
|
|
251
|
-
| `edit.cancel(rowId)` / `edit.cancelAll()` | Drops one draft, or every draft |
|
|
252
|
-
| `edit.submitAll()` | Commits every open row. `Promise<boolean>` |
|
|
253
|
-
| `edit.addRow()` / `edit.deleteRow(rowId)` | Rows in and out |
|
|
254
|
-
| `edit.getForm(rowId)` | The row's live TanStack Form `FormApi` |
|
|
255
|
-
| `edit.store` | The projection store, for `useSelector` |
|
|
255
|
+
`grid.edit` is public, and everything the chrome does goes through it:
|
|
256
|
+
`begin` and `commit`, `cancel` / `cancelAll`, `submitAll`, `addRow` /
|
|
257
|
+
`deleteRow`, `getForm`, and `store` for `useSelector`. Every member with its
|
|
258
|
+
signature, the gates, `clearCell`, `deactivate`, and the `edit.store` shape are
|
|
259
|
+
in [references/editing-api.md](references/editing-api.md#the-edit-engine).
|
|
256
260
|
|
|
257
261
|
```tsx
|
|
258
262
|
import { useSelector } from "@tanstack/react-store";
|
|
@@ -263,11 +267,6 @@ const pendingCount = useSelector(
|
|
|
263
267
|
);
|
|
264
268
|
```
|
|
265
269
|
|
|
266
|
-
`edit.store` holds `active`, `openRowIds`, `rows` (per row `dirtyFields`,
|
|
267
|
-
`errorFields`, `isSubmitting`), `newRows` and `deletedRowIds`. The full member
|
|
268
|
-
list, `clearCell`, `deactivate` and the gates included, is in
|
|
269
|
-
[references/editing-api.md](references/editing-api.md).
|
|
270
|
-
|
|
271
270
|
`getForm` is the *one row, one form* promise made public: render that same form
|
|
272
271
|
in a drawer and it shares values, dirty state and errors with the inline cells,
|
|
273
272
|
because it is the same `FormApi`.
|
|
@@ -281,7 +280,7 @@ Use `editMode: "row"` so a row reaches the form on approval, map by row id
|
|
|
281
280
|
(never index), and mint negative ids for new rows.
|
|
282
281
|
|
|
283
282
|
The validation split is structural, not stylistic: **a rule decidable from one
|
|
284
|
-
row belongs to the grid (`meta.validate`, `rowValidators`); a rule needing the
|
|
283
|
+
row belongs to the grid (`meta.edit.validate`, `rowValidators`); a rule needing the
|
|
285
284
|
other rows or the collection ("has rows", "no duplicates") belongs to the
|
|
286
285
|
form's field validator.** A row's form cannot see the array, and `edit.store`
|
|
287
286
|
publishes field names, never values, so the form cannot see a draft.
|
|
@@ -341,14 +340,14 @@ Source: `src/tmdatagrid/useTMDataGrid.tsx` (`TMDataGridEditingCallbacks`).
|
|
|
341
340
|
|
|
342
341
|
### HIGH A cell editor defined inside the component
|
|
343
342
|
|
|
344
|
-
`meta.editor` is rendered as JSX, so its identity is its component type. An
|
|
343
|
+
`meta.edit.editor` is rendered as JSX, so its identity is its component type. An
|
|
345
344
|
inline arrow is a new type on every render, which unmounts the open editor and
|
|
346
345
|
loses what was being typed.
|
|
347
346
|
|
|
348
347
|
Wrong:
|
|
349
348
|
|
|
350
349
|
```tsx
|
|
351
|
-
meta: { editor: ({ field }) => <Slider value={field.state.value} /> },
|
|
350
|
+
meta: { edit: { editor: ({ field }) => <Slider value={field.state.value} /> } },
|
|
352
351
|
```
|
|
353
352
|
|
|
354
353
|
Correct:
|
|
@@ -363,7 +362,7 @@ const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => (
|
|
|
363
362
|
/>
|
|
364
363
|
);
|
|
365
364
|
|
|
366
|
-
meta: { editor: SalaryEditor },
|
|
365
|
+
meta: { edit: { editor: SalaryEditor } },
|
|
367
366
|
```
|
|
368
367
|
|
|
369
368
|
Source: `src/docs/editors.md`, `src/tmdatagrid/core/editEngine.ts`.
|
|
@@ -411,7 +410,7 @@ Correct:
|
|
|
411
410
|
columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
|
|
412
411
|
id: "fullName",
|
|
413
412
|
header: "Full name",
|
|
414
|
-
meta: { label: "Full name",
|
|
413
|
+
meta: { label: "Full name", edit: { field: "lastName" } },
|
|
415
414
|
});
|
|
416
415
|
```
|
|
417
416
|
|
|
@@ -491,7 +490,8 @@ Source: `src/docs/editing.md` (Adding and deleting rows).
|
|
|
491
490
|
## References
|
|
492
491
|
|
|
493
492
|
- [Editors and validation](references/editors-and-validation.md) - the editor
|
|
494
|
-
contract, wrapping a built-in,
|
|
493
|
+
contract, wrapping a built-in, what `mapValue` leaves alone, field and row
|
|
494
|
+
validators, server-side errors.
|
|
495
495
|
- [Editing API](references/editing-api.md) - every option, callback, column meta
|
|
496
496
|
field, export, CSS variable and data attribute editing owns.
|
|
497
497
|
|
|
@@ -35,10 +35,10 @@ path, which may be dotted.
|
|
|
35
35
|
| --- | --- | --- | --- |
|
|
36
36
|
| `meta.type` | `TMDataGridColumnType` | `"string"` | Picks the built-in editor, and the filter operators. |
|
|
37
37
|
| `meta.options` | `TMDataGridOptionsSource` | – | Choices for `select` / `multiSelect`, shared with the filter panel. Array, `"faceted"`, or a function. |
|
|
38
|
-
| `meta.
|
|
39
|
-
| `meta.
|
|
40
|
-
| `meta.editor` | `TMDataGridEditorComponent` | By `meta.type` | Replaces the cell editor. Define at module scope. |
|
|
41
|
-
| `meta.validate` | `TMDataGridFieldValidate` | – | Field-level validation. A bare schema means `onChange`. |
|
|
38
|
+
| `meta.edit.enabled` | `boolean \| ((row) => boolean)` | editable where a field maps | Whether this column's cells edit. |
|
|
39
|
+
| `meta.edit.field` | `string` | The `accessorKey` | The data path an edit writes to. The only way an `accessorFn` column edits. |
|
|
40
|
+
| `meta.edit.editor` | `TMDataGridEditorComponent` | By `meta.type` | Replaces the cell editor. Define at module scope. |
|
|
41
|
+
| `meta.edit.validate` | `TMDataGridFieldValidate` | – | Field-level validation. A bare schema means `onChange`. |
|
|
42
42
|
|
|
43
43
|
## The `edit` engine
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ path, which may be dotted.
|
|
|
46
46
|
|
|
47
47
|
| Member | Signature | Notes |
|
|
48
48
|
| --- | --- | --- |
|
|
49
|
-
| `begin` | `({ rowId, columnId }) => void` |
|
|
49
|
+
| `begin` | `({ rowId, columnId }) => void` | Row mode opens the whole row either way; `columnId` says which cell takes the caret, `null` (the pencil) its first editable one. |
|
|
50
50
|
| `commit` | `(rowId) => Promise<boolean>` | `false` keeps the form open with its errors. |
|
|
51
51
|
| `cancel` | `(rowId) => void` | Drops one draft. |
|
|
52
52
|
| `cancelAll` | `() => void` | Drops every draft. |
|
|
@@ -66,7 +66,10 @@ path, which may be dotted.
|
|
|
66
66
|
|
|
67
67
|
```ts
|
|
68
68
|
type TMDataGridEditState = {
|
|
69
|
+
// The cell the last open gesture named - where the caret goes.
|
|
69
70
|
active: { rowId: string; columnId: string | null } | null;
|
|
71
|
+
// Rows with a live form. One under `"cell"`; as many as the user opened
|
|
72
|
+
// under `"row"`, `"cellConfirm"` and `"batch"` - which rows are editing.
|
|
70
73
|
openRowIds: ReadonlyArray<string>;
|
|
71
74
|
rows: Record<
|
|
72
75
|
string,
|
|
@@ -29,7 +29,7 @@ the data), or a function of the table, column and row.
|
|
|
29
29
|
|
|
30
30
|
## The editor contract
|
|
31
31
|
|
|
32
|
-
`meta.editor` fills the same slot the built-ins fill. It is a **component**,
|
|
32
|
+
`meta.edit.editor` fills the same slot the built-ins fill. It is a **component**,
|
|
33
33
|
rendered as JSX, so hooks are legal inside it.
|
|
34
34
|
|
|
35
35
|
```ts
|
|
@@ -43,11 +43,16 @@ type TMDataGridEditorArgs = {
|
|
|
43
43
|
commit: () => Promise<boolean>; // what Enter would do
|
|
44
44
|
cancel: () => void; // what Escape would do
|
|
45
45
|
size: TMDataGridSize;
|
|
46
|
-
autoFocus: boolean; // take focus on mount
|
|
47
46
|
seedText?: string; // set when typing opened the editor
|
|
48
47
|
};
|
|
49
48
|
```
|
|
50
49
|
|
|
50
|
+
The grid places the caret itself, once per open gesture: it focuses
|
|
51
|
+
`data-dg-part="editor-input"` when the editor publishes it, and the first
|
|
52
|
+
focusable element inside the editor otherwise. So a custom editor gets the caret
|
|
53
|
+
without asking for focus - set `editor-input` only to name which of several
|
|
54
|
+
inputs should take it.
|
|
55
|
+
|
|
51
56
|
Bind any control to `field` exactly as inside any TanStack Form:
|
|
52
57
|
`field.state.value`, `field.state.meta.errors`, `field.handleChange`,
|
|
53
58
|
`field.handleBlur`.
|
|
@@ -73,7 +78,7 @@ const SalaryEditor: TMDataGridEditorComponent = ({ field, commit, cancel }) => (
|
|
|
73
78
|
|
|
74
79
|
columnHelper.accessor("salary", {
|
|
75
80
|
header: "Salary",
|
|
76
|
-
meta: { type: "number", editor: SalaryEditor },
|
|
81
|
+
meta: { type: "number", edit: { editor: SalaryEditor } },
|
|
77
82
|
});
|
|
78
83
|
```
|
|
79
84
|
|
|
@@ -114,9 +119,52 @@ Pass `args` through whole. `field` is a live TanStack Form `FieldApi`, not a
|
|
|
114
119
|
plain object, so spreading or rebuilding it loses the binding its methods rely
|
|
115
120
|
on.
|
|
116
121
|
|
|
122
|
+
## Mapping the value
|
|
123
|
+
|
|
124
|
+
`meta.edit.mapValue` maps every value an editor writes, before it reaches the
|
|
125
|
+
draft. It runs per write, which for a text input is per keystroke and for a
|
|
126
|
+
select is per pick.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
meta: {
|
|
130
|
+
edit: {
|
|
131
|
+
// Uppercase as it is typed.
|
|
132
|
+
mapValue: ({ value }) =>
|
|
133
|
+
typeof value === "string" ? value.toUpperCase() : value,
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
meta: {
|
|
138
|
+
edit: {
|
|
139
|
+
// Depends on the record being edited; `previous` is the value the field
|
|
140
|
+
// held before this write.
|
|
141
|
+
mapValue: ({ value, previous, row }) =>
|
|
142
|
+
row.original.country === "SE" ? normalise(value) : previous,
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The grid applies it in the editor host, around the field every editor writes
|
|
148
|
+
through, so it covers the six built-ins, a custom `meta.edit.editor`, and the
|
|
149
|
+
type-to-edit seed character.
|
|
150
|
+
|
|
151
|
+
Left unmapped on purpose:
|
|
152
|
+
|
|
153
|
+
- The value an editor opens with. Mapping it would rewrite stored data nobody
|
|
154
|
+
edited, mark a pristine row dirty and swallow the select-all.
|
|
155
|
+
- `edit.clearCell()`, the Delete key: it writes the type's empty value through
|
|
156
|
+
the form, so there is no input to map.
|
|
157
|
+
- An editor calling `field.setValue`. `handleChange` is the mapped path.
|
|
158
|
+
|
|
159
|
+
The built-in string and number editors restore the caret after a mapped write,
|
|
160
|
+
shifted by the length the map changed, because React reassigning `input.value`
|
|
161
|
+
otherwise drops the caret at the end of the field. A custom editor rendering its
|
|
162
|
+
own input has to do the same: record `selectionStart` on change, restore it in a
|
|
163
|
+
layout effect.
|
|
164
|
+
|
|
117
165
|
## Field validation
|
|
118
166
|
|
|
119
|
-
`meta.validate` takes TanStack Form's field-validator vocabulary. A bare schema
|
|
167
|
+
`meta.edit.validate` takes TanStack Form's field-validator vocabulary. A bare schema
|
|
120
168
|
(Zod, or any Standard Schema, or a plain function) means `{ onChange: it }`; the
|
|
121
169
|
object form takes every trigger Form defines.
|
|
122
170
|
|
|
@@ -124,13 +172,13 @@ object form takes every trigger Form defines.
|
|
|
124
172
|
import { z } from "zod";
|
|
125
173
|
|
|
126
174
|
// Bare schema: validates on change.
|
|
127
|
-
meta: { validate: z.string().min(2, "At least two characters") }
|
|
175
|
+
meta: { edit: { validate: z.string().min(2, "At least two characters") } }
|
|
128
176
|
|
|
129
177
|
// Object form: pick the trigger.
|
|
130
|
-
meta: { validate: { onBlur: z.string().email("Not an email address") } }
|
|
178
|
+
meta: { edit: { validate: { onBlur: z.string().email("Not an email address") } } }
|
|
131
179
|
|
|
132
180
|
// A plain function works too.
|
|
133
|
-
meta: { validate: ({ value }) => (value > 0 ? undefined : "Must be positive") }
|
|
181
|
+
meta: { edit: { validate: ({ value }) => (value > 0 ? undefined : "Must be positive") } }
|
|
134
182
|
```
|
|
135
183
|
|
|
136
184
|
`normalizeFieldValidate(validate)` is exported for consumers building their own
|
|
@@ -3,11 +3,11 @@ 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.
|
|
6
|
+
meta.type offers each, meta.filter.defaultOperator, isFilterActive and the
|
|
7
7
|
half-typed filter, the filter panel, TMDataGrid.FilterButton,
|
|
8
8
|
TMDataGrid.FilterPills with its api prop, openColumnFilter, replacing a value
|
|
9
9
|
control with DgRangeSliderFilter / DgDateRangeFilter / DgAutocompleteFilter /
|
|
10
|
-
DgTriStateFilter or a meta.
|
|
10
|
+
DgTriStateFilter or a meta.filter.control component, per-column filterFn, and
|
|
11
11
|
the quick search: TMDataGrid.Search, quickSearchMode fuzzy or contains,
|
|
12
12
|
fuzzyGlobalFilterFn, enableMatchHighlighting and enableGlobalFilter. Load when
|
|
13
13
|
adding filters, choosing operators, building a filter control, showing active
|
|
@@ -15,7 +15,7 @@ description: >
|
|
|
15
15
|
metadata:
|
|
16
16
|
type: core
|
|
17
17
|
library: '@jielga/tmdatagrid'
|
|
18
|
-
library_version: '
|
|
18
|
+
library_version: '2.0.0-beta.0'
|
|
19
19
|
sources:
|
|
20
20
|
- 'Jielga/TMDataGrid:src/docs/filtering.md'
|
|
21
21
|
- 'Jielga/TMDataGrid:src/docs/quick-search.md'
|
|
@@ -70,13 +70,13 @@ and `equals` on a `date` column plays the role of "is". On a `multiSelect`
|
|
|
70
70
|
column, whose cells hold arrays, `isAnyOf` is an intersection test and `isNoneOf`
|
|
71
71
|
its complement. `between` is inclusive at both ends.
|
|
72
72
|
|
|
73
|
-
`meta.
|
|
73
|
+
`meta.filter.defaultOperator` sets which one a fresh filter opens on, and must be
|
|
74
74
|
one of the type's own:
|
|
75
75
|
|
|
76
76
|
```tsx
|
|
77
77
|
columnHelper.accessor("salary", {
|
|
78
78
|
header: "Salary",
|
|
79
|
-
meta: { type: "number",
|
|
79
|
+
meta: { type: "number", filter: { defaultOperator: "between" } },
|
|
80
80
|
});
|
|
81
81
|
```
|
|
82
82
|
|
|
@@ -118,17 +118,16 @@ named exports:
|
|
|
118
118
|
```tsx
|
|
119
119
|
meta: {
|
|
120
120
|
type: "number",
|
|
121
|
-
|
|
122
|
-
defaultFilterOperator: "between",
|
|
121
|
+
filter: { control: DgRangeSliderFilter, defaultOperator: "between" },
|
|
123
122
|
}
|
|
124
123
|
```
|
|
125
124
|
|
|
126
|
-
Pair the range-shaped ones with `
|
|
125
|
+
Pair the range-shaped ones with `defaultOperator: "between"` so the filter
|
|
127
126
|
opens on them. For operators outside their shape every built-in falls back to
|
|
128
127
|
`TMDataGridFilterValueInput`, which is exported so a custom control can take the
|
|
129
128
|
same escape.
|
|
130
129
|
|
|
131
|
-
`meta.
|
|
130
|
+
`meta.filter.control` is a **component**, rendered as JSX, so hooks are legal
|
|
132
131
|
inside. It is a **value-only contract**: it reads `operator` to shape itself and
|
|
133
132
|
writes the bare value through `onChange`, and the grid composes the stored
|
|
134
133
|
`{ operator, value }` around it. The column and operator dropdowns stay the
|
|
@@ -157,7 +156,7 @@ const StatusFilter: TMDataGridFilterControlComponent = ({
|
|
|
157
156
|
</Chip.Group>
|
|
158
157
|
);
|
|
159
158
|
|
|
160
|
-
meta: { type: "select",
|
|
159
|
+
meta: { type: "select", filter: { control: StatusFilter } }
|
|
161
160
|
```
|
|
162
161
|
|
|
163
162
|
`args.options` arrives pre-resolved for a column that declares `meta.options` or
|
|
@@ -259,7 +258,7 @@ Source: `src/docs/filtering.md` (Operators).
|
|
|
259
258
|
|
|
260
259
|
### HIGH A filter control defined inside the component
|
|
261
260
|
|
|
262
|
-
`meta.
|
|
261
|
+
`meta.filter.control` is rendered as JSX, so its identity is its component type.
|
|
263
262
|
An inline arrow is a new type every render, which remounts the control and loses
|
|
264
263
|
what was being typed into it.
|
|
265
264
|
|
|
@@ -269,7 +268,7 @@ Correct:
|
|
|
269
268
|
// Module scope, referenced by name.
|
|
270
269
|
const StatusFilter: TMDataGridFilterControlComponent = (args) => { /* … */ };
|
|
271
270
|
|
|
272
|
-
meta: {
|
|
271
|
+
meta: { filter: { control: StatusFilter } }
|
|
273
272
|
```
|
|
274
273
|
|
|
275
274
|
Source: `src/docs/filtering.md` (Writing your own).
|
|
@@ -318,8 +317,8 @@ Source: `src/docs/quick-search.md` (Fuzzy by default).
|
|
|
318
317
|
| `enableColumnFilters` | Table option | `boolean` | `true` | `false` removes the panel, the button and the menu item. |
|
|
319
318
|
| `enableColumnFilter` | Column option | `boolean` | `true` | `false` takes one column out of filtering. |
|
|
320
319
|
| `meta.type` | Column meta | `TMDataGridColumnType` | `"string"` | Selects the operators and the value control. |
|
|
321
|
-
| `meta.
|
|
322
|
-
| `meta.
|
|
320
|
+
| `meta.filter.defaultOperator` | Column meta | `TMDataGridFilterOperator` | The type's default | The operator a fresh filter opens on. |
|
|
321
|
+
| `meta.filter.control` | Column meta | `TMDataGridFilterControlComponent` | By type and operator | Replaces the value control. Module scope. |
|
|
323
322
|
| `filterFn` | Column option | name or fn | `"tmDataGrid"` | Custom matching for one column. |
|
|
324
323
|
| `quickSearchMode` | Option | `"fuzzy" \| "contains"` | `"fuzzy"` | How the quick search matches. |
|
|
325
324
|
| `enableMatchHighlighting` | Option | `boolean` | `false` | Mark matched text in default-rendered cells. |
|
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: '
|
|
17
|
+
library_version: '2.0.0-beta.0'
|
|
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: '
|
|
20
|
+
library_version: '2.0.0-beta.0'
|
|
21
21
|
sources:
|
|
22
22
|
- 'Jielga/TMDataGrid:src/docs/row-selection.md'
|
|
23
23
|
- 'Jielga/TMDataGrid:src/docs/row-interaction.md'
|
|
@@ -249,10 +249,15 @@ const grid = useTMDataGrid({
|
|
|
249
249
|
});
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
-
There is no built-in pin gesture
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
There is no built-in pin gesture and no pin icon - build one from TanStack's own
|
|
253
|
+
`row.pin("top" | "bottom" | false)`, `row.getIsPinned()` and `row.getCanPin()`,
|
|
254
|
+
either as a display column whose cell is a pin button or in the row context
|
|
255
|
+
menu. Read the pinned state through a subscription
|
|
256
|
+
(`useSelector(row.table.store, () => row.getIsPinned())`) rather than calling it
|
|
257
|
+
in a component body: the `row` identity survives a pin, so the React Compiler
|
|
258
|
+
would cache the call and the icon would never change. Whichever gesture you
|
|
259
|
+
build, put unpin in it too. Pinned rows leave the scrolling order and render in
|
|
260
|
+
sticky blocks: top under the header, bottom above the summary row.
|
|
256
261
|
|
|
257
262
|
Pinned rows are still body rows - selection, editing, details, the context menu
|
|
258
263
|
and per-row styling all behave normally. What they sit out are the statements
|