@svgrid/grid 1.1.0 → 1.1.1
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/GridMenus.svelte +205 -127
- package/dist/SvGrid.controller.svelte.d.ts +44 -1
- package/dist/SvGrid.controller.svelte.js +223 -38
- package/dist/SvGrid.css +2012 -1709
- package/dist/SvGrid.helpers.test.d.ts +1 -0
- package/dist/SvGrid.helpers.test.js +298 -0
- package/dist/SvGrid.svelte +2346 -2043
- package/dist/SvGrid.types.d.ts +91 -4
- package/dist/aligned-grids.d.ts +6 -0
- package/dist/aligned-grids.js +84 -0
- package/dist/aligned-grids.test.d.ts +1 -0
- package/dist/aligned-grids.test.js +75 -0
- package/dist/build-api.coverage.test.d.ts +20 -0
- package/dist/build-api.coverage.test.js +505 -0
- package/dist/build-api.js +59 -29
- package/dist/cell-render.test.d.ts +1 -0
- package/dist/cell-render.test.js +338 -0
- package/dist/chart-export.test.d.ts +1 -0
- package/dist/chart-export.test.js +302 -0
- package/dist/chart.coverage.test.d.ts +1 -0
- package/dist/chart.coverage.test.js +748 -0
- package/dist/clipboard.js +88 -24
- package/dist/clipboard.test.d.ts +1 -0
- package/dist/clipboard.test.js +700 -0
- package/dist/collaboration.coverage.test.d.ts +1 -0
- package/dist/collaboration.coverage.test.js +200 -0
- package/dist/column-groups.d.ts +19 -0
- package/dist/column-groups.js +62 -0
- package/dist/column-groups.test.d.ts +1 -0
- package/dist/column-groups.test.js +56 -0
- package/dist/column-types.d.ts +10 -0
- package/dist/column-types.js +63 -0
- package/dist/column-types.test.d.ts +1 -0
- package/dist/column-types.test.js +62 -0
- package/dist/columns.test.d.ts +1 -0
- package/dist/columns.test.js +625 -0
- package/dist/core.d.ts +85 -0
- package/dist/editing.d.ts +7 -0
- package/dist/editing.js +191 -5
- package/dist/editing.test.d.ts +1 -0
- package/dist/editing.test.js +732 -0
- package/dist/editors/cell-editors.coverage.test.d.ts +1 -0
- package/dist/editors/cell-editors.coverage.test.js +139 -0
- package/dist/facet-buckets.test.d.ts +1 -0
- package/dist/facet-buckets.test.js +296 -0
- package/dist/filter-operators.test.d.ts +1 -0
- package/dist/filter-operators.test.js +135 -0
- package/dist/hyperformula-adapter.test.d.ts +1 -0
- package/dist/hyperformula-adapter.test.js +205 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/keyboard-handlers.coverage.test.d.ts +1 -0
- package/dist/keyboard-handlers.coverage.test.js +495 -0
- package/dist/keyboard-handlers.js +7 -2
- package/dist/menus.js +1 -0
- package/dist/menus.test.d.ts +1 -0
- package/dist/menus.test.js +560 -0
- package/dist/named-views.coverage.test.d.ts +1 -0
- package/dist/named-views.coverage.test.js +180 -0
- package/dist/row-drag.d.ts +49 -0
- package/dist/row-drag.js +221 -0
- package/dist/row-drag.test.d.ts +1 -0
- package/dist/row-drag.test.js +142 -0
- package/dist/row-resize.test.d.ts +1 -0
- package/dist/row-resize.test.js +329 -0
- package/dist/scroll-sync.js +3 -0
- package/dist/scroll-sync.test.d.ts +1 -0
- package/dist/scroll-sync.test.js +290 -0
- package/dist/selection.d.ts +7 -1
- package/dist/selection.js +76 -36
- package/dist/selection.multi-range.test.d.ts +1 -0
- package/dist/selection.multi-range.test.js +55 -0
- package/dist/selection.test.d.ts +1 -0
- package/dist/selection.test.js +647 -0
- package/dist/server-data-source.coverage.test.d.ts +1 -0
- package/dist/server-data-source.coverage.test.js +154 -0
- package/dist/spreadsheet.d.ts +30 -0
- package/dist/spreadsheet.js +48 -0
- package/dist/spreadsheet.test.d.ts +1 -0
- package/dist/spreadsheet.test.js +446 -0
- package/dist/sv-grid-scrollbar.js +13 -1
- package/dist/svgrid-wrapper.types.d.ts +19 -0
- package/dist/svgrid.behavior.test.js +20 -0
- package/dist/svgrid.interaction.test.js +31 -0
- package/dist/svgrid.new-features.wrapper.test.js +34 -2
- package/dist/test-setup.js +9 -3
- package/dist/virtualization/scroll-scaling.d.ts +17 -0
- package/dist/virtualization/scroll-scaling.js +35 -0
- package/dist/virtualization/scroll-scaling.test.js +42 -1
- package/package.json +2 -1
- package/src/GridMenus.svelte +205 -127
- package/src/SvGrid.controller.svelte.ts +2352 -2195
- package/src/SvGrid.css +2012 -1747
- package/src/SvGrid.svelte +2346 -2047
- package/src/SvGrid.types.ts +537 -456
- package/src/aligned-grids.test.ts +80 -0
- package/src/aligned-grids.ts +87 -0
- package/src/build-api.ts +683 -663
- package/src/clipboard.test.ts +49 -0
- package/src/clipboard.ts +51 -23
- package/src/column-groups.test.ts +59 -0
- package/src/column-groups.ts +80 -0
- package/src/column-types.test.ts +68 -0
- package/src/column-types.ts +82 -0
- package/src/core.ts +78 -0
- package/src/editing.ts +669 -513
- package/src/index.ts +12 -0
- package/src/menus.ts +1 -0
- package/src/row-drag.test.ts +168 -0
- package/src/row-drag.ts +255 -0
- package/src/scroll-sync.ts +2 -0
- package/src/selection.multi-range.test.ts +61 -0
- package/src/selection.ts +71 -37
- package/src/spreadsheet.test.ts +489 -445
- package/src/spreadsheet.ts +304 -246
- package/src/svgrid-wrapper.types.ts +19 -0
- package/src/svgrid.new-features.wrapper.test.ts +2 -2
package/dist/core.d.ts
CHANGED
|
@@ -37,6 +37,30 @@ export type CellContext<TData extends RowData> = {
|
|
|
37
37
|
table: SvGrid<TData>;
|
|
38
38
|
getValue: () => unknown;
|
|
39
39
|
};
|
|
40
|
+
/** Params passed to a column's `colSpan(...)` / `rowSpan(...)` callbacks. */
|
|
41
|
+
export type CellSpanParams<TData extends RowData = RowData> = {
|
|
42
|
+
/** The row's underlying data object. */
|
|
43
|
+
data: TData;
|
|
44
|
+
/** Display-row index in the current (filtered/sorted) row set. */
|
|
45
|
+
rowIndex: number;
|
|
46
|
+
/** The column's id. */
|
|
47
|
+
columnId: string;
|
|
48
|
+
/** The cell's base value for this column. */
|
|
49
|
+
value: unknown;
|
|
50
|
+
};
|
|
51
|
+
/** Params passed to a column's `valueParser(...)` on edit commit. */
|
|
52
|
+
export type ValueParserParams<TData extends RowData = RowData> = {
|
|
53
|
+
/** The value after built-in per-`editorType` coercion. */
|
|
54
|
+
newValue: unknown;
|
|
55
|
+
/** The cell's previous value. */
|
|
56
|
+
oldValue: unknown;
|
|
57
|
+
/** The raw string the editor produced (pre-coercion). */
|
|
58
|
+
rawInput: string;
|
|
59
|
+
/** The row's underlying data object. */
|
|
60
|
+
data: TData;
|
|
61
|
+
/** The column's id. */
|
|
62
|
+
columnId: string;
|
|
63
|
+
};
|
|
40
64
|
/**
|
|
41
65
|
* Context passed to a custom `cellEditor` snippet/component. Three write
|
|
42
66
|
* helpers cover the lifecycle:
|
|
@@ -110,6 +134,45 @@ export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
110
134
|
footer?: ColumnDefTemplate<HeaderContext<TData>>;
|
|
111
135
|
cell?: ColumnDefTemplate<CellContext<TData>>;
|
|
112
136
|
columns?: Array<ColumnDef<TFeatures, TData>>;
|
|
137
|
+
/**
|
|
138
|
+
* Declarative cell spanning (merged cells). Return how many COLUMNS this
|
|
139
|
+
* cell spans to the right (1 = no span). Value-driven, AG-Grid-style. Feed
|
|
140
|
+
* `spansToMerges(rows, columns)` into `spreadsheetLayout` to apply - it uses
|
|
141
|
+
* the same real `colspan`/`rowspan` merge engine (no separate code path).
|
|
142
|
+
*/
|
|
143
|
+
colSpan?: (params: CellSpanParams<TData>) => number;
|
|
144
|
+
/**
|
|
145
|
+
* Declarative cell spanning (merged cells). Return how many ROWS this cell
|
|
146
|
+
* spans downward (1 = no span). See `colSpan` for how to apply.
|
|
147
|
+
*/
|
|
148
|
+
rowSpan?: (params: CellSpanParams<TData>) => number;
|
|
149
|
+
/**
|
|
150
|
+
* High-level data type for the column. A convenience that resolves to the
|
|
151
|
+
* right `editorType`, alignment, date `format`, and filter operators without
|
|
152
|
+
* setting each by hand:
|
|
153
|
+
* 'text' → text editor, left-aligned
|
|
154
|
+
* 'number' → number editor, right-aligned, numeric filter operators
|
|
155
|
+
* 'boolean' → checkbox editor, centered
|
|
156
|
+
* 'date' → date editor (Date values), right-aligned, `{ type: 'date' }` format
|
|
157
|
+
* 'dateString' → date editor for ISO date STRINGS (e.g. '2026-06-27')
|
|
158
|
+
* Anything you set explicitly (`editorType`, `align`, `format`) still wins -
|
|
159
|
+
* `cellDataType` only fills the gaps. Grid-level `inferColumnTypes` infers
|
|
160
|
+
* this from the first data row for columns that declare neither.
|
|
161
|
+
*/
|
|
162
|
+
cellDataType?: 'text' | 'number' | 'boolean' | 'date' | 'dateString';
|
|
163
|
+
/**
|
|
164
|
+
* For a column INSIDE a collapsible column group: `'open'` shows this column
|
|
165
|
+
* only while the group is expanded, `'closed'` only while collapsed. Omit to
|
|
166
|
+
* always show it. Setting it on any direct child gives the parent group a
|
|
167
|
+
* collapse toggle. Pair with `openByDefault` on the group.
|
|
168
|
+
*/
|
|
169
|
+
columnGroupShow?: 'open' | 'closed';
|
|
170
|
+
/**
|
|
171
|
+
* For a GROUP column (one with `columns: [...]`): start the group expanded.
|
|
172
|
+
* Defaults to `false` (collapsed), matching AG Grid - so only the always-on
|
|
173
|
+
* and `columnGroupShow: 'closed'` children show until the user expands it.
|
|
174
|
+
*/
|
|
175
|
+
openByDefault?: boolean;
|
|
113
176
|
editorType?: 'text' | 'number' | 'date' | 'datetime' | 'time' | 'password' | 'checkbox' | 'list' | 'chips' | 'select' | 'rich-select' | 'autocomplete' | 'textarea' | 'color' | 'rating';
|
|
114
177
|
/**
|
|
115
178
|
* Custom in-cell editor. Receives the cell context PLUS a `commit(value)`
|
|
@@ -143,6 +206,21 @@ export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
143
206
|
* `false`.
|
|
144
207
|
*/
|
|
145
208
|
editable?: boolean | ((context: CellContext<TData>) => boolean);
|
|
209
|
+
/**
|
|
210
|
+
* Transform the committed edit value before it is written to the row.
|
|
211
|
+
* Runs after the built-in per-`editorType` coercion, so `newValue` is
|
|
212
|
+
* already type-parsed; return the final value to store (e.g. round a
|
|
213
|
+
* number, uppercase a code, look up an id). AG-Grid-style `valueParser`.
|
|
214
|
+
*/
|
|
215
|
+
valueParser?: (params: ValueParserParams<TData>) => unknown;
|
|
216
|
+
/**
|
|
217
|
+
* Briefly flash / highlight this column's cell when its value changes
|
|
218
|
+
* (streaming feeds, edits, server pushes). `true` uses the default flash;
|
|
219
|
+
* pass `{ className }` to apply your own animation class instead.
|
|
220
|
+
*/
|
|
221
|
+
cellFlash?: boolean | {
|
|
222
|
+
className?: string;
|
|
223
|
+
};
|
|
146
224
|
/**
|
|
147
225
|
* When `false`, this column never shows a sort indicator and clicking
|
|
148
226
|
* its header is a no-op - `api.setSort(thisColumn, ...)` is also
|
|
@@ -203,6 +281,13 @@ export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
203
281
|
sparkline?: SparklineConfig;
|
|
204
282
|
/** Initial column width in pixels. Falls back to the grid's `columnWidth` prop. */
|
|
205
283
|
width?: number;
|
|
284
|
+
/**
|
|
285
|
+
* Initial visibility. Set `false` to start the column hidden while still
|
|
286
|
+
* listing it in the Choose Columns UI for the user to re-enable. Applied
|
|
287
|
+
* once at mount; after that `api.setColumnVisible` / user toggles win.
|
|
288
|
+
* On a group column, `false` hides the whole group's leaf columns.
|
|
289
|
+
*/
|
|
290
|
+
visible?: boolean;
|
|
206
291
|
/**
|
|
207
292
|
* Horizontal alignment for header and body cells. When omitted, the
|
|
208
293
|
* default is inferred from `editorType`:
|
package/dist/editing.d.ts
CHANGED
|
@@ -13,6 +13,12 @@ export declare function createEditing<TFeatures extends TableFeatures = TableFea
|
|
|
13
13
|
getRowColumnValue: (row: Row<TData>, columnId: string) => unknown;
|
|
14
14
|
getCellDisplayValue: (rowId: string, columnId: string, baseValue: unknown) => any;
|
|
15
15
|
startEditingWithChar: (rowIndex: number, colIndex: number, char: string) => boolean;
|
|
16
|
+
startEditing: (rowIndex: number, columnId: string) => boolean;
|
|
17
|
+
stopEditing: (cancel?: boolean) => boolean;
|
|
18
|
+
startFullRowEdit: (rowIndex: number) => boolean;
|
|
19
|
+
setFullRowDraft: (columnId: string, value: unknown) => void;
|
|
20
|
+
commitFullRowEdit: () => void;
|
|
21
|
+
cancelFullRowEdit: () => void;
|
|
16
22
|
saveEditingCell: () => void;
|
|
17
23
|
applyHistoryStep: (step: HistoryStep, direction: "undo" | "redo") => void;
|
|
18
24
|
updateEditingCellValue: (value: string) => void;
|
|
@@ -20,5 +26,6 @@ export declare function createEditing<TFeatures extends TableFeatures = TableFea
|
|
|
20
26
|
focusOnMount: (node: HTMLInputElement | HTMLTextAreaElement) => void;
|
|
21
27
|
onCellDoubleClick: (rowIndex: number, colIndex: number) => void;
|
|
22
28
|
pasteFromClipboard: () => Promise<void>;
|
|
29
|
+
onGridPaste: (event: ClipboardEvent) => void;
|
|
23
30
|
};
|
|
24
31
|
export {};
|
package/dist/editing.js
CHANGED
|
@@ -110,27 +110,39 @@ export function createEditing(ctx) {
|
|
|
110
110
|
multiple: column?.columnDef.editorMultiple === true,
|
|
111
111
|
});
|
|
112
112
|
let oldValue = undefined;
|
|
113
|
+
let finalValue = parsedValue;
|
|
113
114
|
if (row?.original && column?.columnDef.field) {
|
|
114
115
|
oldValue = row.original[column.columnDef.field];
|
|
116
|
+
// Per-column valueParser refines the type-coerced value before storing.
|
|
117
|
+
const parser = column.columnDef.valueParser;
|
|
118
|
+
if (parser) {
|
|
119
|
+
finalValue = parser({
|
|
120
|
+
newValue: parsedValue,
|
|
121
|
+
oldValue,
|
|
122
|
+
rawInput: ctx.editingCell.value,
|
|
123
|
+
data: row.original,
|
|
124
|
+
columnId: ctx.editingCell.columnId,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
115
127
|
row.original[column.columnDef.field] =
|
|
116
|
-
|
|
128
|
+
finalValue;
|
|
117
129
|
}
|
|
118
130
|
const key = getCellKey(ctx.editingCell.rowId, ctx.editingCell.columnId);
|
|
119
131
|
ctx.editedCellValues = {
|
|
120
132
|
...ctx.editedCellValues,
|
|
121
|
-
[key]:
|
|
133
|
+
[key]: finalValue,
|
|
122
134
|
};
|
|
123
135
|
ctx.grid.store.setState((prev) => ({ ...prev }));
|
|
124
136
|
// Record into the history at the current pointer. Any forward
|
|
125
137
|
// history (steps the user could have redone) is truncated - this
|
|
126
138
|
// is the standard "edit invalidates redo" rule.
|
|
127
|
-
if (oldValue !==
|
|
139
|
+
if (oldValue !== finalValue && row?.original && column?.columnDef.field) {
|
|
128
140
|
const step = {
|
|
129
141
|
rowId: ctx.editingCell.rowId,
|
|
130
142
|
columnId: ctx.editingCell.columnId,
|
|
131
143
|
field: column.columnDef.field,
|
|
132
144
|
before: oldValue,
|
|
133
|
-
after:
|
|
145
|
+
after: finalValue,
|
|
134
146
|
};
|
|
135
147
|
const truncated = ctx.history.slice(0, ctx.historyPtr + 1);
|
|
136
148
|
truncated.push(step);
|
|
@@ -156,7 +168,7 @@ export function createEditing(ctx) {
|
|
|
156
168
|
rowIndex,
|
|
157
169
|
columnId: column.id,
|
|
158
170
|
oldValue,
|
|
159
|
-
newValue:
|
|
171
|
+
newValue: finalValue,
|
|
160
172
|
row: row.original,
|
|
161
173
|
});
|
|
162
174
|
}
|
|
@@ -228,6 +240,13 @@ export function createEditing(ctx) {
|
|
|
228
240
|
return;
|
|
229
241
|
if (isGroupRow(row))
|
|
230
242
|
return;
|
|
243
|
+
// Full-row editing: put the whole row into edit instead of one cell.
|
|
244
|
+
if (ctx.props.fullRowEditing) {
|
|
245
|
+
if (ctx.fullRowEdit?.rowId === row.id)
|
|
246
|
+
return;
|
|
247
|
+
startFullRowEdit(rowIndex);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
231
250
|
if (!isCellEditable(column, row))
|
|
232
251
|
return;
|
|
233
252
|
if (ctx.editingCell?.rowId === row.id && ctx.editingCell?.columnId === column.id)
|
|
@@ -255,6 +274,12 @@ export function createEditing(ctx) {
|
|
|
255
274
|
ctx.setSelection(rowIndex, colIndex);
|
|
256
275
|
}
|
|
257
276
|
async function pasteFromClipboard() {
|
|
277
|
+
// Preferred path: the async Clipboard API. Requires a secure context
|
|
278
|
+
// (HTTPS or localhost) AND read permission; it also works on Firefox,
|
|
279
|
+
// where the native `paste` event does NOT fire on a non-editable element.
|
|
280
|
+
// On plain HTTP (a XAMPP/Apache LAN host) `navigator.clipboard` is
|
|
281
|
+
// undefined - there the Ctrl+V keydown handler skips preventDefault and
|
|
282
|
+
// lets the browser deliver a native `paste` event to `onGridPaste`.
|
|
258
283
|
if (!navigator.clipboard?.readText)
|
|
259
284
|
return;
|
|
260
285
|
let text;
|
|
@@ -264,6 +289,28 @@ export function createEditing(ctx) {
|
|
|
264
289
|
catch {
|
|
265
290
|
return;
|
|
266
291
|
}
|
|
292
|
+
applyPastedText(text);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Native `paste` ClipboardEvent fallback (insecure-context path). A real
|
|
296
|
+
* Ctrl/Cmd+V on the focused grid root fires this with `clipboardData`
|
|
297
|
+
* readable even over plain HTTP and with no permission prompt. Wired on the
|
|
298
|
+
* grid root in SvGrid.svelte; the keydown handler lets the gesture through
|
|
299
|
+
* (no preventDefault) whenever the async API is unavailable.
|
|
300
|
+
*/
|
|
301
|
+
function onGridPaste(event) {
|
|
302
|
+
// When the async API is available the keydown handler already pasted via
|
|
303
|
+
// pasteFromClipboard(); ignore the (suppressed) native event so we don't
|
|
304
|
+
// paste twice.
|
|
305
|
+
if (typeof navigator.clipboard?.readText === "function")
|
|
306
|
+
return;
|
|
307
|
+
const text = event.clipboardData?.getData("text/plain") ?? "";
|
|
308
|
+
if (!text)
|
|
309
|
+
return;
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
applyPastedText(text);
|
|
312
|
+
}
|
|
313
|
+
function applyPastedText(text) {
|
|
267
314
|
const anchor = ctx.selectionRange.anchor ?? ctx.grid.getState().activeCell;
|
|
268
315
|
if (!anchor)
|
|
269
316
|
return;
|
|
@@ -326,12 +373,150 @@ export function createEditing(ctx) {
|
|
|
326
373
|
ctx.internalData = next;
|
|
327
374
|
ctx.grid.store.setState((prev) => ({ ...prev }));
|
|
328
375
|
}
|
|
376
|
+
/** Programmatically begin editing a cell (mirrors a double-click). */
|
|
377
|
+
function startEditing(rowIndex, columnId) {
|
|
378
|
+
if (!ctx.editingEnabled)
|
|
379
|
+
return false;
|
|
380
|
+
const row = ctx.allRows[rowIndex];
|
|
381
|
+
if (!row || isGroupRow(row))
|
|
382
|
+
return false;
|
|
383
|
+
const colIndex = ctx.allColumns.findIndex((c) => c.id === columnId);
|
|
384
|
+
if (colIndex < 0)
|
|
385
|
+
return false;
|
|
386
|
+
const column = ctx.allColumns[colIndex];
|
|
387
|
+
if (!isCellEditable(column, row))
|
|
388
|
+
return false;
|
|
389
|
+
if (ctx.editingCell?.rowId === row.id &&
|
|
390
|
+
ctx.editingCell?.columnId === columnId)
|
|
391
|
+
return true;
|
|
392
|
+
onCellDoubleClick(rowIndex, colIndex);
|
|
393
|
+
return (ctx.editingCell?.rowId === row.id &&
|
|
394
|
+
ctx.editingCell?.columnId === columnId);
|
|
395
|
+
}
|
|
396
|
+
/** Commit (default) or cancel the active edit, if any. */
|
|
397
|
+
function stopEditing(cancel = false) {
|
|
398
|
+
if (!ctx.editingCell)
|
|
399
|
+
return false;
|
|
400
|
+
if (cancel) {
|
|
401
|
+
ctx.editingCell = null;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
saveEditingCell();
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
// ---- Full-row editing -------------------------------------------------
|
|
409
|
+
/** Put the whole row into edit mode, seeding a draft per editable column. */
|
|
410
|
+
function startFullRowEdit(rowIndex) {
|
|
411
|
+
if (!ctx.editingEnabled || !ctx.props.fullRowEditing)
|
|
412
|
+
return false;
|
|
413
|
+
const row = ctx.allRows[rowIndex];
|
|
414
|
+
if (!row || isGroupRow(row))
|
|
415
|
+
return false;
|
|
416
|
+
if (ctx.fullRowEdit && ctx.fullRowEdit.rowId !== row.id)
|
|
417
|
+
commitFullRowEdit();
|
|
418
|
+
ctx.editingCell = null; // never both at once
|
|
419
|
+
const draft = {};
|
|
420
|
+
for (const column of ctx.allColumns) {
|
|
421
|
+
if (!column.columnDef.field)
|
|
422
|
+
continue;
|
|
423
|
+
if (!isCellEditable(column, row))
|
|
424
|
+
continue;
|
|
425
|
+
draft[column.id] = getCellDisplayValue(row.id, column.id, row.getCellValueByColumnId(column.id));
|
|
426
|
+
}
|
|
427
|
+
ctx.fullRowEdit = { rowId: row.id, draft };
|
|
428
|
+
ctx.setActiveCell(rowIndex, Math.max(0, ctx.allColumns.findIndex((c) => c.id in draft)));
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
/** Stage one column's draft value during full-row editing. */
|
|
432
|
+
function setFullRowDraft(columnId, value) {
|
|
433
|
+
const fr = ctx.fullRowEdit;
|
|
434
|
+
if (!fr)
|
|
435
|
+
return;
|
|
436
|
+
ctx.fullRowEdit = { rowId: fr.rowId, draft: { ...fr.draft, [columnId]: value } };
|
|
437
|
+
}
|
|
438
|
+
/** Commit every drafted column of the full-row edit in one data update. */
|
|
439
|
+
function commitFullRowEdit() {
|
|
440
|
+
const fr = ctx.fullRowEdit;
|
|
441
|
+
if (!fr)
|
|
442
|
+
return;
|
|
443
|
+
const row = ctx.allRows.find((r) => r.id === fr.rowId);
|
|
444
|
+
if (!row?.original) {
|
|
445
|
+
ctx.fullRowEdit = null;
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const changed = [];
|
|
449
|
+
for (const column of ctx.allColumns) {
|
|
450
|
+
if (!(column.id in fr.draft))
|
|
451
|
+
continue;
|
|
452
|
+
const field = column.columnDef.field;
|
|
453
|
+
if (!field)
|
|
454
|
+
continue;
|
|
455
|
+
const editorType = (column.columnDef.editorType ?? "text");
|
|
456
|
+
const parsed = parseEditorValue(editorType, fr.draft[column.id], {
|
|
457
|
+
multiple: column.columnDef.editorMultiple === true,
|
|
458
|
+
});
|
|
459
|
+
const oldValue = row.original[field];
|
|
460
|
+
const parser = column.columnDef.valueParser;
|
|
461
|
+
const finalValue = parser
|
|
462
|
+
? parser({
|
|
463
|
+
newValue: parsed,
|
|
464
|
+
oldValue,
|
|
465
|
+
rawInput: String(fr.draft[column.id] ?? ""),
|
|
466
|
+
data: row.original,
|
|
467
|
+
columnId: column.id,
|
|
468
|
+
})
|
|
469
|
+
: parsed;
|
|
470
|
+
if (oldValue !== finalValue) {
|
|
471
|
+
row.original[field] = finalValue;
|
|
472
|
+
const key = getCellKey(fr.rowId, column.id);
|
|
473
|
+
ctx.editedCellValues = { ...ctx.editedCellValues, [key]: finalValue };
|
|
474
|
+
changed.push({ columnId: column.id, field, before: oldValue, after: finalValue });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// Record the whole-row change as consecutive history steps.
|
|
478
|
+
if (changed.length) {
|
|
479
|
+
let hist = ctx.history.slice(0, ctx.historyPtr + 1);
|
|
480
|
+
for (const c of changed) {
|
|
481
|
+
hist.push({ rowId: fr.rowId, columnId: c.columnId, field: c.field, before: c.before, after: c.after });
|
|
482
|
+
}
|
|
483
|
+
if (hist.length > ctx.UNDO_LIMIT)
|
|
484
|
+
hist = hist.slice(hist.length - ctx.UNDO_LIMIT);
|
|
485
|
+
ctx.history = hist;
|
|
486
|
+
ctx.historyPtr = ctx.history.length - 1;
|
|
487
|
+
ctx.historyVersion += 1;
|
|
488
|
+
}
|
|
489
|
+
ctx.grid.store.setState((prev) => ({ ...prev }));
|
|
490
|
+
if (ctx.props.onCellValueChange && changed.length) {
|
|
491
|
+
const rowIndex = ctx.internalData.indexOf(row.original);
|
|
492
|
+
for (const c of changed) {
|
|
493
|
+
ctx.props.onCellValueChange({
|
|
494
|
+
rowIndex,
|
|
495
|
+
columnId: c.columnId,
|
|
496
|
+
oldValue: c.before,
|
|
497
|
+
newValue: c.after,
|
|
498
|
+
row: row.original,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
ctx.fullRowEdit = null;
|
|
503
|
+
}
|
|
504
|
+
/** Discard the full-row edit. */
|
|
505
|
+
function cancelFullRowEdit() {
|
|
506
|
+
ctx.fullRowEdit = null;
|
|
507
|
+
}
|
|
329
508
|
return {
|
|
330
509
|
isCellEditable,
|
|
331
510
|
isCellEditableAt,
|
|
332
511
|
getRowColumnValue,
|
|
333
512
|
getCellDisplayValue,
|
|
334
513
|
startEditingWithChar,
|
|
514
|
+
startEditing,
|
|
515
|
+
stopEditing,
|
|
516
|
+
startFullRowEdit,
|
|
517
|
+
setFullRowDraft,
|
|
518
|
+
commitFullRowEdit,
|
|
519
|
+
cancelFullRowEdit,
|
|
335
520
|
saveEditingCell,
|
|
336
521
|
applyHistoryStep,
|
|
337
522
|
updateEditingCellValue,
|
|
@@ -339,5 +524,6 @@ export function createEditing(ctx) {
|
|
|
339
524
|
focusOnMount,
|
|
340
525
|
onCellDoubleClick,
|
|
341
526
|
pasteFromClipboard,
|
|
527
|
+
onGridPaste,
|
|
342
528
|
};
|
|
343
529
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|