@jielga/tmdatagrid 2.0.0-beta.2 → 2.0.0-beta.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jielga/tmdatagrid",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.4",
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",
@@ -16,7 +16,7 @@ description: >
16
16
  metadata:
17
17
  type: core
18
18
  library: '@jielga/tmdatagrid'
19
- library_version: '2.0.0-beta.2'
19
+ library_version: '2.0.0-beta.4'
20
20
  sources:
21
21
  - 'Jielga/TMDataGrid:src/docs/styling.md'
22
22
  - 'Jielga/TMDataGrid:src/docs/toolbar.md'
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0-beta.2'
17
+ library_version: '2.0.0-beta.4'
18
18
  sources:
19
19
  - 'Jielga/TMDataGrid:src/docs/cell-selection.md'
20
20
  - 'Jielga/TMDataGrid:src/tmdatagrid/core/cellNavigation.ts'
@@ -16,7 +16,7 @@ description: >
16
16
  metadata:
17
17
  type: core
18
18
  library: '@jielga/tmdatagrid'
19
- library_version: '2.0.0-beta.2'
19
+ library_version: '2.0.0-beta.4'
20
20
  sources:
21
21
  - 'Jielga/TMDataGrid:src/docs/columns.md'
22
22
  - 'Jielga/TMDataGrid:src/docs/column-layout.md'
@@ -15,7 +15,7 @@ description: >
15
15
  metadata:
16
16
  type: core
17
17
  library: '@jielga/tmdatagrid'
18
- library_version: '2.0.0-beta.2'
18
+ library_version: '2.0.0-beta.4'
19
19
  sources:
20
20
  - 'Jielga/TMDataGrid:src/docs/pagination.md'
21
21
  - 'Jielga/TMDataGrid:src/docs/scrolling.md'
@@ -3,7 +3,7 @@ name: editing
3
3
  description: >
4
4
  Edit cells and rows in TMDataGrid. Covers the editing option with its four
5
5
  mode policies (cell, cellConfirm, row, draft), the required getRowId,
6
- editing.onCommit and editing.onCommitDrafts, why the grid never mutates data,
6
+ editing.onCommit and editing.onSaveDrafts, why the grid never mutates data,
7
7
  per-column gating with meta.edit.enabled and meta.edit.field, the six built-in
8
8
  editors picked by meta.type, custom editors through meta.edit.editor,
9
9
  per-keystroke value mapping with meta.edit.mapValue, field validation with
@@ -11,13 +11,14 @@ description: >
11
11
  Schema and Zod), adding and deleting rows with edit.addRow,
12
12
  editing.newRowDefaults, editing.onRowAdd and editing.onRowDelete, the
13
13
  generated edit lane, TMDataGrid.EditActions with its renderActions slot, and
14
- the public edit engine (begin, commit, cancel, submitAll, getForm, store).
15
- Load when making a grid editable, choosing an edit mode, wiring a save,
16
- writing a cell editor, validating an edit, or when cells will not open.
14
+ the public edit engine (begin, commit, commitAll, saveDrafts, addRows,
15
+ getForm, store). Load when making a grid editable, choosing an edit mode,
16
+ wiring a save, writing a cell editor, validating an edit, or when cells will
17
+ not open.
17
18
  metadata:
18
19
  type: core
19
20
  library: '@jielga/tmdatagrid'
20
- library_version: '2.0.0-beta.2'
21
+ library_version: '2.0.0-beta.4'
21
22
  sources:
22
23
  - 'Jielga/TMDataGrid:src/docs/editing.md'
23
24
  - 'Jielga/TMDataGrid:src/docs/query-builder.md'
