@jielga/tmdatagrid 2.0.0-beta.9 → 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 +1281 -768
- package/dist/index.js +4607 -3250
- 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 +46 -47
- package/skills/columns/SKILL.md +90 -34
- package/skills/data/SKILL.md +86 -16
- package/skills/editing/SKILL.md +67 -40
- package/skills/editing/references/common-mistakes.md +77 -69
- package/skills/editing/references/editing-api.md +22 -19
- 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 +7 -7
- 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/components → components}/TMDataGrid.tsx +38 -21
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
- 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 +5 -51
- package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
- 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 +5 -69
- 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 +11 -48
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
- 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}/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 +1107 -460
- 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} +69 -35
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
- package/src/useTMDataGridExport.ts +78 -0
- 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/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
- /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}/capabilities.ts +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}/editorFocus.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
package/skills/data/SKILL.md
CHANGED
|
@@ -10,17 +10,23 @@ description: >
|
|
|
10
10
|
onReachEnd is better for loading more, the header and pinned-lane depth
|
|
11
11
|
shadows, and the four empty states in precedence order with meta.loading,
|
|
12
12
|
renderEmptyState, hasActiveFilters, TMDataGrid.LoadingIndicator and
|
|
13
|
-
TMDataGrid.SummaryCount
|
|
14
|
-
|
|
13
|
+
TMDataGrid.SummaryCount, and export: TMDataGrid.Menu.Export and
|
|
14
|
+
Menu.ExportSelected, useTMDataGridExport, exportGrid, exportOptions, the
|
|
15
|
+
csvExcel / csv / tsv / json formats, meta.exportValue and meta.enableExport.
|
|
16
|
+
Load when adding a pager, tuning scrolling, scrolling to a row, deciding what
|
|
17
|
+
an empty grid should say, or exporting rows to Excel or CSV.
|
|
15
18
|
metadata:
|
|
16
19
|
type: core
|
|
17
20
|
library: '@jielga/tmdatagrid'
|
|
18
|
-
library_version: '2.0.0
|
|
21
|
+
library_version: '2.0.0'
|
|
19
22
|
sources:
|
|
20
|
-
- 'Jielga/TMDataGrid:
|
|
21
|
-
- 'Jielga/TMDataGrid:
|
|
22
|
-
- 'Jielga/TMDataGrid:
|
|
23
|
-
- 'Jielga/TMDataGrid:
|
|
23
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/pagination.md'
|
|
24
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/scrolling.md'
|
|
25
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/loading-and-empty.md'
|
|
26
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/export.md'
|
|
27
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/components/TMDataGridFooter.tsx'
|
|
28
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/pagination.ts'
|
|
29
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/export.ts'
|
|
24
30
|
---
|
|
25
31
|
|
|
26
32
|
# TMDataGrid - Pagination, scrolling and empty states
|
|
@@ -77,7 +83,10 @@ pieces, already wired.
|
|
|
77
83
|
`Controls.PageSize`, `Controls.Range` and `Controls.Pager` are what the default
|
|
78
84
|
footer renders, in that order, so a custom layout can keep the parts it wants
|
|
79
85
|
instead of rebuilding them. They behave exactly as before, including greying out
|
|
80
|
-
under a suspended pager.
|
|
86
|
+
under a suspended pager. `Controls.PageNumber` - the "Page 3 of 200" label a
|
|
87
|
+
server-paged grid usually shows in place of a row range - is a fourth control,
|
|
88
|
+
not in the default footer; put it in through the slot rather than writing it by
|
|
89
|
+
hand.
|
|
81
90
|
|
|
82
91
|
`state` carries `pageIndex`, `pageSize`, `pageCount`, `rowCount`,
|
|
83
92
|
`canPreviousPage`, `canNextPage`, `from`, `to` and `isPagingActive`. `actions`
|
|
@@ -178,6 +187,67 @@ server-driven grid refetching with rows still on screen keeps showing them.
|
|
|
178
187
|
total, where the total is `meta.totalRowCount` when provided and the pre-filtered
|
|
179
188
|
count otherwise.
|
|
180
189
|
|
|
190
|
+
## Export
|
|
191
|
+
|
|
192
|
+
Every filtered and sorted row across every page, or the selected rows, as a
|
|
193
|
+
file. The built-in entry points are menu items; a button of your own uses the
|
|
194
|
+
hook.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
const grid = useTMDataGrid({
|
|
198
|
+
data,
|
|
199
|
+
columns,
|
|
200
|
+
exportOptions: { format: csvFormat(), fileName: "employees" },
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
<TMDataGrid.Menu>
|
|
204
|
+
<TMDataGrid.Menu.Export />
|
|
205
|
+
<TMDataGrid.Menu.ExportSelected />
|
|
206
|
+
</TMDataGrid.Menu>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
function ExportButton() {
|
|
211
|
+
const { exportAll, exportSelected, selectedCount, canExportSelected } =
|
|
212
|
+
useTMDataGridExport();
|
|
213
|
+
return <Button onClick={() => void exportAll()}>Export</Button>;
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
What is written: the data columns in render order (never the generated lanes,
|
|
218
|
+
never a column with `meta.enableExport: false`), every row after filtering and
|
|
219
|
+
sorting (a grouped grid writes the records under every group, never the group
|
|
220
|
+
rows), and each cell's **value** rather than what it renders -
|
|
221
|
+
`meta.exportValue: ({ value, row, column }) => unknown` substitutes one.
|
|
222
|
+
`columns` on `exportOptions`, the items and the functions is `"visible"` (the
|
|
223
|
+
default), `"all"` (hidden columns too) or a list of ids; `columns="custom"` on
|
|
224
|
+
a menu item opens a picker instead - every exportable column, the visible ones
|
|
225
|
+
ticked, Export and Cancel - driven by `ui.state.exportPicker` and
|
|
226
|
+
`ui.actions.openExportPicker`. `TMDataGrid.Menu.ExportSelected` counts and
|
|
227
|
+
writes the ticked rows of the current view, in grid order; it renders nothing
|
|
228
|
+
when row selection is off.
|
|
229
|
+
|
|
230
|
+
Formats, each a `TMDataGridExportFormat` from a factory:
|
|
231
|
+
|
|
232
|
+
| Factory | Writes |
|
|
233
|
+
| --- | --- |
|
|
234
|
+
| `csvExcelFormat()` (default) | BOM, `sep=;` line, CRLF, `;` fields, `,` decimal - opens straight into columns in Excel. `separator`, `decimalComma` for another locale. |
|
|
235
|
+
| `csvFormat()` | RFC 4180: `,` fields, `.` decimal, BOM, no `sep=` line - for Google Sheets, Numbers and tooling. |
|
|
236
|
+
| `tsvFormat()` | Tab-separated, the clipboard shape as a file. |
|
|
237
|
+
| `jsonFormat()` | One object per row keyed by column label, numbers as numbers, dates as ISO strings. |
|
|
238
|
+
| `xlsxFormat()` | Excel workbook, from the separate `@jielga/tmdatagrid-xlsx` package. |
|
|
239
|
+
|
|
240
|
+
The text formats guard against formula injection by default: a value starting
|
|
241
|
+
with `=`, `+`, `-` or `@` that is not a number is prefixed with `'`.
|
|
242
|
+
`escapeFormulas: false` on the format turns that off.
|
|
243
|
+
|
|
244
|
+
`exportGrid({ table, rows: "all" | "selected" | rows, options })` is the same
|
|
245
|
+
export for code outside a component; `buildExportData` is the step before the
|
|
246
|
+
file. A format of your own is `{ id, extension, mimeType, write(data, { includeHeaders }) }`
|
|
247
|
+
returning a string, a `Blob`, or a promise of either.
|
|
248
|
+
|
|
249
|
+
Source: `packages/tmdatagrid/docs/export.md`.
|
|
250
|
+
|
|
181
251
|
## Common mistakes
|
|
182
252
|
|
|
183
253
|
### CRITICAL Turning pagination on to make a large grid fast
|
|
@@ -186,7 +256,7 @@ Virtualization is already unconditional, so paging a 200 000-row grid changes
|
|
|
186
256
|
nothing about rendering cost. It only changes how users navigate. Enable it when
|
|
187
257
|
they should move page by page, not for performance.
|
|
188
258
|
|
|
189
|
-
Source: `
|
|
259
|
+
Source: `packages/tmdatagrid/docs/pagination.md`, `packages/tmdatagrid/docs/scrolling.md`.
|
|
190
260
|
|
|
191
261
|
### CRITICAL A variable row height
|
|
192
262
|
|
|
@@ -207,7 +277,7 @@ Correct:
|
|
|
207
277
|
useTMDataGrid({ data, columns, meta: { rowHeight: 64 } });
|
|
208
278
|
```
|
|
209
279
|
|
|
210
|
-
Source: `
|
|
280
|
+
Source: `packages/tmdatagrid/docs/scrolling.md` (Row height).
|
|
211
281
|
|
|
212
282
|
### HIGH `scrollIntoView` on a row that is not mounted
|
|
213
283
|
|
|
@@ -223,7 +293,7 @@ grid.scrollToRow({ rowId: "4000", align: "center" });
|
|
|
223
293
|
`scrollToRow` returns `false` when the row is not in the current view (filtered
|
|
224
294
|
out, on another page, or an id matching no row) and nothing scrolled.
|
|
225
295
|
|
|
226
|
-
Source: `
|
|
296
|
+
Source: `packages/tmdatagrid/docs/scrolling.md` (Scrolling to a row).
|
|
227
297
|
|
|
228
298
|
### HIGH Loading more rows from `onScrollToBottom`
|
|
229
299
|
|
|
@@ -231,7 +301,7 @@ It fires at the very bottom, so the user waits at the end of the list for the
|
|
|
231
301
|
fetch. `onReachEnd` fires a number of rows earlier and latches per row count, so
|
|
232
302
|
a pending fetch is not requested twice.
|
|
233
303
|
|
|
234
|
-
Source: `
|
|
304
|
+
Source: `packages/tmdatagrid/docs/scrolling.md` (Edge callbacks).
|
|
235
305
|
|
|
236
306
|
### HIGH `SummaryCount` reporting the page under manual pagination
|
|
237
307
|
|
|
@@ -250,7 +320,7 @@ useTMDataGrid({
|
|
|
250
320
|
});
|
|
251
321
|
```
|
|
252
322
|
|
|
253
|
-
Source: `
|
|
323
|
+
Source: `packages/tmdatagrid/docs/loading-and-empty.md` (Counting what is there).
|
|
254
324
|
|
|
255
325
|
### MEDIUM Rendering an empty message while data is loading
|
|
256
326
|
|
|
@@ -264,7 +334,7 @@ Correct:
|
|
|
264
334
|
useTMDataGrid({ data, columns, meta: { loading: isFetching } });
|
|
265
335
|
```
|
|
266
336
|
|
|
267
|
-
Source: `
|
|
337
|
+
Source: `packages/tmdatagrid/docs/loading-and-empty.md` (What wins).
|
|
268
338
|
|
|
269
339
|
### MEDIUM Trusting the pager while grouped
|
|
270
340
|
|
|
@@ -272,7 +342,7 @@ Source: `src/docs/loading-and-empty.md` (What wins).
|
|
|
272
342
|
grid is rendering the whole tree. A custom pager must read
|
|
273
343
|
`isPagingActive(table, features)` rather than the page count.
|
|
274
344
|
|
|
275
|
-
Source: `
|
|
345
|
+
Source: `packages/tmdatagrid/docs/pagination.md` (Grouping suspends it).
|
|
276
346
|
|
|
277
347
|
## Reference
|
|
278
348
|
|
|
@@ -283,7 +353,7 @@ Source: `src/docs/pagination.md` (Grouping suspends it).
|
|
|
283
353
|
| `rowCount` | Table option | `number` | – | The true total, required under `manualPagination`. |
|
|
284
354
|
| `initialState.pagination` | Table option | `{ pageIndex, pageSize }` | `{ 0, 25 }` | Where paging starts. A `data` slice, so it persists. |
|
|
285
355
|
| `onPaginationChange` | Table option | `OnChangeFn` | – | Controls the pagination state. |
|
|
286
|
-
| `TMDataGrid.Footer` | Component | `pageSizeOptions`, `
|
|
356
|
+
| `TMDataGrid.Footer` | Component | `pageSizeOptions`, `renderPagination`, Mantine `BoxProps` | `[10, 25, 50, 100]` | The footer bar. Renders nothing when paging is off. Style props set on the bar. |
|
|
287
357
|
| `Footer` `renderPagination` | Slot | `({ state, actions, Controls }) => ReactNode` | Built-in pager | Replaces the pager, and hands over its pieces. |
|
|
288
358
|
| `getTMDataGridPaginationApi` | Export | `(table) => { state, actions }` | – | The pager API, outside the Footer. |
|
|
289
359
|
| `TMDataGridPaginationState` · `TMDataGridPaginationActions` · `TMDataGridPaginationControls` | Exports | types | – | The three parts of the slot argument. |
|
package/skills/editing/SKILL.md
CHANGED
|
@@ -18,13 +18,15 @@ description: >
|
|
|
18
18
|
metadata:
|
|
19
19
|
type: core
|
|
20
20
|
library: '@jielga/tmdatagrid'
|
|
21
|
-
library_version: '2.0.0
|
|
21
|
+
library_version: '2.0.0'
|
|
22
22
|
sources:
|
|
23
|
-
- 'Jielga/TMDataGrid:
|
|
24
|
-
- 'Jielga/TMDataGrid:
|
|
25
|
-
- 'Jielga/TMDataGrid:
|
|
26
|
-
- 'Jielga/TMDataGrid:
|
|
27
|
-
- 'Jielga/TMDataGrid:
|
|
23
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/editing.md'
|
|
24
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/draft-store.md'
|
|
25
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/adding-rows.md'
|
|
26
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/query-builder.md'
|
|
27
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/docs/editors.md'
|
|
28
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/core/editEngine.ts'
|
|
29
|
+
- 'Jielga/TMDataGrid:packages/tmdatagrid/src/useTMDataGrid.tsx'
|
|
28
30
|
---
|
|
29
31
|
|
|
30
32
|
# TMDataGrid - Editing
|
|
@@ -34,9 +36,10 @@ happen. Three facts decide every wiring question below:
|
|
|
34
36
|
|
|
35
37
|
- **The grid never mutates `data`.** `editing.onCommit` applies the change
|
|
36
38
|
wherever the data lives, and the updated rows arrive back through `data`.
|
|
37
|
-
- **One row, one form.** Each
|
|
38
|
-
row id and living outside the DOM, so a draft
|
|
39
|
-
filtering.
|
|
39
|
+
- **One row, one form, while it is open.** Each row being edited gets its own
|
|
40
|
+
TanStack Form, keyed by row id and living outside the DOM, so a draft
|
|
41
|
+
survives scrolling, sorting and filtering. A committed row holds values, not
|
|
42
|
+
a form.
|
|
40
43
|
- **`getRowId` is required** once `editing` is set, and it must be the record's
|
|
41
44
|
own identity. Drafts are keyed by it.
|
|
42
45
|
|
|
@@ -85,13 +88,15 @@ They are independent, and every pair is legal.
|
|
|
85
88
|
| `"cellConfirm"` | ✓ or Enter; Tab walks input, ✓, ✕ and then leaves, keeping the draft | ✕ or Escape | ✓ / ✕ beside the input |
|
|
86
89
|
| `"row"` | Save in the edit lane, or Enter | Cancel, or Escape | generated edit lane |
|
|
87
90
|
|
|
91
|
+
Leaving a cell commits it only once the value passes: a refused commit keeps the editor open, invalid, with the message in its tooltip, until the value is fixed or Escape drops it.
|
|
92
|
+
|
|
88
93
|
| `editing.draft` | Where a commit goes |
|
|
89
94
|
| --- | --- |
|
|
90
95
|
| `false` (default) | Straight out: `onCommit`, `onRowAdd`, `onRowDelete` |
|
|
91
96
|
| `true` | Into the grid's draft store, until `edit.saveDrafts()` sends the lot |
|
|
92
97
|
|
|
93
98
|
Which to pick: `"cell"` for spreadsheet feel; `"cellConfirm"` when a stray click must not fire a request; `"row"` when the row is the unit of the save or a rule spans two columns.
|
|
94
|
-
Add `draft: true` for many edits sent as one transaction - `{ mode: "row", draft: true }`
|
|
99
|
+
Add `draft: true` for many edits sent as one transaction - `{ mode: "row", draft: true }` commits a whole row from the lane's ✓, `{ mode: "cell", draft: true }` commits a row as the caret leaves it.
|
|
95
100
|
|
|
96
101
|
An editor opens on double-click, or with the cell cursor on the cell: Enter, F2,
|
|
97
102
|
or typing, where the first character replaces the value. Delete or Backspace
|
|
@@ -108,11 +113,11 @@ commit. Rows accumulate: opening a second row leaves the first open, and each
|
|
|
108
113
|
row's ✓ and ✕ act on that row alone.
|
|
109
114
|
|
|
110
115
|
Under `draft: true` nothing reaches a callback until `saveDrafts`.
|
|
111
|
-
The mode's own commit gesture
|
|
112
|
-
`edit.commit(rowId)`
|
|
113
|
-
A
|
|
116
|
+
The mode's own commit gesture puts the row in the draft store instead of sending it, Escape drops that one draft, and committed rows accumulate.
|
|
117
|
+
`edit.commit(rowId)` goes to the draft store too, so there is no per-row escape hatch to the consumer.
|
|
118
|
+
A committed row is displayed: the cell renders the draft value through the column's own `cell` renderer, with the blue corner marking it dirty and `data-dirty` on the row.
|
|
114
119
|
It is a row like any other to the table: sorting, filtering, quick search, grouping, aggregates, export, selection, the row counts, `edit.getRows()` and `editing.tableValidators` all read its draft values, and the row callbacks receive it with the draft as `row.original`.
|
|
115
|
-
A
|
|
120
|
+
A committed row that stops matching a filter leaves the view, and the Save bar still counts it.
|
|
116
121
|
`data` itself is never modified, and only top-level rows are overlaid - `getSubRows` children keep their `data` values.
|
|
117
122
|
An entry row is row-shaped in every mode - every editable cell open at once, the browser's Tab, and the lane's ✓ to enter it.
|
|
118
123
|
|
|
@@ -131,10 +136,22 @@ a list of row ids. Rows failing validation stay open either way.
|
|
|
131
136
|
|
|
132
137
|
`onSaveDrafts` decides how much of the store is cleared: returning nothing
|
|
133
138
|
saves everything, throwing saves nothing, and returning
|
|
134
|
-
`{ updated, created, deleted }`
|
|
135
|
-
`false`. Each key takes `false` for the
|
|
136
|
-
an unnamed id saved. A kept row stays
|
|
137
|
-
|
|
139
|
+
`{ updated, created, deleted }` (a `TMDataGridSaveDraftsResponse`) saves
|
|
140
|
+
everything except the ids reported `false`. Each key takes `false` for the
|
|
141
|
+
whole bucket or a map of id to result; an unnamed id saved. A kept row stays
|
|
142
|
+
committed, so the next `saveDrafts()` retries it.
|
|
143
|
+
|
|
144
|
+
`saveDrafts()` resolves a `TMDataGridSaveDraftsResult`, `{ ok, saved, kept, reopened }`.
|
|
145
|
+
Every id the save took from the draft store is in exactly one list - row ids for edits and deletions, temp ids for new rows, all kinds mixed:
|
|
146
|
+
|
|
147
|
+
- `saved` - left the draft store; the consumer accepted it
|
|
148
|
+
- `kept` - still in the draft store, still committed, retried by the next save: an id `onSaveDrafts` returned as failed, every id it was sent when it threw, or, without `onSaveDrafts`, a deletion whose `onRowDelete` threw
|
|
149
|
+
- `reopened` - open again with an error: a table rule rejected it, or on the per-row path its `onCommit` / `onRowAdd` threw
|
|
150
|
+
- `ok` - `true` when `kept` and `reopened` are both empty
|
|
151
|
+
|
|
152
|
+
On the per-row path a deletion always leaves the store and is reported in `saved`.
|
|
153
|
+
An empty store resolves `{ ok: true, saved: [], kept: [], reopened: [] }`.
|
|
154
|
+
Rows still open are in no list.
|
|
138
155
|
|
|
139
156
|
Rows carry `data-dirty` (values typed in), `data-draft` (committed, waiting for
|
|
140
157
|
Save), `data-deleted` and `data-new` - a committed new row in the body, or an
|
|
@@ -216,7 +233,11 @@ rowValidators: {
|
|
|
216
233
|
```
|
|
217
234
|
|
|
218
235
|
Pathed issues land on the matching cells, pathless ones on the row, and cell
|
|
219
|
-
corners mark both: blue for a dirty draft, red for a validation error.
|
|
236
|
+
corners mark both: blue for a dirty draft, red for a validation error. A
|
|
237
|
+
field's message shows in a tooltip on the open editor, which the host renders
|
|
238
|
+
for a custom editor as much as a built-in one; the plain-function form of a
|
|
239
|
+
validator types `value` as `never`, so annotate the parameter -
|
|
240
|
+
`({ value }: { value: unknown })`.
|
|
220
241
|
|
|
221
242
|
`editing.tableValidators` carries the rules that need the other rows - no
|
|
222
243
|
duplicate keys, no overlapping ranges, shares summing to a total. Its
|
|
@@ -225,8 +246,9 @@ duplicate keys, no overlapping ranges, shares summing to a total. Its
|
|
|
225
246
|
overlaid, committed new rows among them, the entry rows the table does not hold
|
|
226
247
|
appended, deletion-marked rows removed. Each row appears once. Same result
|
|
227
248
|
vocabulary as `rowValidators`; errors land on the committing row. The rules
|
|
228
|
-
re-run per
|
|
229
|
-
invalidated
|
|
249
|
+
re-run per committed row during `saveDrafts`, the only validation that runs
|
|
250
|
+
there: a committed row a later edit invalidated is reopened with its errors
|
|
251
|
+
and the save reports it in `reopened`.
|
|
230
252
|
|
|
231
253
|
```tsx
|
|
232
254
|
tableValidators: {
|
|
@@ -255,8 +277,8 @@ from `editing.newRowDefaults`. `edit.addRow(values)` overrides that seed key by
|
|
|
255
277
|
key, so `addRow()` opens the `newRowDefaults` row and `addRow(values)` opens it
|
|
256
278
|
with those fields filled in - pass a whole row to duplicate it. Enter, or the
|
|
257
279
|
lane's ✓, commits the add through `editing.onRowAdd`; under `draft: true` it
|
|
258
|
-
|
|
259
|
-
`saveDrafts` reports it in `
|
|
280
|
+
commits the row into the draft store, validated, and
|
|
281
|
+
`saveDrafts` reports it in `created`. Escape, or ✕, discards the entry. An entry
|
|
260
282
|
row never OK'd is not part of a save - it stays open.
|
|
261
283
|
|
|
262
284
|
Under `draft: true` a committed entry row leaves the entry block and becomes a
|
|
@@ -297,13 +319,14 @@ const grid = useTMDataGrid({
|
|
|
297
319
|
`edit.addRows(rows, options?)` opens a batch in one write. `{ commit: true }`
|
|
298
320
|
submits each row as it lands - the import case: valid rows are committed,
|
|
299
321
|
invalid ones stay open in the entry block with their errors, and the result
|
|
300
|
-
(`{ committed, open }`) says which went which way
|
|
301
|
-
even though the rows never had an
|
|
302
|
-
`meta.edit.validate` itself at
|
|
322
|
+
(`{ ok, committed, open }`) says which went which way; `ok` is `true` when
|
|
323
|
+
`open` is empty. Column rules are enforced even though the rows never had an
|
|
324
|
+
editor on screen, because the engine runs `meta.edit.validate` itself at
|
|
325
|
+
commit.
|
|
303
326
|
|
|
304
327
|
```tsx
|
|
305
|
-
const {
|
|
306
|
-
if (
|
|
328
|
+
const { ok, open } = await grid.edit.addRows(parsed, { commit: true });
|
|
329
|
+
if (!ok) notify(`${open.length} rows need attention`);
|
|
307
330
|
await grid.edit.saveDrafts();
|
|
308
331
|
```
|
|
309
332
|
|
|
@@ -318,8 +341,8 @@ it toggles a mark instead: the row renders struck through and inert
|
|
|
318
341
|
|
|
319
342
|
The generated edit lane (`EDIT_COLUMN_ID`, pinned right) appears when `editing.mode` is `"row"`, when `editing.draft` is on, or when `editing.onRowDelete` is set.
|
|
320
343
|
Nothing else adds it.
|
|
321
|
-
It holds one thing per axis: the mode's own controls while a row is open - Save and Cancel under `"row"` - and, once a row is
|
|
322
|
-
A
|
|
344
|
+
It holds one thing per axis: the mode's own controls while a row is open - Save and Cancel under `"row"` - and, once a row is committed, the row-state marker with Revert or Restore.
|
|
345
|
+
A committed row never offers a save.
|
|
323
346
|
The trash shows when the deletion has somewhere to report to: `onRowDelete` is set, or under `draft: true`, `onSaveDrafts` is.
|
|
324
347
|
If validation blocks a row, its marker - or the open row's ✓ - turns red with the message in the tooltip, which is where a pathless `rowValidators` message shows.
|
|
325
348
|
Every control carries a tooltip from the labels.
|
|
@@ -353,10 +376,10 @@ while editing is off, and works under any mode, not only draft.
|
|
|
353
376
|
```
|
|
354
377
|
|
|
355
378
|
`state` is
|
|
356
|
-
`{ draftCount, openCount, openRowIds,
|
|
357
|
-
`pendingCount` deprecated, reading as `draftCount + openCount`. `actions` is
|
|
379
|
+
`{ draftCount, openCount, openRowIds, isSubmitting, isSaving }`. `actions` is
|
|
358
380
|
`{ save, commitAll, discard, scrollToRow, scrollToFirstOpenRow }`, and
|
|
359
381
|
`Controls` is `{ Save, Discard, OpenRowsNote }`.
|
|
382
|
+
`actions.save` and `actions.commitAll` resolve what `edit.saveDrafts()` and `edit.commitAll()` resolve.
|
|
360
383
|
|
|
361
384
|
The grid is always virtualized, so an open row far down the list has no element
|
|
362
385
|
to scroll to. `actions.scrollToFirstOpenRow(align?)` moves the virtualizer to
|
|
@@ -376,16 +399,20 @@ row it reaches.
|
|
|
376
399
|
`setCellValue` / `setRowValues` / `clearCell`, `addRow` / `addRows` /
|
|
377
400
|
`deleteRow`, `getForm`, and `store` for `useSelector` (an example is under
|
|
378
401
|
[Submitting an outer form](#high-submitting-an-outer-form-while-the-grid-holds-a-draft)).
|
|
379
|
-
`
|
|
380
|
-
`
|
|
381
|
-
|
|
402
|
+
`commitAll()` resolves a `TMDataGridCommitAllResult`, `{ ok, committed, open }`: every row open at the call is in exactly one list, and `ok` is `true` when `open` is empty.
|
|
403
|
+
`commit(rowId)` alone resolves a plain boolean.
|
|
404
|
+
`edit.store` publishes each open or committed row's drafted values as
|
|
405
|
+
`rows[rowId].values`, which is what a cross-row check reads. A `cell`
|
|
406
|
+
renderer needs no lookup: its `row.original` is already the row as shown, and
|
|
407
|
+
`getRowValues(rowId)` is the same row for a handler with no cell context.
|
|
382
408
|
Every member with its signature, the gates, `isColumnEditable`, `deactivate`,
|
|
383
409
|
and the `edit.store` shape are in
|
|
384
410
|
[references/editing-api.md](references/editing-api.md#the-edit-engine).
|
|
385
411
|
|
|
386
|
-
`getForm` exposes
|
|
387
|
-
dirty state and errors with the inline cells, because it is the same
|
|
388
|
-
`FormApi`.
|
|
412
|
+
`getForm` exposes an open row's form: render it in a drawer and it shares
|
|
413
|
+
values, dirty state and errors with the inline cells, because it is the same
|
|
414
|
+
`FormApi`. It is `undefined` for a committed row, which holds values and no
|
|
415
|
+
form; `begin` reopens the row with a form seeded from them.
|
|
389
416
|
|
|
390
417
|
`edit.setCellValue(rowId, columnId, value)` writes one cell and commits its row with no editor open, which is what a toolbar action or a bulk fill wants.
|
|
391
418
|
The row need not be mounted, so a selected row inside a collapsed group takes the write like any other.
|
|
@@ -397,7 +424,7 @@ for (const row of grid.table.getSelectedRowModel().rows) {
|
|
|
397
424
|
}
|
|
398
425
|
```
|
|
399
426
|
|
|
400
|
-
Under `draft: true` each row
|
|
427
|
+
Under `draft: true` each row is committed into the draft store like any hand-made edit, with the same change markers and the same per-row revert, and the basket leaves through `saveDrafts`.
|
|
401
428
|
`value` is the stored value: no editor runs, so `meta.edit.mapValue` does not run either, while `meta.edit.validate` does.
|
|
402
429
|
Both resolve `false` when the cell takes no edit - no such row or column, `editing.columns` excludes it, `meta.edit.enabled` is off, or the row is not editable - and when validation refuses the value, which leaves the row open carrying its errors.
|
|
403
430
|
|
|
@@ -428,7 +455,6 @@ are in [references/common-mistakes.md](references/common-mistakes.md).
|
|
|
428
455
|
| CRITICAL | Expecting the grid to write into `data` - without `editing.onCommit` the cell reverts |
|
|
429
456
|
| CRITICAL | `getRowId` built from the row index - drafts follow the index, not the record |
|
|
430
457
|
| HIGH | A cell editor defined inside the component - a new type per render unmounts the editor |
|
|
431
|
-
| HIGH | A custom editor that binds no error text - a refused commit shows no message; bind `field.state.meta.errors` |
|
|
432
458
|
| HIGH | A cross-field rule under `mode: "cell"` - `rowValidators` needs `"row"` |
|
|
433
459
|
| HIGH | An `accessorFn` column with no `meta.edit.field` - it maps to nothing and stays read-only |
|
|
434
460
|
| HIGH | Swallowing the error in `editing.onCommit` - a resolved catch drops the draft |
|
|
@@ -437,6 +463,7 @@ are in [references/common-mistakes.md](references/common-mistakes.md).
|
|
|
437
463
|
| MEDIUM | A computed column frozen while a row is edited - `accessorFn` reads `data`; read the draft from `edit.store`'s `rows[rowId].values` |
|
|
438
464
|
| MEDIUM | Reading a commit's result as the saved value - it is a `boolean` about the form |
|
|
439
465
|
| MEDIUM | Expecting `editing.onRowDelete` to fire under draft - the mark waits for `saveDrafts` |
|
|
466
|
+
| MEDIUM | A custom editor that binds no invalid state - the host shows the message in a tooltip, but the control keeps its normal border; bind a boolean to `error` |
|
|
440
467
|
|
|
441
468
|
## References
|
|
442
469
|
|
|
@@ -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).
|