@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
|
@@ -22,7 +22,7 @@ useTMDataGrid({
|
|
|
22
22
|
Correct: wire `editing.onCommit` to apply the change where the data lives, as
|
|
23
23
|
in the skill's Setup section.
|
|
24
24
|
|
|
25
|
-
Source: `
|
|
25
|
+
Source: `packages/tmdatagrid/docs/editing.md`, `packages/tmdatagrid/src/core/editEngine.ts`.
|
|
26
26
|
|
|
27
27
|
## CRITICAL `getRowId` built from the row index
|
|
28
28
|
|
|
@@ -42,7 +42,7 @@ Correct:
|
|
|
42
42
|
getRowId: (row) => String(row.id),
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Source: `
|
|
45
|
+
Source: `packages/tmdatagrid/src/useTMDataGrid.tsx` (`TMDataGridEditingCallbacks`).
|
|
46
46
|
|
|
47
47
|
## HIGH A cell editor defined inside the component
|
|
48
48
|
|
|
@@ -71,45 +71,7 @@ const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => (
|
|
|
71
71
|
meta: { edit: { editor: SalaryEditor } },
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
Source: `
|
|
75
|
-
|
|
76
|
-
## HIGH A custom editor that binds no error text
|
|
77
|
-
|
|
78
|
-
The built-in editors bind the field's first error to the input's `error` prop.
|
|
79
|
-
A custom editor that binds nothing still blocks the commit, but all the user
|
|
80
|
-
sees is `data-invalid` on the cell - the row stays open with no message
|
|
81
|
-
anywhere on screen, which reads as a broken grid rather than a rejected value.
|
|
82
|
-
|
|
83
|
-
Wrong:
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => (
|
|
87
|
-
<Slider value={field.state.value} onChange={field.handleChange} onChangeEnd={() => void commit()} />
|
|
88
|
-
);
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Correct:
|
|
92
|
-
|
|
93
|
-
```tsx
|
|
94
|
-
const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => {
|
|
95
|
-
const error = field.state.meta.errors
|
|
96
|
-
.map((e) => (typeof e === "string" ? e : e?.message))
|
|
97
|
-
.find(Boolean);
|
|
98
|
-
return (
|
|
99
|
-
<Slider
|
|
100
|
-
value={field.state.value}
|
|
101
|
-
onChange={field.handleChange}
|
|
102
|
-
onChangeEnd={() => void commit()}
|
|
103
|
-
error={error}
|
|
104
|
-
/>
|
|
105
|
-
);
|
|
106
|
-
};
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
An entry of `field.state.meta.errors` is a string from a function validator,
|
|
110
|
-
or an issue carrying a `message` from a schema.
|
|
111
|
-
|
|
112
|
-
Source: `src/docs/editors.md` (Writing your own).
|
|
74
|
+
Source: `packages/tmdatagrid/docs/editors.md`, `packages/tmdatagrid/src/core/editEngine.ts`.
|
|
113
75
|
|
|
114
76
|
## HIGH A cross-field rule under `editing.mode: "cell"`
|
|
115
77
|
|
|
@@ -120,7 +82,7 @@ other column's old value, and the row cannot be saved.
|
|
|
120
82
|
Correct: `editing.rowValidators` needs `mode: "row"`, which validates the
|
|
121
83
|
whole row in one commit.
|
|
122
84
|
|
|
123
|
-
Source: `
|
|
85
|
+
Source: `packages/tmdatagrid/docs/editors.md` (Validation).
|
|
124
86
|
|
|
125
87
|
## HIGH An `accessorFn` column that never opens an editor
|
|
126
88
|
|
|
@@ -147,7 +109,7 @@ columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
|
|
|
147
109
|
});
|
|
148
110
|
```
|
|
149
111
|
|
|
150
|
-
Source: `
|
|
112
|
+
Source: `packages/tmdatagrid/docs/editing.md` (Which cells edit).
|
|
151
113
|
|
|
152
114
|
## HIGH Swallowing the error in `editing.onCommit`
|
|
153
115
|
|
|
@@ -170,7 +132,7 @@ onCommit: async ({ rowId, value }) => {
|
|
|
170
132
|
Correct: no `catch` - let the rejection propagate, and the form stays open
|
|
171
133
|
with the error on the row.
|
|
172
134
|
|
|
173
|
-
Source: `
|
|
135
|
+
Source: `packages/tmdatagrid/src/useTMDataGrid.tsx` (`TMDataGridEditingCallbacks`).
|
|
174
136
|
|
|
175
137
|
## HIGH Submitting an outer form while the grid holds a draft
|
|
176
138
|
|
|
@@ -186,12 +148,14 @@ const hasOpenDraft = useSelector(grid.edit.store, (s) => s.openRowIds.length > 0
|
|
|
186
148
|
|
|
187
149
|
<Button type="submit" disabled={!canSubmit || hasOpenDraft}>Save</Button>
|
|
188
150
|
// or commit and flush instead of blocking:
|
|
189
|
-
await grid.edit.commitAll();
|
|
190
|
-
const flushed = await grid.edit.saveDrafts();
|
|
191
|
-
if (flushed) await form.handleSubmit();
|
|
151
|
+
const committed = await grid.edit.commitAll();
|
|
152
|
+
const flushed = committed.ok ? await grid.edit.saveDrafts() : undefined;
|
|
153
|
+
if (flushed?.ok) await form.handleSubmit();
|
|
192
154
|
```
|
|
193
155
|
|
|
194
|
-
|
|
156
|
+
Both results are objects, so test `.ok`: `if (await grid.edit.saveDrafts())` is always true.
|
|
157
|
+
|
|
158
|
+
Source: `packages/tmdatagrid/docs/query-builder.md` (Which mode, Submitting).
|
|
195
159
|
|
|
196
160
|
## HIGH A bulk write built from `begin`, `getForm` and `commit`
|
|
197
161
|
|
|
@@ -223,40 +187,51 @@ await grid.edit.setRowValues(rowId, { status: "Closed", closedOn: today() });
|
|
|
223
187
|
Both write the stored value through the row's form, so `meta.edit.mapValue` does not run - no editor is involved - while `meta.edit.validate` does, at the commit.
|
|
224
188
|
A refused value leaves the row open carrying its errors and the call resolves `false`, so read the result rather than assuming the fill landed.
|
|
225
189
|
|
|
226
|
-
Source: `
|
|
190
|
+
Source: `packages/tmdatagrid/src/core/editEngine.ts` (`begin`, `writeFields`).
|
|
227
191
|
|
|
228
|
-
## MEDIUM
|
|
192
|
+
## MEDIUM Reading an open row's values off `row.original` outside a cell
|
|
229
193
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
`data
|
|
233
|
-
|
|
194
|
+
Inside a `cell` renderer, `row.original` and `getValue()` are the row as
|
|
195
|
+
shown: the open form's values while the row is edited, the committed draft
|
|
196
|
+
after ✓, and `data` otherwise - a computed column follows the draft as it is
|
|
197
|
+
typed, and a button in a cell sends the draft the user sees. Outside it, the
|
|
198
|
+
row callbacks (`onRowClick`, `renderRowContextMenu`, `rowClassName`) and any
|
|
199
|
+
code holding a row id see `data`, or the committed draft, and never an open
|
|
200
|
+
form's values.
|
|
234
201
|
|
|
235
|
-
Correct:
|
|
202
|
+
Correct: reach the row as shown by id:
|
|
236
203
|
|
|
237
204
|
```tsx
|
|
238
|
-
|
|
239
|
-
const { edit } = useTMDataGridContext();
|
|
240
|
-
const values = useSelector(edit.store, (state) => state.rows[rowId]?.values);
|
|
241
|
-
return (values as Product | undefined) ?? original;
|
|
242
|
-
}
|
|
205
|
+
const shown = grid.edit.getRowValues(row.id) ?? row.original;
|
|
243
206
|
```
|
|
244
207
|
|
|
245
|
-
Source: `
|
|
208
|
+
Source: `packages/tmdatagrid/docs/editing.md` (How a draft renders).
|
|
246
209
|
|
|
247
210
|
## MEDIUM Reading a commit's result as the saved value
|
|
248
211
|
|
|
249
|
-
`edit.commit(rowId)
|
|
250
|
-
`
|
|
251
|
-
|
|
252
|
-
not happen.
|
|
212
|
+
`edit.commit(rowId)` resolves a `boolean`: `false` when validation or a rejected commit kept the row open.
|
|
213
|
+
`edit.commitAll()` resolves `{ ok, committed, open }`, and `edit.saveDrafts()` resolves `{ ok, saved, kept, reopened }`.
|
|
214
|
+
Each id is in exactly one list, and `ok` is `false` when any row stayed open, was kept in the draft store, or was reopened with an error.
|
|
215
|
+
Ignoring the result reports a save that did not happen, and testing the object itself is always true.
|
|
216
|
+
|
|
217
|
+
Wrong:
|
|
253
218
|
|
|
254
219
|
```tsx
|
|
255
|
-
|
|
256
|
-
notifications.show({ message: saved ? "Saved" : "Some rows need attention" });
|
|
220
|
+
if (await grid.edit.saveDrafts()) notifications.show({ message: "Saved" });
|
|
257
221
|
```
|
|
258
222
|
|
|
259
|
-
|
|
223
|
+
Correct:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
const { ok, kept, reopened } = await grid.edit.saveDrafts();
|
|
227
|
+
notifications.show({
|
|
228
|
+
message: ok
|
|
229
|
+
? "Saved"
|
|
230
|
+
: `${kept.length} rows not saved, ${reopened.length} rows need attention`,
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Source: `packages/tmdatagrid/src/core/editEngine.ts` (`TMDataGridEditApi`).
|
|
260
235
|
|
|
261
236
|
## MEDIUM Expecting `editing.onRowDelete` to fire under a draft store
|
|
262
237
|
|
|
@@ -266,4 +241,37 @@ Under `draft: true` it only toggles a deletion mark, so nothing is removed until
|
|
|
266
241
|
no such callback, in the per-row `editing.onRowDelete` loop. A confirmation
|
|
267
242
|
placed inside `editing.onRowDelete` therefore guards the save, not the trash.
|
|
268
243
|
|
|
269
|
-
Source: `
|
|
244
|
+
Source: `packages/tmdatagrid/docs/adding-rows.md`.
|
|
245
|
+
|
|
246
|
+
## MEDIUM A custom editor that binds no invalid state
|
|
247
|
+
|
|
248
|
+
The message is the host's: it shows in a tooltip on the editor whatever the
|
|
249
|
+
editor is. The invalid styling is the editor's own, and one that binds nothing
|
|
250
|
+
keeps its normal border while the commit is refused, so the only marks on
|
|
251
|
+
screen are the tooltip and `data-invalid` on the cell.
|
|
252
|
+
|
|
253
|
+
Wrong:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => (
|
|
257
|
+
<Slider value={field.state.value} onChange={field.handleChange} onChangeEnd={() => void commit()} />
|
|
258
|
+
);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Correct:
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => {
|
|
265
|
+
const hasError = field.state.meta.errors.length > 0;
|
|
266
|
+
return (
|
|
267
|
+
<Slider
|
|
268
|
+
value={field.state.value}
|
|
269
|
+
onChange={field.handleChange}
|
|
270
|
+
onChangeEnd={() => void commit()}
|
|
271
|
+
error={hasError}
|
|
272
|
+
/>
|
|
273
|
+
);
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Source: `packages/tmdatagrid/docs/editors.md` (Writing your own).
|
|
@@ -9,14 +9,14 @@ kind.
|
|
|
9
9
|
| --- | --- | --- | --- |
|
|
10
10
|
| `editing` | `TMDataGridEditingOptions` | off | The editing namespace. Setting it turns editing on. |
|
|
11
11
|
| `editing.mode` | `"cell" \| "cellConfirm" \| "row"` | – | What counts as a commit, and which controls trigger it. |
|
|
12
|
-
| `editing.draft` | `boolean` | `false` | Where a commit goes. On, it
|
|
12
|
+
| `editing.draft` | `boolean` | `false` | Where a commit goes. On, it is held in the grid's draft store until `edit.saveDrafts()`. |
|
|
13
13
|
| `getRowId` | `(row) => string` | – | A TanStack table option, required once `editing` is set. Drafts are keyed by it. |
|
|
14
14
|
| `editing.columns` | `ReadonlyArray<string>` | every column mapping to a data path | The column ids that take edits. Gates before `meta.edit`, never past it: a column left out takes no edits whatever its meta says, and a listed column still answers to its `meta.edit.enabled`. Also decides which cells an entry row opens. |
|
|
15
15
|
| `editing.isRowEditable` | `(row) => boolean` | – | Closes a whole row to editing, in every mode. |
|
|
16
16
|
| `editing.rowValidators` | `TMDataGridRowValidators` | – | Form-level validation. Cross-field rules live here. |
|
|
17
17
|
| `editing.tableValidators` | `TMDataGridTableValidators` | – | Cross-row rules, handed the collection with every draft overlaid. |
|
|
18
18
|
| `editing.newRowDefaults` | `TData \| (() => TData)` | – | Seeds the entry row's form. A function is called per added row. |
|
|
19
|
-
| `editing.newRowsSticky` | `boolean` | `false` | `draft: true` only. Keeps
|
|
19
|
+
| `editing.newRowsSticky` | `boolean` | `false` | `draft: true` only. Keeps committed entry rows in the sticky entry block, out of the body's sort and out of the row count, until the save. Off, a committed entry row becomes a body row. |
|
|
20
20
|
| `cellSelection` | `"none" \| "single" \| "range"` | `"single"` while `editing` is set | Editing turns the cell cursor on; set it explicitly to override. |
|
|
21
21
|
|
|
22
22
|
Passing any other member of `editing` without `mode` is a compile error, and
|
|
@@ -28,8 +28,7 @@ the type.
|
|
|
28
28
|
| Name | Argument | What it does |
|
|
29
29
|
| --- | --- | --- |
|
|
30
30
|
| `editing.onCommit` | `{ rowId, value, original, changes, source }` | Applies one row's change. Reject to keep the draft and show the error. |
|
|
31
|
-
| `editing.onSaveDrafts` | `{ updated, created, deleted }` | `draft: true` only. One call for the whole draft store. Without it, `saveDrafts` loops `editing.onCommit`, `editing.onRowAdd` and `editing.onRowDelete`. |
|
|
32
|
-
| `editing.onCommitDrafts` | `{ updated, created, deleted }` | **Deprecated** - renamed to `onSaveDrafts`. Still honoured; the new name wins if both are set. |
|
|
31
|
+
| `editing.onSaveDrafts` | `{ updated, created, deleted }` | `draft: true` only. One call for the whole draft store. May return a `TMDataGridSaveDraftsResponse` naming the ids that failed; they stay in the draft store. Without it, `saveDrafts` loops `editing.onCommit`, `editing.onRowAdd` and `editing.onRowDelete`. |
|
|
33
32
|
| `editing.onRowAdd` | `{ tempId, value }` | Commits an entry row. Mint the real id here. |
|
|
34
33
|
| `editing.onRowDelete` | `{ rowId, row }` | Deletes a row. Shows the trash; under `draft: true`, `onSaveDrafts` shows it too. |
|
|
35
34
|
|
|
@@ -54,24 +53,25 @@ path, which may be dotted.
|
|
|
54
53
|
| Member | Signature | Notes |
|
|
55
54
|
| --- | --- | --- |
|
|
56
55
|
| `begin` | `({ rowId, columnId }) => void` | Row mode opens the entire row either way. `columnId` selects which cell takes the caret; `null` (the pencil) uses its first editable one. On a committed row it reopens it, taking it back out of the draft store. |
|
|
57
|
-
| `commit` | `(rowId) => Promise<boolean>` | The OK gesture: submits the row's form. `false` keeps it open with its errors, and the message outlives the editor that found it. Under `draft: true` a pass
|
|
58
|
-
| `commitAll` | `() => Promise<
|
|
59
|
-
| `saveDrafts` | `() => Promise<
|
|
56
|
+
| `commit` | `(rowId) => Promise<boolean>` | The OK gesture: submits the row's form. `false` keeps it open with its errors, and the message outlives the editor that found it. Under `draft: true` a pass commits the row into the draft store - no consumer callback runs until `saveDrafts`. Column rules run whether or not an editor is mounted. |
|
|
57
|
+
| `commitAll` | `() => Promise<TMDataGridCommitAllResult>` | Submits every open row. Rows that fail stay open. Resolves `{ ok, committed, open }`: every row open at the call is in exactly one list, and `ok` is `false` when one stayed open. |
|
|
58
|
+
| `saveDrafts` | `() => Promise<TMDataGridSaveDraftsResult>` | Sends the draft store, re-running `editing.tableValidators` only; a committed row that fails is reopened with its errors. Open rows are left alone and stay open. Resolves `{ ok, saved, kept, reopened }` over row ids and temp ids, all kinds mixed: `saved` left the draft store, `kept` is still in it for the next save (reported failed by `onSaveDrafts`, sent when it threw, or a deletion whose per-row `onRowDelete` threw), `reopened` is open again with an error (a table rule, or a throwing per-row `onCommit` / `onRowAdd`). `ok` is `false` when anything was kept or reopened. A call during a save joins it and resolves the same result. |
|
|
60
59
|
| `cancel` | `(rowId) => void` | Drops one draft. |
|
|
61
60
|
| `cancelAll` | `() => void` | Drops every draft. |
|
|
62
61
|
| `deactivate` | `() => void` | Closes the editor without touching the draft, as blur does under `"cellConfirm"`. |
|
|
63
|
-
| `submitAll` | `() => Promise<boolean>` | **Deprecated** - `commitAll()` then `saveDrafts()`. |
|
|
64
62
|
| `clearCell` | `(rowId, columnId) => Promise<boolean>` | What Delete does: writes the type's empty value and commits. |
|
|
65
|
-
| `setCellValue` | `(rowId, columnId, value) => Promise<boolean>` | Writes one cell and commits the row, with no editor - toolbar actions and bulk fills. The row need not be mounted; a row inside a collapsed group takes the write. `value` is the stored value, so `meta.edit.mapValue` does not run and `meta.edit.validate` does. Under `draft: true` the row
|
|
63
|
+
| `setCellValue` | `(rowId, columnId, value) => Promise<boolean>` | Writes one cell and commits the row, with no editor - toolbar actions and bulk fills. The row need not be mounted; a row inside a collapsed group takes the write. `value` is the stored value, so `meta.edit.mapValue` does not run and `meta.edit.validate` does. Under `draft: true` the row is committed into the draft store like any hand-made edit. `false` when the cell takes no edit, or when validation refused the value and left the row open with its errors. |
|
|
66
64
|
| `setRowValues` | `(rowId, values) => Promise<boolean>` | `setCellValue` for several cells of one row in a single commit - one consumer call and one draft entry. Keys are column ids. All or nothing: if any named cell takes no edit, nothing is written and it resolves `false`. |
|
|
67
65
|
| `addRow` | `(values?) => string` | Opens an entry row, returns its `tempId`. `values` overrides `editing.newRowDefaults` key by key for that row; with no argument the row is `newRowDefaults` alone. |
|
|
68
|
-
| `addRows` | `(rows, options?) => Promise<
|
|
69
|
-
| `deleteRow` | `(rowId) => void` | `editing.onRowDelete`, or a deletion mark under `draft: true`.
|
|
66
|
+
| `addRows` | `(rows, options?) => Promise<TMDataGridAddRowsResult>` | Opens a batch in one write, each row seeded as `addRow` seeds. `{ commit: true }` submits each as it lands - valid rows commit, invalid ones stay open with their errors. Resolves `{ ok, committed, open }`; `ok` is `false` when a row stayed open. |
|
|
67
|
+
| `deleteRow` | `(rowId) => void` | `editing.onRowDelete`, or a deletion mark under `draft: true`. Idempotent: a second call leaves the row marked; `restoreRow` is the undo. |
|
|
70
68
|
| `canEditCell` | `(row, column) => boolean` | The check the built-in controls use. Both halves: the column's and the row's. |
|
|
71
69
|
| `canEditRow` | `(row) => boolean` | The pencil's gate. |
|
|
72
70
|
| `isColumnEditable` | `(column) => boolean` | The column's half alone, with no row in hand: it maps to a field, `editing.columns` lists it when that is set, and `meta.edit.enabled` is not `false`. |
|
|
73
71
|
| `canDeleteRows` | `() => boolean` | Whether the delete control should be shown. |
|
|
74
|
-
| `getForm` | `(rowId) => TMDataGridRowEditForm \| undefined` | The row's live `FormApi
|
|
72
|
+
| `getForm` | `(rowId) => TMDataGridRowEditForm \| undefined` | The open row's live `FormApi`; `undefined` for a committed row. |
|
|
73
|
+
| `getRowValues` | `(rowId) => TData \| undefined` | The row as shown: the open form's values, else the committed draft, else the `data` value. `undefined` for an unknown row. |
|
|
74
|
+
| `getRows` | `() => ReadonlyArray<TMDataGridEditRowSnapshot>` | Every row as shown, from the core row model: drafts overlaid, entry rows appended, deletion-marked rows included and flagged `deleted`. |
|
|
75
75
|
| `state` | `TMDataGridEditState` | Snapshot, for reads outside React. |
|
|
76
76
|
| `store` | `Store<TMDataGridEditState>` | For `useSelector`. |
|
|
77
77
|
|
|
@@ -81,10 +81,10 @@ path, which may be dotted.
|
|
|
81
81
|
type TMDataGridEditState = {
|
|
82
82
|
// The cell the last open gesture named - where the caret goes.
|
|
83
83
|
active: { rowId: string; columnId: string | null } | null;
|
|
84
|
-
//
|
|
85
|
-
// here and not in `committedRowIds`.
|
|
84
|
+
// Every row the grid holds work for: open rows and committed rows. A row
|
|
85
|
+
// is *open* when it is in here and not in `committedRowIds`.
|
|
86
86
|
openRowIds: ReadonlyArray<string>;
|
|
87
|
-
// One `TMDataGridEditRowProjection` per
|
|
87
|
+
// One `TMDataGridEditRowProjection` per row in `openRowIds`.
|
|
88
88
|
rows: Record<
|
|
89
89
|
string,
|
|
90
90
|
{
|
|
@@ -96,13 +96,20 @@ type TMDataGridEditState = {
|
|
|
96
96
|
values: TMDataGridRowData;
|
|
97
97
|
}
|
|
98
98
|
>;
|
|
99
|
-
// The draft store's edit slice: existing rows
|
|
100
|
-
//
|
|
99
|
+
// The draft store's edit slice: existing rows that passed their commit,
|
|
100
|
+
// held as values for `saveDrafts`. Empty without `editing.draft`.
|
|
101
101
|
committedRowIds: ReadonlyArray<string>;
|
|
102
|
+
// What a committed row *is* to the table: the draft store's values per row,
|
|
103
|
+
// snapshotted at each commit and kept across a reopen, so the row holds its
|
|
104
|
+
// place until it commits again or is dropped. The grid feeds these to the
|
|
105
|
+
// table in place of the consumer's records.
|
|
106
|
+
committedValues: Readonly<Record<string, TMDataGridRowData>>;
|
|
102
107
|
// `committed` is "in the store, awaiting the save". Without `editing.draft`
|
|
103
108
|
// a commit adds through `onRowAdd`, so it stays `false`.
|
|
104
109
|
newRows: ReadonlyArray<{ tempId: string; committed: boolean }>;
|
|
105
110
|
deletedRowIds: ReadonlyArray<string>;
|
|
111
|
+
// `true` while `saveDrafts` is in flight.
|
|
112
|
+
isSaving: boolean;
|
|
106
113
|
};
|
|
107
114
|
```
|
|
108
115
|
|
|
@@ -114,12 +121,13 @@ type TMDataGridEditState = {
|
|
|
114
121
|
| `TMDataGridDraftActions` | Export | The same component, for use outside the namespace. |
|
|
115
122
|
| `EDIT_COLUMN_ID` | Export | `"__edit__"`, the generated edit lane's id. |
|
|
116
123
|
| `clearedValueForType` | Export | `(type) => unknown` - what Delete writes per column type. |
|
|
124
|
+
| `hasPendingEdits` | Export | `(state) => boolean` - unsaved work: an open row with a changed value, any entry row, the draft store, or a save in flight. `useSelector(grid.edit.store, hasPendingEdits)` for a navigation blocker; do not use `openRowIds.length`, which also counts committed rows and rows that are only open. |
|
|
117
125
|
| `getEditFieldName` | Export | `(column) => string` - the data path a column's edits write to. |
|
|
118
126
|
| `normalizeFieldValidate` | Export | `(validate) => validators` - a bare schema into Form's shape. |
|
|
119
127
|
| `TMDataGridStringEditor` … `TMDataGridMultiSelectEditor` | Exports | The six built-in editors, for wrapping. |
|
|
120
128
|
|
|
121
129
|
Types: `TMDataGridEditMode`, `TMDataGridEditApi`, `TMDataGridEditState`,
|
|
122
|
-
`TMDataGridEditCommitArgs`, `
|
|
130
|
+
`TMDataGridEditCommitArgs`, `TMDataGridSaveDraftsArgs`,
|
|
123
131
|
`TMDataGridEditChange`, `TMDataGridEditorArgs`, `TMDataGridEditorComponent`,
|
|
124
132
|
`TMDataGridEditField`, `TMDataGridEditRowProjection`, `TMDataGridFieldValidate`,
|
|
125
133
|
`TMDataGridRowValidators`, `TMDataGridRowEditForm`, `TMDataGridRowAddArgs`,
|
|
@@ -143,11 +151,11 @@ The trash itself shows when the deletion has somewhere to report to:
|
|
|
143
151
|
mark is part of the save.
|
|
144
152
|
|
|
145
153
|
What the lane holds follows the row's state, one axis each. An open row shows
|
|
146
|
-
the mode's own controls, `save-row` and `cancel-row`. A
|
|
154
|
+
the mode's own controls, `save-row` and `cancel-row`. A committed row shows
|
|
147
155
|
`row-state` instead, whose `data-state` is `new`, `edited` or `deleted`,
|
|
148
156
|
together with `revert-row` on an edited row, `restore-row` on one marked for
|
|
149
157
|
deletion, and `edit-row` plus `discard-new-row` on an entered new row - never a
|
|
150
|
-
save, which the engine would only
|
|
158
|
+
save, which the engine would only commit again. A committed row hides `delete-row`.
|
|
151
159
|
Every control carries a tooltip from the labels, `revertRow`, `rowStateNew`,
|
|
152
160
|
`rowStateEdited` and `rowStateDeleted` among them.
|
|
153
161
|
|
|
@@ -156,11 +164,11 @@ Every control carries a tooltip from the labels, `revertRow`, `rowStateNew`,
|
|
|
156
164
|
| Name | Kind | What it is |
|
|
157
165
|
| --- | --- | --- |
|
|
158
166
|
| `--dg-entry-height` | CSS variable | Height of the sticky entry block. From `size`. |
|
|
159
|
-
| `--dg-row-new-bg` | CSS variable | Background of
|
|
167
|
+
| `--dg-row-new-bg` | CSS variable | Background of a committed new row, in the body or the entry block. A green tint. |
|
|
160
168
|
| `data-deleted` | Row attribute | On a row marked for deletion under `draft: true`. |
|
|
161
169
|
| `data-dirty` | Row attribute | On a body row holding a dirty draft. Also on the cell whose field is dirty. |
|
|
162
|
-
| `data-new`
|
|
163
|
-
| `data-
|
|
170
|
+
| `data-new` | Row attribute | On a body row that is a committed new row, and on an entry row. |
|
|
171
|
+
| `data-committed` | Entry row attribute | On an entry row once it is committed, awaiting the save. Seen only under `editing.newRowsSticky`. |
|
|
164
172
|
| `data-dg-part="editor-input"` | Part | The control inside an editing cell. |
|
|
165
173
|
| `data-dg-part="save-row"` / `"cancel-row"` | Parts | The edit lane's buttons on an open row, with `data-row-id`. |
|
|
166
174
|
| `data-dg-part="row-state"` | Part | The draft store's change marker, with `data-row-id` and `data-state` of `new`, `edited` or `deleted`. |
|
|
@@ -63,11 +63,11 @@ Bind any control to `field` exactly as inside any TanStack Form:
|
|
|
63
63
|
`field.state.value`, `field.state.meta.errors`, `field.handleChange`,
|
|
64
64
|
`field.handleBlur`.
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
binds
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
The message itself is the host's: it shows in a tooltip on the editor, opened
|
|
67
|
+
by focus and by hover, for a custom editor as much as a built-in one. What an
|
|
68
|
+
editor binds is the invalid state - the built-ins pass a boolean to the input's
|
|
69
|
+
`error` prop for the border alone, and an editor that binds nothing looks
|
|
70
|
+
unchanged while the commit is refused.
|
|
71
71
|
|
|
72
72
|
```tsx
|
|
73
73
|
import { Slider } from "@mantine/core";
|
|
@@ -193,8 +193,13 @@ meta: { edit: { validate: z.string().min(2, "At least two characters") } }
|
|
|
193
193
|
// Object form: pick the trigger.
|
|
194
194
|
meta: { edit: { validate: { onBlur: z.string().email("Not an email address") } } }
|
|
195
195
|
|
|
196
|
-
// A plain function works too.
|
|
197
|
-
meta: {
|
|
196
|
+
// A plain function works too. `value` is typed `never`, so annotate the parameter.
|
|
197
|
+
meta: {
|
|
198
|
+
edit: {
|
|
199
|
+
validate: ({ value }: { value: unknown }) =>
|
|
200
|
+
typeof value === "number" && value > 0 ? undefined : "Must be positive",
|
|
201
|
+
},
|
|
202
|
+
}
|
|
198
203
|
```
|
|
199
204
|
|
|
200
205
|
`normalizeFieldValidate(validate)` is exported for consumers building their own
|
|
@@ -224,8 +229,8 @@ const grid = useTMDataGrid({
|
|
|
224
229
|
```
|
|
225
230
|
|
|
226
231
|
Issues with a path land on the matching column's cell; pathless issues land on
|
|
227
|
-
the row, where the message shows in the edit lane's tooltip
|
|
228
|
-
row's
|
|
232
|
+
the row, where the message shows in the edit lane's tooltip on the open
|
|
233
|
+
row's ✓. A nested schema's issues follow the
|
|
229
234
|
same rule, so a `address.city` issue lands on the column whose `editField` is
|
|
230
235
|
`"address.city"`.
|
|
231
236
|
|
|
@@ -261,9 +266,9 @@ row's cells. `onSubmit` runs first, and its failure stands without
|
|
|
261
266
|
`onSubmitAsync` running. Errors land on the committing row only.
|
|
262
267
|
|
|
263
268
|
The rules run at every commit - typed, ✓, `edit.setCellValue`, an entry
|
|
264
|
-
row's - after the row's own validators, and again for every
|
|
265
|
-
`saveDrafts
|
|
266
|
-
|
|
269
|
+
row's - after the row's own validators, and again for every committed row during
|
|
270
|
+
`saveDrafts`, the only rules that run there: a committed row that a later edit
|
|
271
|
+
invalidated is reopened with its errors, and the save reports it in `reopened`.
|
|
267
272
|
|
|
268
273
|
## Server-side errors
|
|
269
274
|
|
|
@@ -283,9 +288,9 @@ rowValidators: {
|
|
|
283
288
|
},
|
|
284
289
|
```
|
|
285
290
|
|
|
286
|
-
A commit blocked by validation keeps the editor open with the message
|
|
287
|
-
|
|
288
|
-
row.
|
|
291
|
+
A commit blocked by validation keeps the editor open, invalid, with the message
|
|
292
|
+
in its tooltip. A rejected `editing.onCommit` keeps the draft too, with the
|
|
293
|
+
error on the row.
|
|
289
294
|
|
|
290
295
|
## Where the state shows
|
|
291
296
|
|
|
@@ -294,11 +299,13 @@ row.
|
|
|
294
299
|
| The cell's own value | A held draft is displayed: the cell renders the draft value through the column's `cell` renderer, in every mode |
|
|
295
300
|
| Blue cell corner | The field is dirty against its original value |
|
|
296
301
|
| Red cell corner | The field carries a validation error |
|
|
297
|
-
|
|
|
302
|
+
| Field error message | In a tooltip on the open editor, shown while the input has focus and on hover |
|
|
303
|
+
| Row error text | A pathless rule failed, or a commit was rejected. In the lane's tooltip, on the open row's ✓ |
|
|
298
304
|
| `data-dirty` on the row | The row holds a dirty draft |
|
|
299
305
|
|
|
300
306
|
The same information is readable from `edit.store`: `rows[rowId].dirtyFields`,
|
|
301
307
|
`rows[rowId].errorFields`, `rows[rowId].errorMessages` (`{ field, message }`
|
|
302
308
|
pairs), `rows[rowId].hasRowError`, `rows[rowId].isSubmitting`, and
|
|
303
309
|
`rows[rowId].values` for the draft itself. The pathless message's text is not
|
|
304
|
-
in the store; read it from `edit.getForm(rowId)?.state.errors
|
|
310
|
+
in the store; read it from `edit.getForm(rowId)?.state.errors` on the open
|
|
311
|
+
row that carries it.
|