@jielga/tmdatagrid 2.0.0-beta.1 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +60 -23
- package/dist/index.js +2020 -1704
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/skills/appearance/SKILL.md +1 -1
- package/skills/cell-selection/SKILL.md +1 -1
- package/skills/columns/SKILL.md +2 -2
- package/skills/data/SKILL.md +1 -1
- package/skills/editing/SKILL.md +68 -213
- package/skills/editing/references/common-mistakes.md +177 -0
- package/skills/editing/references/editing-api.md +39 -13
- package/skills/editing/references/editors-and-validation.md +5 -2
- package/skills/filtering/SKILL.md +1 -1
- package/skills/getting-started/SKILL.md +1 -1
- package/skills/grouping/SKILL.md +1 -1
- package/skills/options/SKILL.md +2 -2
- package/skills/rows/SKILL.md +1 -1
- package/skills/rows/references/rows-api.md +1 -1
- package/skills/server-side/SKILL.md +1 -1
- package/skills/testing/SKILL.md +3 -2
- package/src/tmdatagrid/components/TMDataGrid.module.css +7 -0
- package/src/tmdatagrid/components/TMDataGridCellEditor.tsx +20 -8
- package/src/tmdatagrid/components/TMDataGridEditActions.tsx +1 -1
- package/src/tmdatagrid/components/TMDataGridEditColumn.tsx +242 -86
- package/src/tmdatagrid/components/TMDataGridEntryRows.tsx +176 -77
- package/src/tmdatagrid/components/TMDataGridTable.module.css +47 -3
- package/src/tmdatagrid/components/TMDataGridTable.tsx +44 -4
- package/src/tmdatagrid/components/icons.ts +1 -0
- package/src/tmdatagrid/core/capabilities.ts +10 -0
- package/src/tmdatagrid/core/draftCellContext.ts +68 -0
- package/src/tmdatagrid/core/editEngine.ts +106 -32
- package/src/tmdatagrid/core/labels.ts +13 -1
- package/src/tmdatagrid/core/labelsSv.ts +4 -0
- package/src/tmdatagrid/index.ts +1 -1
- package/src/tmdatagrid/useTMDataGrid.tsx +39 -20
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Editing - common mistakes
|
|
2
|
+
|
|
3
|
+
The failure modes that compile, raise no warning, and lose a user's typing.
|
|
4
|
+
Severity is how much data or trust the mistake costs.
|
|
5
|
+
|
|
6
|
+
## CRITICAL Expecting the grid to write into `data`
|
|
7
|
+
|
|
8
|
+
The grid holds no copy of the rows. Without `editing.onCommit` an edit commits,
|
|
9
|
+
the draft clears, and the cell reverts to the value in `data`.
|
|
10
|
+
|
|
11
|
+
Wrong:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
useTMDataGrid({
|
|
15
|
+
data: employees,
|
|
16
|
+
columns,
|
|
17
|
+
getRowId,
|
|
18
|
+
editing: { mode: "cell" },
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Correct: wire `editing.onCommit` to apply the change where the data lives, as
|
|
23
|
+
in the skill's Setup section.
|
|
24
|
+
|
|
25
|
+
Source: `src/docs/editing.md`, `src/tmdatagrid/core/editEngine.ts`.
|
|
26
|
+
|
|
27
|
+
## CRITICAL `getRowId` built from the row index
|
|
28
|
+
|
|
29
|
+
`getRowId` accepts any string, so an index-based id compiles. Drafts are keyed
|
|
30
|
+
by it, so after a sort or a filter the draft is applied to whichever record now
|
|
31
|
+
sits at that index.
|
|
32
|
+
|
|
33
|
+
Wrong:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
getRowId: (row, index) => String(index),
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Correct:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
getRowId: (row) => String(row.id),
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Source: `src/tmdatagrid/useTMDataGrid.tsx` (`TMDataGridEditingCallbacks`).
|
|
46
|
+
|
|
47
|
+
## HIGH A cell editor defined inside the component
|
|
48
|
+
|
|
49
|
+
`meta.edit.editor` is rendered as JSX, so its identity is its component type. An
|
|
50
|
+
inline arrow function is a new type on every render, which unmounts the open
|
|
51
|
+
editor and discards what was being typed.
|
|
52
|
+
|
|
53
|
+
Wrong:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
meta: { edit: { editor: ({ field }) => <Slider value={field.state.value} /> } },
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Correct:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// Module scope, referenced by name.
|
|
63
|
+
const SalaryEditor: TMDataGridEditorComponent = ({ field, commit }) => (
|
|
64
|
+
<Slider
|
|
65
|
+
value={Number(field.state.value ?? 0)}
|
|
66
|
+
onChange={(next) => field.handleChange(next)}
|
|
67
|
+
onChangeEnd={() => void commit()}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
meta: { edit: { editor: SalaryEditor } },
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Source: `src/docs/editors.md`, `src/tmdatagrid/core/editEngine.ts`.
|
|
75
|
+
|
|
76
|
+
## HIGH A cross-field rule under `editing.mode: "cell"`
|
|
77
|
+
|
|
78
|
+
Under `"cell"` each cell commits on its own, so a rule spanning two columns
|
|
79
|
+
cannot be satisfied by either one: the first cell edited is rejected against the
|
|
80
|
+
other column's old value, and the row cannot be saved.
|
|
81
|
+
|
|
82
|
+
Correct: `editing.rowValidators` needs `mode: "row"` or `"draft"`, which
|
|
83
|
+
validate the whole row in one commit.
|
|
84
|
+
|
|
85
|
+
Source: `src/docs/editors.md` (Validation).
|
|
86
|
+
|
|
87
|
+
## HIGH An `accessorFn` column that never opens an editor
|
|
88
|
+
|
|
89
|
+
Editability follows the data path, not the column. A column built on an accessor
|
|
90
|
+
function has no `accessorKey`, so it maps to nothing and stays read-only while
|
|
91
|
+
every other column edits. No warning is raised.
|
|
92
|
+
|
|
93
|
+
Wrong:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
|
|
97
|
+
id: "fullName",
|
|
98
|
+
header: "Full name",
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Correct:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
columnHelper.accessor((row) => `${row.firstName} ${row.lastName}`, {
|
|
106
|
+
id: "fullName",
|
|
107
|
+
header: "Full name",
|
|
108
|
+
meta: { label: "Full name", edit: { field: "lastName" } },
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Source: `src/docs/editing.md` (Which cells edit).
|
|
113
|
+
|
|
114
|
+
## HIGH Swallowing the error in `editing.onCommit`
|
|
115
|
+
|
|
116
|
+
The draft is dropped when `editing.onCommit` resolves. A `try/catch` that logs
|
|
117
|
+
the failure resolves it, so a save that failed on the server clears the editor
|
|
118
|
+
and the grid shows the old value as though nothing happened.
|
|
119
|
+
|
|
120
|
+
Wrong:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
onCommit: async ({ rowId, changes }) => {
|
|
124
|
+
try {
|
|
125
|
+
await api.patch(rowId, changes);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(error);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Correct: no `catch` - let the rejection propagate, and the form stays open
|
|
133
|
+
with the error on the row.
|
|
134
|
+
|
|
135
|
+
Source: `src/tmdatagrid/useTMDataGrid.tsx` (`TMDataGridEditingCallbacks`).
|
|
136
|
+
|
|
137
|
+
## HIGH Submitting an outer form while the grid holds a draft
|
|
138
|
+
|
|
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()`,
|
|
141
|
+
so a form submit saves stale rows and collection rules skip pending values.
|
|
142
|
+
|
|
143
|
+
Correct:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const hasOpenDraft = useSelector(grid.edit.store, (s) => s.openRowIds.length > 0);
|
|
147
|
+
|
|
148
|
+
<Button type="submit" disabled={!canSubmit || hasOpenDraft}>Save</Button>
|
|
149
|
+
// or flush instead of blocking:
|
|
150
|
+
const flushed = await grid.edit.submitAll();
|
|
151
|
+
if (flushed) await form.handleSubmit();
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Source: `src/docs/query-builder.md` (Which mode, Submitting).
|
|
155
|
+
|
|
156
|
+
## MEDIUM Reading a commit's result as the saved value
|
|
157
|
+
|
|
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.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
const saved = await grid.edit.submitAll();
|
|
164
|
+
notifications.show({ message: saved ? "Saved" : "Some rows need attention" });
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Source: `src/tmdatagrid/core/editEngine.ts` (`TMDataGridEditApi`).
|
|
168
|
+
|
|
169
|
+
## MEDIUM Expecting `editing.onRowDelete` to fire under draft
|
|
170
|
+
|
|
171
|
+
Under the immediate modes `edit.deleteRow` calls `editing.onRowDelete` at once.
|
|
172
|
+
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
|
|
174
|
+
no such callback, in the per-row `editing.onRowDelete` loop. A confirmation
|
|
175
|
+
placed inside `editing.onRowDelete` therefore guards the save, not the trash.
|
|
176
|
+
|
|
177
|
+
Source: `src/docs/editing.md` (Adding and deleting rows).
|
|
@@ -8,22 +8,24 @@ kind.
|
|
|
8
8
|
| Name | Type | Default | What it does |
|
|
9
9
|
| --- | --- | --- | --- |
|
|
10
10
|
| `editing` | `TMDataGridEditingOptions` | off | The editing namespace. Setting it turns editing on. |
|
|
11
|
-
| `editing.mode` | `"cell" \| "cellConfirm" \| "row" \| "
|
|
11
|
+
| `editing.mode` | `"cell" \| "cellConfirm" \| "row" \| "draft"` | – | Picks the commit policy. |
|
|
12
12
|
| `getRowId` | `(row) => string` | – | A TanStack table option, required once `editing` is set. Drafts are keyed by it. |
|
|
13
13
|
| `editing.isRowEditable` | `(row) => boolean` | – | Closes a whole row to editing, in every mode. |
|
|
14
14
|
| `editing.rowValidators` | `TMDataGridRowValidators` | – | Form-level validation. Cross-field rules live here. |
|
|
15
15
|
| `editing.newRowDefaults` | `TData \| (() => TData)` | – | Seeds the entry row's form. A function is called per added row. |
|
|
16
|
+
| `editing.newRowsSticky` | `boolean` | `false` | Draft mode only. Keeps entered new rows pinned in the entry block until Save all, instead of letting them scroll with the body. |
|
|
16
17
|
| `cellSelection` | `"none" \| "single" \| "range"` | `"single"` while `editing` is set | Editing turns the cell cursor on; set it explicitly to override. |
|
|
17
18
|
|
|
18
19
|
Passing any other member of `editing` without `mode` is a compile error, and
|
|
19
|
-
`
|
|
20
|
+
`onCommitDrafts` and `newRowsSticky` exist only in the `"draft"` branch of the
|
|
21
|
+
type.
|
|
20
22
|
|
|
21
23
|
## Callbacks
|
|
22
24
|
|
|
23
25
|
| Name | Argument | What it does |
|
|
24
26
|
| --- | --- | --- |
|
|
25
27
|
| `editing.onCommit` | `{ rowId, value, original, changes, source }` | Applies one row's change. Reject to keep the draft and show the error. |
|
|
26
|
-
| `editing.
|
|
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`. |
|
|
27
29
|
| `editing.onRowAdd` | `{ tempId, value }` | Commits an entry row. Mint the real id here. |
|
|
28
30
|
| `editing.onRowDelete` | `{ rowId, row }` | Deletes a row under the immediate modes, and puts the trash can in the edit lane. |
|
|
29
31
|
|
|
@@ -47,15 +49,15 @@ path, which may be dotted.
|
|
|
47
49
|
|
|
48
50
|
| Member | Signature | Notes |
|
|
49
51
|
| --- | --- | --- |
|
|
50
|
-
| `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. |
|
|
51
|
-
| `commit` | `(rowId) => Promise<boolean>` | `false` keeps the form open with its errors. |
|
|
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`. |
|
|
52
54
|
| `cancel` | `(rowId) => void` | Drops one draft. |
|
|
53
55
|
| `cancelAll` | `() => void` | Drops every draft. |
|
|
54
56
|
| `deactivate` | `() => void` | Closes the editor without touching the draft, as blur does under `"cellConfirm"`. |
|
|
55
|
-
| `submitAll` | `() => Promise<boolean>` |
|
|
57
|
+
| `submitAll` | `() => Promise<boolean>` | Draft mode's Save all. `true` when every row landed. |
|
|
56
58
|
| `clearCell` | `(rowId, columnId) => Promise<boolean>` | What Delete does: writes the type's empty value and commits. |
|
|
57
59
|
| `addRow` | `() => string` | Opens an entry row, returns its `tempId`. |
|
|
58
|
-
| `deleteRow` | `(rowId) => void` | `editing.onRowDelete` under the immediate modes, a deletion mark under
|
|
60
|
+
| `deleteRow` | `(rowId) => void` | `editing.onRowDelete` under the immediate modes, a deletion mark under draft mode. Toggles: a second call restores the row. |
|
|
59
61
|
| `canEditCell` | `(row, column) => boolean` | The check the built-in controls use. |
|
|
60
62
|
| `canEditRow` | `(row) => boolean` | The pencil's gate. |
|
|
61
63
|
| `canDeleteRows` | `() => boolean` | Whether the delete control should be shown. |
|
|
@@ -70,17 +72,23 @@ type TMDataGridEditState = {
|
|
|
70
72
|
// The cell the last open gesture named - where the caret goes.
|
|
71
73
|
active: { rowId: string; columnId: string | null } | null;
|
|
72
74
|
// Rows with a live form. One under `"cell"`; as many as the user opened
|
|
73
|
-
// under `"row"`, `"cellConfirm"` and `"
|
|
75
|
+
// under `"row"`, `"cellConfirm"` and `"draft"` - which rows are editing.
|
|
74
76
|
openRowIds: ReadonlyArray<string>;
|
|
77
|
+
// One `TMDataGridEditRowProjection` per open row.
|
|
75
78
|
rows: Record<
|
|
76
79
|
string,
|
|
77
80
|
{
|
|
78
81
|
dirtyFields: ReadonlyArray<string>;
|
|
79
82
|
errorFields: ReadonlyArray<string>;
|
|
83
|
+
hasRowError: boolean;
|
|
80
84
|
isSubmitting: boolean;
|
|
85
|
+
// The row as drafted, reference-stable while no value changes.
|
|
86
|
+
values: TMDataGridRowData;
|
|
81
87
|
}
|
|
82
88
|
>;
|
|
83
|
-
|
|
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 }>;
|
|
84
92
|
deletedRowIds: ReadonlyArray<string>;
|
|
85
93
|
};
|
|
86
94
|
```
|
|
@@ -98,7 +106,7 @@ type TMDataGridEditState = {
|
|
|
98
106
|
| `TMDataGridStringEditor` … `TMDataGridMultiSelectEditor` | Exports | The six built-in editors, for wrapping. |
|
|
99
107
|
|
|
100
108
|
Types: `TMDataGridEditMode`, `TMDataGridEditApi`, `TMDataGridEditState`,
|
|
101
|
-
`TMDataGridEditCommitArgs`, `
|
|
109
|
+
`TMDataGridEditCommitArgs`, `TMDataGridEditCommitDraftsArgs`,
|
|
102
110
|
`TMDataGridEditChange`, `TMDataGridEditorArgs`, `TMDataGridEditorComponent`,
|
|
103
111
|
`TMDataGridEditField`, `TMDataGridEditRowProjection`, `TMDataGridFieldValidate`,
|
|
104
112
|
`TMDataGridRowValidators`, `TMDataGridRowEditForm`, `TMDataGridRowAddArgs`,
|
|
@@ -110,20 +118,38 @@ Generated, pinned right, id `EDIT_COLUMN_ID`. It appears when any of these
|
|
|
110
118
|
holds:
|
|
111
119
|
|
|
112
120
|
- `editing.mode: "row"` - the lane is Save and Cancel's home
|
|
121
|
+
- `editing.mode: "draft"` - the lane is the change indicator and the per-row
|
|
122
|
+
revert, and `editing.onCommitDrafts` is not required for it
|
|
113
123
|
- `editing.onRowDelete` is set - the trash can has somewhere to report to
|
|
114
|
-
- `editing.mode: "batch"` **and** `editing.onCommitBatch` is set
|
|
115
124
|
|
|
116
125
|
`"cell"` and `"cellConfirm"` have no lane unless `editing.onRowDelete` asks for
|
|
117
126
|
one.
|
|
118
127
|
|
|
128
|
+
What the lane holds depends on the mode. Under `"row"` an open row shows
|
|
129
|
+
`save-row` and `cancel-row`; those two never render under `"draft"`. Under
|
|
130
|
+
`"draft"` every changed row shows `row-state`, whose `data-state` is `new`,
|
|
131
|
+
`edited` or `deleted`, together with `revert-row` on an edited row,
|
|
132
|
+
`restore-row` on one marked for deletion, and `edit-row` plus `discard-new-row`
|
|
133
|
+
on an entered new row. A row holding a dirty draft hides `delete-row`. Every
|
|
134
|
+
control carries a tooltip from the labels, `revertRow`, `rowStateNew`,
|
|
135
|
+
`rowStateEdited` and `rowStateDeleted` among them.
|
|
136
|
+
|
|
119
137
|
## Styling and test hooks
|
|
120
138
|
|
|
121
139
|
| Name | Kind | What it is |
|
|
122
140
|
| --- | --- | --- |
|
|
123
141
|
| `--dg-entry-height` | CSS variable | Height of the sticky entry block. From `size`. |
|
|
124
|
-
|
|
|
142
|
+
| `--dg-row-new-bg` | CSS variable | Background of an entered new row. A green tint. |
|
|
143
|
+
| `data-deleted` | Row attribute | On a row marked for deletion under draft mode. |
|
|
144
|
+
| `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. |
|
|
146
|
+
| `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. |
|
|
125
147
|
| `data-dg-part="editor-input"` | Part | The control inside an editing cell. |
|
|
126
|
-
| `data-dg-part="save-row"` / `"cancel-row"` | Parts | The edit lane's buttons, with `data-row-id`. |
|
|
148
|
+
| `data-dg-part="save-row"` / `"cancel-row"` | Parts | The edit lane's buttons on an open row, with `data-row-id`. Row mode only. |
|
|
149
|
+
| `data-dg-part="row-state"` | Part | Draft mode's change marker, with `data-row-id` and `data-state` of `new`, `edited` or `deleted`. |
|
|
150
|
+
| `data-dg-part="revert-row"` / `"restore-row"` | Parts | Drops a row's draft, and undoes a deletion mark, with `data-row-id`. |
|
|
151
|
+
| `data-dg-part="edit-row"` / `"delete-row"` | Parts | The idle lane's pencil and trash, with `data-row-id`. `edit-row` also reopens an entered new row. |
|
|
152
|
+
| `data-dg-part="confirm-new-row"` / `"discard-new-row"` | Parts | An entry row's ✓ and ✕, with `data-row-id`. |
|
|
127
153
|
| `data-dg-part="save-all"` / `"discard-all"` | Parts | `EditActions`. |
|
|
128
154
|
|
|
129
155
|
See the `testing` skill for how to compose these into selectors.
|
|
@@ -214,7 +214,7 @@ issue lands on the column whose `editField` is `"address.city"`.
|
|
|
214
214
|
|
|
215
215
|
Cross-field rules need a mode that commits the whole row at once. Under `"cell"`
|
|
216
216
|
each cell commits alone, so the rule is evaluated against the other column's
|
|
217
|
-
unedited value and cannot pass. Use `"row"` or `"
|
|
217
|
+
unedited value and cannot pass. Use `"row"` or `"draft"`.
|
|
218
218
|
|
|
219
219
|
## Server-side errors
|
|
220
220
|
|
|
@@ -242,9 +242,12 @@ row.
|
|
|
242
242
|
|
|
243
243
|
| Marker | Means |
|
|
244
244
|
| --- | --- |
|
|
245
|
+
| 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 |
|
|
245
246
|
| Blue cell corner | The field is dirty against its original value |
|
|
246
247
|
| Red cell corner | The field carries a validation error |
|
|
247
248
|
| Row error text | A pathless rule failed, or a commit was rejected |
|
|
249
|
+
| `data-dirty` on the row | The row holds a dirty draft |
|
|
248
250
|
|
|
249
251
|
The same information is readable from `edit.store`: `rows[rowId].dirtyFields`,
|
|
250
|
-
`rows[rowId].errorFields`, `rows[rowId].
|
|
252
|
+
`rows[rowId].errorFields`, `rows[rowId].hasRowError`,
|
|
253
|
+
`rows[rowId].isSubmitting`, and `rows[rowId].values` for the draft itself.
|
package/skills/grouping/SKILL.md
CHANGED
package/skills/options/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
metadata:
|
|
15
15
|
type: core
|
|
16
16
|
library: '@jielga/tmdatagrid'
|
|
17
|
-
library_version: '2.0.0-beta.
|
|
17
|
+
library_version: '2.0.0-beta.2'
|
|
18
18
|
sources:
|
|
19
19
|
- 'Jielga/TMDataGrid:src/docs/use-tm-data-grid.md'
|
|
20
20
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
|
@@ -65,7 +65,7 @@ rather than forwarded to TanStack.
|
|
|
65
65
|
| `initialState` | `Partial<TableState>` | See below | Merged over the grid defaults. |
|
|
66
66
|
| `meta` | `TMDataGridTableMeta` | `{}` | Grid configuration, see below. |
|
|
67
67
|
| `persist` | `TMDataGridPersistence` | – | State persistence, see below. |
|
|
68
|
-
| `editing` | `TMDataGridEditingOptions` | off | Turns editing on. `mode` (`"cell" \| "cellConfirm" \| "row" \| "
|
|
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
69
|
| `labels` | `TMDataGridLabelsOverride` | English | Overrides for the grid's strings and `aria-label`s. |
|
|
70
70
|
|
|
71
71
|
### Default initial state
|
package/skills/rows/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ description: >
|
|
|
17
17
|
metadata:
|
|
18
18
|
type: core
|
|
19
19
|
library: '@jielga/tmdatagrid'
|
|
20
|
-
library_version: '2.0.0-beta.
|
|
20
|
+
library_version: '2.0.0-beta.2'
|
|
21
21
|
sources:
|
|
22
22
|
- 'Jielga/TMDataGrid:src/docs/row-selection.md'
|
|
23
23
|
- 'Jielga/TMDataGrid:src/docs/row-interaction.md'
|
|
@@ -69,7 +69,7 @@ Row data attributes:
|
|
|
69
69
|
| `data-grouped` | Group rows |
|
|
70
70
|
| `data-depth` | Every row - the nesting level |
|
|
71
71
|
| `data-context-menu` | The row whose context menu is open |
|
|
72
|
-
| `data-deleted` | Rows marked for deletion under
|
|
72
|
+
| `data-deleted` | Rows marked for deletion under draft editing |
|
|
73
73
|
| `data-row-id` | Every row - its id |
|
|
74
74
|
|
|
75
75
|
## Details
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
metadata:
|
|
11
11
|
type: core
|
|
12
12
|
library: '@jielga/tmdatagrid'
|
|
13
|
-
library_version: '2.0.0-beta.
|
|
13
|
+
library_version: '2.0.0-beta.2'
|
|
14
14
|
sources:
|
|
15
15
|
- 'Jielga/TMDataGrid:src/docs/server-side.md'
|
|
16
16
|
- 'Jielga/TMDataGrid:src/tmdatagrid/useTMDataGrid.tsx'
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -11,7 +11,7 @@ description: >
|
|
|
11
11
|
metadata:
|
|
12
12
|
type: core
|
|
13
13
|
library: '@jielga/tmdatagrid'
|
|
14
|
-
library_version: '2.0.0-beta.
|
|
14
|
+
library_version: '2.0.0-beta.2'
|
|
15
15
|
sources:
|
|
16
16
|
- 'Jielga/TMDataGrid:src/docs/testing.md'
|
|
17
17
|
- 'Jielga/TMDataGrid:src/tmdatagrid/components/TMDataGrid.tsx'
|
|
@@ -71,7 +71,8 @@ Body cells carry no `data-dg-part` - the coordinate pair already names them.
|
|
|
71
71
|
|
|
72
72
|
**Keyed by `data-row-id`**: `row`, `entry-row`, `details`, `select-row`,
|
|
73
73
|
`details-toggle`, `group-toggle`, `edit-row`, `delete-row`, `save-row`,
|
|
74
|
-
`cancel-row`, `
|
|
74
|
+
`cancel-row`, `row-state`, `revert-row`, `restore-row`, `confirm-new-row`,
|
|
75
|
+
`discard-new-row`.
|
|
75
76
|
|
|
76
77
|
**Keyed by `data-column-id`**: `header`, `header-sort`, `header-menu`,
|
|
77
78
|
`header-filter`, `filter-row`, `filter-pill`, `columns-toggle`.
|
|
@@ -71,6 +71,13 @@
|
|
|
71
71
|
var(--mantine-color-text) 3.5%,
|
|
72
72
|
var(--mantine-color-body)
|
|
73
73
|
);
|
|
74
|
+
/* A confirmed entry row under draft mode - entered, awaiting Save all. A
|
|
75
|
+
whisper of green over the body, one declaration for both themes. */
|
|
76
|
+
--dg-row-new-bg: color-mix(
|
|
77
|
+
in srgb,
|
|
78
|
+
var(--mantine-color-green-6) 8%,
|
|
79
|
+
var(--mantine-color-body)
|
|
80
|
+
);
|
|
74
81
|
|
|
75
82
|
/* The stacking ladder, stated once. Body cells sit at auto (0); everything
|
|
76
83
|
sticky stacks over them in this order, and a sticky row crossing a sticky
|
|
@@ -43,7 +43,7 @@ const BUILT_IN_EDITORS: Record<
|
|
|
43
43
|
export type TMDataGridCellEditorClose =
|
|
44
44
|
| { committed: true; via: "enter" | "tab" | "shift-tab" | "pick" }
|
|
45
45
|
| { committed: false; via: "escape" }
|
|
46
|
-
/**
|
|
46
|
+
/** Draft: the editor closed, the draft stays - Enter parks, Tab moves on. */
|
|
47
47
|
| { committed: false; via: "defer" | "defer-tab" | "defer-shift-tab" };
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -59,6 +59,7 @@ export function TMDataGridCellEditor({
|
|
|
59
59
|
row,
|
|
60
60
|
takeSeedText,
|
|
61
61
|
onClose,
|
|
62
|
+
inEntryBlock = false,
|
|
62
63
|
}: {
|
|
63
64
|
cell: Cell<TMDataGridFeatures, TMDataGridRowData, unknown>;
|
|
64
65
|
row: Row<TMDataGridFeatures, TMDataGridRowData>;
|
|
@@ -66,6 +67,13 @@ export function TMDataGridCellEditor({
|
|
|
66
67
|
takeSeedText: () => string | undefined;
|
|
67
68
|
/** After the editor closed itself - the table moves the focus. */
|
|
68
69
|
onClose: (args: TMDataGridCellEditorClose) => void;
|
|
70
|
+
/**
|
|
71
|
+
* Hosted by the entry block rather than a body cell. Under draft the keys
|
|
72
|
+
* change meaning there: Enter confirms the whole entry (the engine parks
|
|
73
|
+
* it), and Tab stays the browser's, walking the entry row's own inputs the
|
|
74
|
+
* way row mode's do.
|
|
75
|
+
*/
|
|
76
|
+
inEntryBlock?: boolean;
|
|
69
77
|
}) {
|
|
70
78
|
const { table, edit, features, labels, controlSize } = useTMDataGridContext();
|
|
71
79
|
// Row mode mounts one host per editable cell of the row; every key that
|
|
@@ -164,7 +172,7 @@ export function TMDataGridCellEditor({
|
|
|
164
172
|
onClose({ committed: false, via: "escape" });
|
|
165
173
|
};
|
|
166
174
|
|
|
167
|
-
/**
|
|
175
|
+
/** Draft: the key closes the editor and leaves the draft for submitAll. */
|
|
168
176
|
const deferAndClose = (via: "defer" | "defer-tab" | "defer-shift-tab") => {
|
|
169
177
|
if (closingRef.current) return;
|
|
170
178
|
closingRef.current = true;
|
|
@@ -173,23 +181,27 @@ export function TMDataGridCellEditor({
|
|
|
173
181
|
};
|
|
174
182
|
|
|
175
183
|
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
176
|
-
const
|
|
184
|
+
const isDraft = features.editMode === "draft";
|
|
177
185
|
if (event.key === "Enter") {
|
|
178
186
|
event.preventDefault();
|
|
179
187
|
event.stopPropagation();
|
|
180
188
|
// In row mode Enter (and Ctrl+Enter, the documented pair) saves the
|
|
181
|
-
// row; in
|
|
182
|
-
// the draft
|
|
183
|
-
|
|
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");
|
|
184
193
|
else void commitAndClose("enter");
|
|
185
194
|
} else if (event.key === "Escape") {
|
|
186
195
|
event.preventDefault();
|
|
187
196
|
event.stopPropagation();
|
|
188
197
|
cancelAndClose();
|
|
189
|
-
} else if (event.key === "Tab" && !isRowMode) {
|
|
198
|
+
} else if (event.key === "Tab" && !isRowMode && !(isDraft && inEntryBlock)) {
|
|
199
|
+
// In the draft entry block Tab stays the browser's: every editable
|
|
200
|
+
// cell's editor is mounted, so it walks the row's inputs as row mode's
|
|
201
|
+
// does.
|
|
190
202
|
event.preventDefault();
|
|
191
203
|
event.stopPropagation();
|
|
192
|
-
if (
|
|
204
|
+
if (isDraft) {
|
|
193
205
|
deferAndClose(event.shiftKey ? "defer-shift-tab" : "defer-tab");
|
|
194
206
|
} else {
|
|
195
207
|
void commitAndClose(event.shiftKey ? "shift-tab" : "tab");
|
|
@@ -120,7 +120,7 @@ export type TMDataGridEditActionsProps = {
|
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
123
|
+
* Draft mode's toolbar chrome: Save with the dirty-row count, and Discard.
|
|
124
124
|
* Both read the edit store, so they grey out while nothing is dirty and the
|
|
125
125
|
* Save spins while a submit is in flight. Works under any `editing.mode` - a
|
|
126
126
|
* cellConfirm grid accumulating drafts can offer the same pair - and renders
|