@@ -69,8 +70,8 @@ const grid = useTMDataGrid({
69
70
  ```
70
71
 
71
72
  Two rules are compile errors, not options that silently do nothing: `editing`
72
- requires `getRowId`, and `editing.onCommitDrafts` exists only under
73
- `editing.mode: "draft"`. Draft _without_ `onCommitDrafts` is fine: `submitAll`
73
+ requires `getRowId`, and `editing.onSaveDrafts` exists only under
74
+ `editing.mode: "draft"`. Draft _without_ `onSaveDrafts` is fine: `saveDrafts`
74
75
  falls back to the per-row `editing.onCommit` loop.
75
76
 
76
77
  ## The four modes
@@ -83,7 +84,7 @@ as a commit, and which controls trigger it.
83
84
  | `"cell"` | Enter, Tab, blur | Escape | none |
84
85
  | `"cellConfirm"` | ✓ or Enter; blur keeps the draft | ✕ or Escape | ✓ / ✕ beside the input |
85
86
  | `"row"` | Save in the edit lane, or Ctrl+Enter | Cancel, or Escape | generated edit lane |
86
- | `"draft"` | `edit.submitAll()` | `edit.cancelAll()`, or per row in the lane | `TMDataGrid.EditActions` + the edit lane |
87
+ | `"draft"` | Enter or the lane's ✓, into the draft store | `edit.cancelAll()`, or per row in the lane | `TMDataGrid.EditActions` + the edit lane |
87
88
 
88
89
  Which to pick: `"cell"` for saved-as-you-go spreadsheet feel; `"cellConfirm"`
89
90
  when a stray click must not fire a request; `"row"` when the row is the unit of
@@ -104,7 +105,8 @@ the cell clicked, opens every editable cell of the row, and ✓ saves them as on
104
105
  commit. Rows accumulate: opening a second row leaves the first open, and each
105
106
  row's ✓ and ✕ act on that row alone.
106
107
 
107
- Under `"draft"` nothing reaches a callback until `submitAll`. Enter and Tab hold
108
+ Under `"draft"` nothing reaches a callback until `saveDrafts`. Enter commits the
109
+ row into the draft store; Tab holds
108
110
  the draft, Tab moving on to the next editable cell, Escape drops that one draft,
109
111
  and drafts accumulate across rows, surviving filters, sorts and scrolling.
110
112
  `edit.commit(rowId)` validates and holds the draft too, so there is no per-row
@@ -119,11 +121,23 @@ Restore. A row holding a dirty draft hides the trash - revert first, then
119
121
  delete. A row blocked by validation turns its icon red with the message in the
120
122
  tooltip.
121
123
 
122
- `submitAll` then commits every pending change: through the per-row
124
+ `saveDrafts` then sends the draft store: through the per-row
123
125
  `editing.onCommit` / `editing.onRowAdd` / `editing.onRowDelete` loop by default,
124
- or through one `editing.onCommitDrafts({ rows, added, deleted })` call when that
125
- is set. Rows failing validation stay open either way, and a rejected save keeps
126
- every draft.
126
+ or through one `editing.onSaveDrafts({ updated, created, deleted })` call when
127
+ that is set. `updated` entries carry a `rowId`, `created` entries a `tempId`, `deleted` is
128
+ a list of row ids. Rows failing validation stay open either way.
129
+
130
+ `onSaveDrafts` decides how much of the store is cleared: returning nothing
131
+ saves everything, throwing saves nothing, and returning
132
+ `{ updated, created, deleted }` saves everything except the ids reported
133
+ `false`. Each key takes `false` for the whole bucket or a map of id to result;
134
+ an unnamed id saved. A kept row stays committed, so the next `saveDrafts()`
135
+ retries it, and `saveDrafts()` resolves `false` when anything was kept.
136
+
137
+ Rows carry `data-dirty` (values typed in), `data-draft` (committed, waiting for
138
+ Save), `data-deleted` and, on entry rows, `data-new`. The grid paints none of
139
+ them; use `rowStyle` / `rowClassName` or the attributes to highlight what is
140
+ pending.
127
141
 
128
142
  ## What a commit receives
129
143
 
@@ -210,18 +224,21 @@ Detail for all three: [references/editors-and-validation.md](references/editors-
210
224
  ## Adding and deleting rows
211
225
 
212
226
  `edit.addRow()` opens an entry row in a sticky block under the header, seeded
213
- from `editing.newRowDefaults`. Enter, or the lane's ✓, commits the add through
214
- `editing.onRowAdd` under the immediate modes; under draft mode it enters the
215
- row, which is validated, held with the other drafts, and reported in
216
- `submitAll`'s `added`. Escape, or ✕, discards the entry.
227
+ from `editing.newRowDefaults`. `edit.addRow(values)` overrides that seed key by
228
+ key, so `addRow()` opens the `newRowDefaults` row and `addRow(values)` opens it
229
+ with those fields filled in - pass a whole row to duplicate it. Enter, or the
230
+ lane's ✓, commits the add through `editing.onRowAdd` under the immediate modes;
231
+ under draft mode it puts the row in the draft store, validated, and
232
+ `saveDrafts` reports it in `added`. Escape, or ✕, discards the entry. An entry
233
+ row never OK'd is not part of a save - it stays open.
217
234
 
218
235
  Under `"draft"` an entered row renders as a value row with no inputs, marked
219
- `data-new` and `data-confirmed` and tinted with `--dg-row-new-bg`. By default it
236
+ `data-new` and `data-committed` and tinted with `--dg-row-new-bg`. By default it
220
237
  joins the scrolling flow above the body rows; `editing.newRowsSticky: true`
221
- keeps entered rows pinned in the entry block until Save all. Double-click, or
238
+ keeps committed rows pinned in the entry block until the save. Double-click, or
222
239
  the lane's pencil, reopens it; ✕ removes it. To limit how many entry rows are
223
240
  open at once, gate the Add button on
224
- `useSelector(grid.edit.store, (s) => s.newRows.some((n) => !n.confirmed))`.
241
+ `useSelector(grid.edit.store, (s) => s.newRows.some((n) => !n.committed))`.
225
242
 
226
243
  ```tsx
227
244
  const grid = useTMDataGrid({
@@ -231,23 +248,39 @@ const grid = useTMDataGrid({
231
248
  editing: {
232
249
  mode: "draft",
233
250
  newRowDefaults: () => ({ id: 0, firstName: "", salary: 30_000 }),
234
- onCommitDrafts: async ({ rows, added, deleted }) => {
235
- await api.saveBatch({ rows, added, deleted });
251
+ onSaveDrafts: async ({ updated, created, deleted }) => {
252
+ await api.saveBatch({ updated, created, deleted });
236
253
  },
237
254
  },
238
255
  });
239
256
 
240
257
  <TMDataGrid.Toolbar>
241
258
  <Button onClick={() => grid.edit.addRow()}>Add row</Button>
259
+ <Button onClick={() => grid.edit.addRow({ salary: 50_000 })}>
260
+ Add senior
261
+ </Button>
242
262
  <TMDataGrid.Spacer />
243
263
  <TMDataGrid.EditActions />
244
264
  </TMDataGrid.Toolbar>;
245
265
  ```
246
266
 
267
+ `edit.addRows(rows, options?)` opens a batch in one write. `{ commit: true }`
268
+ submits each row as it lands - the import case: valid rows are committed,
269
+ invalid ones stay open in the entry block with their errors, and the result
270
+ (`{ committed, open }`) says which went which way. Column rules are enforced
271
+ even though the rows never had an editor on screen, because the engine runs
272
+ `meta.edit.validate` itself at commit.
273
+
274
+ ```tsx
275
+ const { committed, open } = await grid.edit.addRows(parsed, { commit: true });
276
+ if (open.length > 0) notify(`${open.length} rows need attention`);
277
+ await grid.edit.saveDrafts();
278
+ ```
279
+
247
280
  `edit.deleteRow(rowId)` calls `editing.onRowDelete({ rowId, row })` immediately
248
281
  under the immediate modes, so put any confirmation inside that callback. Under
249
282
  draft mode it toggles a mark instead: the row renders struck through and inert
250
- (`data-deleted`), the lane shows Restore, and `submitAll` reports the ids in
283
+ (`data-deleted`), the lane shows Restore, and `saveDrafts` reports the ids in
251
284
  `deleted`. You apply adds and deletes, the same as edits. The engine's `tempId`
252
285
  (`__new__1`, …) does not need to become a real id.
253
286
 
@@ -283,7 +316,8 @@ while editing is off, and works under any mode, not only draft.
283
316
  ## The engine: `edit`
284
317
 
285
318
  `grid.edit` is public, and everything the built-in controls do goes through it:
286
- `begin` and `commit`, `cancel` / `cancelAll`, `submitAll`, `addRow` /
319
+ `begin` and `commit`, `cancel` / `cancelAll`, `commitAll` / `saveDrafts`,
320
+ `addRow` / `addRows` /
287
321
  `deleteRow`, `getForm`, and `store` for `useSelector` (an example is under
288
322
  [Submitting an outer form](#high-submitting-an-outer-form-while-the-grid-holds-a-draft)).
289
323
  Every member with its signature, the gates, `clearCell`, `deactivate`, and the
@@ -324,7 +358,7 @@ are in [references/common-mistakes.md](references/common-mistakes.md).
324
358
  | HIGH | Swallowing the error in `editing.onCommit` - a resolved catch drops the draft |
325
359
  | HIGH | Submitting an outer form while the grid holds a draft - it saves stale rows |
326
360
  | MEDIUM | Reading a commit's result as the saved value - it is a `boolean` about the form |
327
- | MEDIUM | Expecting `editing.onRowDelete` to fire under draft - the mark waits for `submitAll` |
361
+ | MEDIUM | Expecting `editing.onRowDelete` to fire under draft - the mark waits for `saveDrafts` |
328
362
 
329
363
  ## References
330
364
 
@@ -137,7 +137,7 @@ Source: `src/tmdatagrid/useTMDataGrid.tsx` (`TMDataGridEditingCallbacks`).
137
137
  ## HIGH Submitting an outer form while the grid holds a draft
138
138
 
139
139
  The outer form's array contains only committed rows. Under any mode a mid-edit
140
- row is invisible to it, and under `"draft"` every edit is until `submitAll()`,
140
+ row is invisible to it, and under `"draft"` every edit is until `saveDrafts()`,
141
141
  so a form submit saves stale rows and collection rules skip pending values.
142
142
 
143
143
  Correct:
@@ -146,8 +146,9 @@ Correct:
146
146
  const hasOpenDraft = useSelector(grid.edit.store, (s) => s.openRowIds.length > 0);
147
147
 
148
148
  <Button type="submit" disabled={!canSubmit || hasOpenDraft}>Save</Button>
149
- // or flush instead of blocking:
150
- const flushed = await grid.edit.submitAll();
149
+ // or commit and flush instead of blocking:
150
+ await grid.edit.commitAll();
151
+ const flushed = await grid.edit.saveDrafts();
151
152
  if (flushed) await form.handleSubmit();
152
153
  ```
153
154
 
@@ -155,12 +156,13 @@ Source: `src/docs/query-builder.md` (Which mode, Submitting).
155
156
 
156
157
  ## MEDIUM Reading a commit's result as the saved value
157
158
 
158
- `edit.commit(rowId)` and `edit.submitAll()` resolve to a `boolean` saying
159
- whether the form closed, and resolve `false` when validation or a rejected save
160
- kept it open. Ignoring the result reports a save that did not happen.
159
+ `edit.commit(rowId)`, `edit.commitAll()` and `edit.saveDrafts()` resolve to a
160
+ `boolean` saying whether everything landed, and resolve `false` when validation
161
+ or a rejected save kept a row open. Ignoring the result reports a save that did
162
+ not happen.
161
163
 
162
164
  ```tsx
163
- const saved = await grid.edit.submitAll();
165
+ const saved = await grid.edit.saveDrafts();
164
166
  notifications.show({ message: saved ? "Saved" : "Some rows need attention" });
165
167
  ```
166
168
 
@@ -170,7 +172,7 @@ Source: `src/tmdatagrid/core/editEngine.ts` (`TMDataGridEditApi`).
170
172
 
171
173
  Under the immediate modes `edit.deleteRow` calls `editing.onRowDelete` at once.
172
174
  Under `"draft"` it only toggles a deletion mark, so nothing is removed until
173
- `submitAll`: the ids arrive as `deleted` in `editing.onCommitDrafts`, or, with
175
+ `saveDrafts`: the ids arrive as `deleted` in `editing.onSaveDrafts`, or, with
174
176
  no such callback, in the per-row `editing.onRowDelete` loop. A confirmation
175
177
  placed inside `editing.onRowDelete` therefore guards the save, not the trash.
176
178
 
@@ -17,7 +17,7 @@ kind.
17
17
  | `cellSelection` | `"none" \| "single" \| "range"` | `"single"` while `editing` is set | Editing turns the cell cursor on; set it explicitly to override. |
18
18
 
19
19
  Passing any other member of `editing` without `mode` is a compile error, and
20
- `onCommitDrafts` and `newRowsSticky` exist only in the `"draft"` branch of the
20
+ `onSaveDrafts` and `newRowsSticky` exist only in the `"draft"` branch of the
21
21
  type.
22
22
 
23
23
  ## Callbacks
@@ -25,7 +25,8 @@ type.
25
25
  | Name | Argument | What it does |
26
26
  | --- | --- | --- |
27
27
  | `editing.onCommit` | `{ rowId, value, original, changes, source }` | Applies one row's change. Reject to keep the draft and show the error. |
28
- | `editing.onCommitDrafts` | `{ rows, added, deleted }` | Draft mode only. One call for the entire save. Without it, `submitAll` loops `editing.onCommit`, `editing.onRowAdd` and `editing.onRowDelete`. |
28
+ | `editing.onSaveDrafts` | `{ rows, added, deleted }` | Draft mode only. One call for the whole draft store. Without it, `saveDrafts` loops `editing.onCommit`, `editing.onRowAdd` and `editing.onRowDelete`. |
29
+ | `editing.onCommitDrafts` | `{ rows, added, deleted }` | **Deprecated** - renamed to `onSaveDrafts`. Still honoured; the new name wins if both are set. |
29
30
  | `editing.onRowAdd` | `{ tempId, value }` | Commits an entry row. Mint the real id here. |
30
31
  | `editing.onRowDelete` | `{ rowId, row }` | Deletes a row under the immediate modes, and puts the trash can in the edit lane. |
31
32
 
@@ -49,14 +50,17 @@ path, which may be dotted.
49
50
 
50
51
  | Member | Signature | Notes |
51
52
  | --- | --- | --- |
52
- | `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 an entered new row it reopens the row, flipping `confirmed` back to `false`. |
53
- | `commit` | `(rowId) => Promise<boolean>` | `false` keeps the form open with its errors. Under `"draft"` it validates and holds the draft: no consumer callback runs until `submitAll`. |
53
+ | `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. |
54
+ | `commit` | `(rowId) => Promise<boolean>` | The OK gesture: submits the row's form. `false` keeps it open with its errors. Under `"draft"` a pass puts the row in the draft store - no consumer callback runs until `saveDrafts`. Column rules run whether or not an editor is mounted. |
55
+ | `commitAll` | `() => Promise<boolean>` | Submits every open row. Rows that fail stay open. `false` when one did. |
56
+ | `saveDrafts` | `() => Promise<boolean>` | Sends the draft store. Open rows are left alone and stay open. |
54
57
  | `cancel` | `(rowId) => void` | Drops one draft. |
55
58
  | `cancelAll` | `() => void` | Drops every draft. |
56
59
  | `deactivate` | `() => void` | Closes the editor without touching the draft, as blur does under `"cellConfirm"`. |
57
- | `submitAll` | `() => Promise<boolean>` | Draft mode's Save all. `true` when every row landed. |
60
+ | `submitAll` | `() => Promise<boolean>` | **Deprecated** - `commitAll()` then `saveDrafts()`. |
58
61
  | `clearCell` | `(rowId, columnId) => Promise<boolean>` | What Delete does: writes the type's empty value and commits. |
59
- | `addRow` | `() => string` | Opens an entry row, returns its `tempId`. |
62
+ | `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. |
63
+ | `addRows` | `(rows, options?) => Promise<{ committed, open }>` | 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. |
60
64
  | `deleteRow` | `(rowId) => void` | `editing.onRowDelete` under the immediate modes, a deletion mark under draft mode. Toggles: a second call restores the row. |
61
65
  | `canEditCell` | `(row, column) => boolean` | The check the built-in controls use. |
62
66
  | `canEditRow` | `(row) => boolean` | The pencil's gate. |
@@ -71,8 +75,8 @@ path, which may be dotted.
71
75
  type TMDataGridEditState = {
72
76
  // The cell the last open gesture named - where the caret goes.
73
77
  active: { rowId: string; columnId: string | null } | null;
74
- // Rows with a live form. One under `"cell"`; as many as the user opened
75
- // under `"row"`, `"cellConfirm"` and `"draft"` - which rows are editing.
78
+ // Rows with a live form, committed or not. A row is *open* when it is in
79
+ // here and not in `committedRowIds`.
76
80
  openRowIds: ReadonlyArray<string>;
77
81
  // One `TMDataGridEditRowProjection` per open row.
78
82
  rows: Record<
@@ -86,9 +90,12 @@ type TMDataGridEditState = {
86
90
  values: TMDataGridRowData;
87
91
  }
88
92
  >;
89
- // `confirmed` is draft mode's "entered, awaiting Save all". Under the
90
- // immediate modes a confirm commits through `onRowAdd`, so it stays `false`.
91
- newRows: ReadonlyArray<{ tempId: string; confirmed: boolean }>;
93
+ // The draft store's edit slice: existing rows whose form passed its submit,
94
+ // parked for `saveDrafts`. Empty outside `"draft"`.
95
+ committedRowIds: ReadonlyArray<string>;
96
+ // `committed` is draft mode's "in the store, awaiting the save". Under the
97
+ // immediate modes a commit adds through `onRowAdd`, so it stays `false`.
98
+ newRows: ReadonlyArray<{ tempId: string; committed: boolean }>;
92
99
  deletedRowIds: ReadonlyArray<string>;
93
100
  };
94
101
  ```
@@ -119,7 +126,7 @@ holds:
119
126
 
120
127
  - `editing.mode: "row"` - the lane is Save and Cancel's home
121
128
  - `editing.mode: "draft"` - the lane is the change indicator and the per-row
122
- revert, and `editing.onCommitDrafts` is not required for it
129
+ revert, and `editing.onSaveDrafts` is not required for it
123
130
  - `editing.onRowDelete` is set - the trash can has somewhere to report to
124
131
 
125
132
  `"cell"` and `"cellConfirm"` have no lane unless `editing.onRowDelete` asks for
@@ -142,7 +149,7 @@ control carries a tooltip from the labels, `revertRow`, `rowStateNew`,
142
149
  | `--dg-row-new-bg` | CSS variable | Background of an entered new row. A green tint. |
143
150
  | `data-deleted` | Row attribute | On a row marked for deletion under draft mode. |
144
151
  | `data-dirty` | Row attribute | On a body row holding a dirty draft. Also on the cell whose field is dirty. |
145
- | `data-new` / `data-confirmed` | Entry row attributes | On an entry row; `data-confirmed` once it is entered, awaiting Save all. |
152
+ | `data-new` / `data-committed` | Entry row attributes | On an entry row; `data-committed` once it is committed, awaiting the save. |
146
153
  | `data-dg-entry-flow-block` | Attribute | The in-flow block above the body rows holding entered new rows, unless `editing.newRowsSticky` keeps them in the sticky entry block. |
147
154
  | `data-dg-part="editor-input"` | Part | The control inside an editing cell. |
148
155
  | `data-dg-part="save-row"` / `"cancel-row"` | Parts | The edit lane's buttons on an open row, with `data-row-id`. Row mode only. |
@@ -15,7 +15,7 @@ description: >
15
15
  metadata:
16
16
  type: core
17
17
  library: '@jielga/tmdatagrid'
18
- library_version: '2.0.0-beta.2'
18
+ library_version: '2.0.0-beta.4'
19
19
  sources:
20
20
  - 'Jielga/TMDataGrid:src/docs/filtering.md'
21
21
  - 'Jielga/TMDataGrid:src/docs/quick-search.md'
@@ -11,7 +11,7 @@ description: >
11
11
  metadata:
12
12
  type: core
13
13
  library: '@jielga/tmdatagrid'
14
- library_version: '2.0.0-beta.2'
14
+ library_version: '2.0.0-beta.4'
15
15
  sources:
16
16
  - 'Jielga/TMDataGrid:src/docs/getting-started.md'
17
17
  - 'Jielga/TMDataGrid:src/docs/anatomy.md'
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0-beta.2'
17
+ library_version: '2.0.0-beta.4'
18
18
  sources:
19
19
  - 'Jielga/TMDataGrid:src/docs/grouping.md'
20
20
  - 'Jielga/TMDataGrid:src/docs/summary-row.md'
@@ -14,7 +14,7 @@ description: >
14
14
  metadata:
15
15
  type: core
16
16
  library: '@jielga/tmdatagrid'
17
- library_version: '2.0.0-beta.2'
17
+ library_version: '2.0.0-beta.4'
18
18
  sources:
19
19
  - 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
20
20
  - 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
@@ -62,10 +62,11 @@ rather than forwarded to TanStack.
62
62
  | `onFocusedCellChange` | `(cell \| null) => void` | – | Fires whenever the focused cell moves. |
63
63
  | `overscan` | `number` | `6` | Rows the virtualizer keeps mounted above and below the viewport. Defined by the grid. |
64
64
  | `columnResizeMode` | `"onChange" \| "onEnd"` | `"onChange"` | Resize update strategy. |
65
- | `initialState` | `Partial<TableState>` | See below | Merged over the grid defaults. |
65
+ | `initialState` | `Partial<TableState>` | See below | The state the grid starts from, read once on mount. Merged over the grid defaults. |
66
+ | `state` | `Partial<TableState>` | – | Controlled state. Each slice requires its `onXChange` - see below. |
66
67
  | `meta` | `TMDataGridTableMeta` | `{}` | Grid configuration, see below. |
67
68
  | `persist` | `TMDataGridPersistence` | – | State persistence, see below. |
68
- | `editing` | `TMDataGridEditingOptions` | off | Turns editing on. `mode` (`"cell" \| "cellConfirm" \| "row" \| "draft"`) picks the commit policy; the object also holds `onCommit`, `onCommitDrafts`, `rowValidators`, `isRowEditable`, `newRowDefaults`, `newRowsSticky`, `onRowAdd` and `onRowDelete` - see the `editing` skill. |
69
+ | `editing` | `TMDataGridEditingOptions` | off | Turns editing on. `mode` (`"cell" \| "cellConfirm" \| "row" \| "draft"`) picks the commit policy; the object also holds `onCommit`, `onSaveDrafts`, `rowValidators`, `isRowEditable`, `newRowDefaults`, `newRowsSticky`, `onRowAdd` and `onRowDelete` - see the `editing` skill. |
69
70
  | `labels` | `TMDataGridLabelsOverride` | English | Overrides for the grid's strings and `aria-label`s. |
70
71
 
71
72
  ### Default initial state
@@ -76,6 +77,35 @@ rather than forwarded to TanStack.
76
77
  | `columnPinning.left` | The checkbox column, followed by any columns you provide |
77
78
  | `globalFilterFn` | `"includesString"` |
78
79
 
80
+ ### Controlled state
81
+
82
+ `state` makes a slice controlled: the parent owns the value, the grid reads it
83
+ every render, and all writes go through the matching `onXChange`. Without the
84
+ callback the slice cannot change; the grid logs a console warning in
85
+ development. For a starting value only, use `initialState`.
86
+
87
+ ```tsx
88
+ const [columnVisibility, setColumnVisibility] = useState({ play: false });
89
+
90
+ const grid = useTMDataGrid({
91
+ data,
92
+ columns,
93
+ state: { columnVisibility },
94
+ onColumnVisibilityChange: setColumnVisibility,
95
+ });
96
+ ```
97
+
98
+ - A key set to `undefined` is ignored; the slice is uncontrolled.
99
+ - Slices compare structurally between renders, so the `state` object can be
100
+ built inline. `Date`s compare by time; `Map`s and class instances compare by
101
+ identity and belong in `useState` or `useMemo`.
102
+ - `atoms` also controls a slice, with no callback. Takes precedence over
103
+ `state` for the same slice.
104
+ - `columnVisibility` toggles user-defined columns only. Entries for the
105
+ generated columns are ignored; enable or disable those through their feature
106
+ options. The tree column's entry follows `grouping`.
107
+ - `persist` cannot restore a controlled slice; it still writes it to storage.
108
+
79
109
  ## meta
80
110
 
81
111
  | Field | Type | Default | Description |
@@ -17,7 +17,7 @@ description: >
17
17
  metadata:
18
18
  type: core
19
19
  library: '@jielga/tmdatagrid'
20
- library_version: '2.0.0-beta.2'
20
+ library_version: '2.0.0-beta.4'
21
21
  sources:
22
22
  - 'Jielga/TMDataGrid:src/docs/row-selection.md'
23
23
  - 'Jielga/TMDataGrid:src/docs/row-interaction.md'
@@ -10,7 +10,7 @@ description: >
10
10
  metadata:
11
11
  type: core
12
12
  library: '@jielga/tmdatagrid'
13
- library_version: '2.0.0-beta.2'
13
+ library_version: '2.0.0-beta.4'
14
14
  sources:
15
15
  - 'Jielga/TMDataGrid:src/docs/server-side.md'
16
16
  - 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
@@ -11,7 +11,7 @@ description: >
11
11
  metadata:
12
12
  type: core
13
13
  library: '@jielga/tmdatagrid'
14
- library_version: '2.0.0-beta.2'
14
+ library_version: '2.0.0-beta.4'
15
15
  sources:
16
16
  - 'Jielga/TMDataGrid:src/docs/testing.md'
17
17
  - 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGrid.tsx'
@@ -172,7 +172,11 @@ export function TMDataGridCellEditor({
172
172
  onClose({ committed: false, via: "escape" });
173
173
  };
174
174
 
175
- /** Draft: the key closes the editor and leaves the draft for submitAll. */
175
+ /**
176
+ * Draft: the key closes the editor and leaves the row open - undecided
177
+ * form state, which `saveDrafts` does not send. Tab moving along the row
178
+ * is not a decision about it; Enter is, and commits instead.
179
+ */
176
180
  const deferAndClose = (via: "defer" | "defer-tab" | "defer-shift-tab") => {
177
181
  if (closingRef.current) return;
178
182
  closingRef.current = true;
@@ -185,12 +189,11 @@ export function TMDataGridCellEditor({
185
189
  if (event.key === "Enter") {
186
190
  event.preventDefault();
187
191
  event.stopPropagation();
188
- // In row mode Enter (and Ctrl+Enter, the documented pair) saves the
189
- // row; in draft nothing reaches a callback until submitAll, so Enter
190
- // parks the draft - and in the entry block it confirms the entry, which
191
- // the engine parks the same way.
192
- if (isDraft && !inEntryBlock) deferAndClose("defer");
193
- else void commitAndClose("enter");
192
+ // Enter is OK, in every mode: the row's form submits and has to pass
193
+ // validation. What a commit *does* is the mode's business - the
194
+ // immediate modes call the consumer, draft mode parks the row in the
195
+ // draft store for `saveDrafts` - so this key does not branch.
196
+ void commitAndClose("enter");
194
197
  } else if (event.key === "Escape") {
195
198
  event.preventDefault();
196
199
  event.stopPropagation();