@svgrid/enterprise 2.6.4 → 2.7.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/dist/SvGridBulkEditDrawer.svelte +158 -0
- package/dist/SvGridBulkEditDrawer.svelte.d.ts +9 -0
- package/dist/SvGridSelectionBar.svelte +452 -0
- package/dist/SvGridSelectionBar.svelte.d.ts +6 -0
- package/dist/SvPivotDesigner.svelte +3 -3
- package/dist/SvPivotDesigner.svelte.d.ts +3 -3
- package/dist/bulk-edit.d.ts +64 -0
- package/dist/bulk-edit.js +152 -0
- package/dist/cdn/svgrid-enterprise.svelte-external.js +6182 -5779
- package/dist/designer/assets/GridMenus-Cgilexpk.js +8 -0
- package/dist/designer/assets/SvDateRangeInput-BV5OXYQ2.css +1 -0
- package/dist/designer/assets/SvDateRangeInput-CVn-yqrQ.js +1 -0
- package/dist/designer/assets/SvDateTimePicker-BdioTrJQ.js +1 -0
- package/dist/designer/assets/SvDateTimePicker-CY8TK4BT.css +1 -0
- package/dist/designer/assets/SvField-BsqiA5me.js +1 -0
- package/dist/designer/assets/SvGridCellEditor-dLY7_kk9.js +2 -0
- package/dist/designer/assets/SvListBox-CVYQcLX9.js +1 -0
- package/dist/designer/assets/{SvListBox-BMSfFJJT.css → SvListBox-DgN8kvjt.css} +1 -1
- package/dist/designer/assets/column-resize-C_inr5zT.js +1 -0
- package/dist/designer/assets/date-format-4DJvNtG2.js +1 -0
- package/dist/designer/assets/{SvDateTimePicker-Cygij-dr.css → date-format-Dzy4mTGh.css} +1 -1
- package/dist/designer/assets/dismissable-BO547MH9.js +1 -0
- package/dist/designer/assets/editor-registry-BjP16H0v.js +1 -0
- package/dist/designer/assets/{index-DxBZuosR.js → index-BvaLmQSd.js} +129 -129
- package/dist/designer/assets/index-Cp7fDJ3b.css +1 -0
- package/dist/designer/assets/{js-scroller.svelte-03aI6ovu.js → js-scroller.svelte-Bhxqb3-c.js} +2 -2
- package/dist/designer/assets/preload-helper-CG0n22O7.js +1 -0
- package/dist/designer/assets/row-resize-B0FWHcNr.js +1 -0
- package/dist/designer/assets/src-7qOYsBug.js +2896 -0
- package/dist/designer/assets/src-B7DNcoQK.css +1 -0
- package/dist/designer/assets/validate-h-aslRV9.js +1 -0
- package/dist/designer/index.html +18 -13
- package/dist/export.d.ts +4 -0
- package/dist/import.d.ts +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +8 -0
- package/dist/install.d.ts +3 -0
- package/dist/install.js +2 -0
- package/dist/node/studio.js +65 -3
- package/dist/pivot.d.ts +11 -0
- package/dist/pivot.js +6 -1
- package/dist/selection-bar.d.ts +7 -0
- package/dist/selection-bar.js +42 -0
- package/dist/studio/emit-project.js +6 -0
- package/dist/studio/ui-components.generated.js +64 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -3
- package/dist/designer/assets/GridMenus-BIU7Nfbr.js +0 -7
- package/dist/designer/assets/SvDateTimePicker-DOBYyLeN.js +0 -1
- package/dist/designer/assets/SvGrid.helpers-C6wxF55r.js +0 -1
- package/dist/designer/assets/SvGridCellEditor-CMtmQINJ.js +0 -2
- package/dist/designer/assets/SvListBox-D-_I_Aff.js +0 -1
- package/dist/designer/assets/dismissable-2EZKoens.js +0 -1
- package/dist/designer/assets/editor-registry-BB1Qb5mp.js +0 -1
- package/dist/designer/assets/index-BfzoL904.css +0 -1
- package/dist/designer/assets/src-D8qr49rE.css +0 -1
- package/dist/designer/assets/src-Dn1Axx6T.js +0 -2896
- /package/dist/designer/assets/{dismissable-BF-9TJSd.css → SvField-BF-9TJSd.css} +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk edit - set one or more fields across every selected row, as a single
|
|
3
|
+
* undoable run. The engine half; `SvGridBulkEditDrawer.svelte` is the UI.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of the component so it can be tested without mounting anything, and
|
|
6
|
+
* so the rules below live in one readable place rather than inside markup.
|
|
7
|
+
*/
|
|
8
|
+
export type BulkEditField = {
|
|
9
|
+
/** Column id, which is what the write is addressed by. */
|
|
10
|
+
id: string;
|
|
11
|
+
/** Header text, or the column id when the header is not a plain string. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** The column's editor type, mapped to a form control by the drawer. */
|
|
14
|
+
editorType: string;
|
|
15
|
+
/** Options for list / select style columns, when the column declares them. */
|
|
16
|
+
options?: Array<{
|
|
17
|
+
value: unknown;
|
|
18
|
+
label: string;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The columns a bulk edit can target.
|
|
23
|
+
*
|
|
24
|
+
* A column qualifies only if it is field-backed (there is somewhere to write
|
|
25
|
+
* the value) and editable. `isCellEditable` is asked WITHOUT a row, which is
|
|
26
|
+
* the column-level question - per-row rules are re-checked per cell at write
|
|
27
|
+
* time, so a column that is editable for some rows still shows up here and
|
|
28
|
+
* simply skips the rows that refuse it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function bulkEditableFields(ctx: any): BulkEditField[];
|
|
31
|
+
/** Coerce a form control's raw output to what the column should store. */
|
|
32
|
+
export declare function coerceBulkValue(editorType: string, raw: unknown): unknown;
|
|
33
|
+
/**
|
|
34
|
+
* What each field should show when the drawer opens.
|
|
35
|
+
*
|
|
36
|
+
* A field where every selected row already agrees shows that shared value, so
|
|
37
|
+
* the form reads as the current state rather than as empty. A field where they
|
|
38
|
+
* differ shows nothing and is reported as `mixed` - the drawer labels it, and
|
|
39
|
+
* leaving it alone leaves each row's own value intact. This is the answer to
|
|
40
|
+
* "what does an untouched field mean" that makes multi-field bulk edit safe:
|
|
41
|
+
* only fields the user actually changes are written.
|
|
42
|
+
*/
|
|
43
|
+
export declare function bulkEditInitialValues(ctx: any, fields: BulkEditField[]): {
|
|
44
|
+
values: Record<string, unknown>;
|
|
45
|
+
mixed: Set<string>;
|
|
46
|
+
};
|
|
47
|
+
export type BulkEditResult = {
|
|
48
|
+
/** Cells actually changed. Skips rows already holding the value. */
|
|
49
|
+
changed: number;
|
|
50
|
+
/** Cells the grid refused to write, e.g. a per-row `editable` rule. */
|
|
51
|
+
skipped: number;
|
|
52
|
+
/** How many distinct fields were written. */
|
|
53
|
+
fields: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Apply `edits` (columnId -> value) to every selected row.
|
|
57
|
+
*
|
|
58
|
+
* Every write goes through `writeCellRaw`, so `onCellValueChange` fires per
|
|
59
|
+
* cell exactly as it does for a single edit. The undo steps for the WHOLE run -
|
|
60
|
+
* across every field and every row - are appended together, so one bulk edit
|
|
61
|
+
* walks back the way a paste does rather than leaving the user pressing Ctrl+Z
|
|
62
|
+
* once per cell.
|
|
63
|
+
*/
|
|
64
|
+
export declare function applyBulkEdit(ctx: any, edits: Record<string, unknown>): BulkEditResult;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk edit - set one or more fields across every selected row, as a single
|
|
3
|
+
* undoable run. The engine half; `SvGridBulkEditDrawer.svelte` is the UI.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of the component so it can be tested without mounting anything, and
|
|
6
|
+
* so the rules below live in one readable place rather than inside markup.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The columns a bulk edit can target.
|
|
10
|
+
*
|
|
11
|
+
* A column qualifies only if it is field-backed (there is somewhere to write
|
|
12
|
+
* the value) and editable. `isCellEditable` is asked WITHOUT a row, which is
|
|
13
|
+
* the column-level question - per-row rules are re-checked per cell at write
|
|
14
|
+
* time, so a column that is editable for some rows still shows up here and
|
|
15
|
+
* simply skips the rows that refuse it.
|
|
16
|
+
*/
|
|
17
|
+
export function bulkEditableFields(ctx) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const column of ctx.allColumns ?? []) {
|
|
20
|
+
const def = column?.columnDef;
|
|
21
|
+
if (!def?.field)
|
|
22
|
+
continue;
|
|
23
|
+
if (def.editable === false)
|
|
24
|
+
continue;
|
|
25
|
+
if (!ctx.isCellEditable(column))
|
|
26
|
+
continue;
|
|
27
|
+
// A snippet header has no text to put in a form label; the id is at least
|
|
28
|
+
// stable and recognisable.
|
|
29
|
+
const label = typeof def.header === 'string' && def.header ? def.header : column.id;
|
|
30
|
+
const raw = def.editorOptions;
|
|
31
|
+
const options = Array.isArray(raw)
|
|
32
|
+
? raw.map((o) => o && typeof o === 'object' && 'value' in o
|
|
33
|
+
? { value: o.value, label: String(o.label ?? o.value) }
|
|
34
|
+
: { value: o, label: String(o) })
|
|
35
|
+
: undefined;
|
|
36
|
+
out.push({ id: column.id, label, editorType: String(def.editorType ?? 'text'), options });
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/** Coerce a form control's raw output to what the column should store. */
|
|
41
|
+
export function coerceBulkValue(editorType, raw) {
|
|
42
|
+
if (editorType === 'checkbox')
|
|
43
|
+
return Boolean(raw);
|
|
44
|
+
if (editorType === 'number') {
|
|
45
|
+
if (raw === '' || raw == null)
|
|
46
|
+
return null;
|
|
47
|
+
const n = Number(raw);
|
|
48
|
+
return Number.isNaN(n) ? null : n;
|
|
49
|
+
}
|
|
50
|
+
return raw;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What each field should show when the drawer opens.
|
|
54
|
+
*
|
|
55
|
+
* A field where every selected row already agrees shows that shared value, so
|
|
56
|
+
* the form reads as the current state rather than as empty. A field where they
|
|
57
|
+
* differ shows nothing and is reported as `mixed` - the drawer labels it, and
|
|
58
|
+
* leaving it alone leaves each row's own value intact. This is the answer to
|
|
59
|
+
* "what does an untouched field mean" that makes multi-field bulk edit safe:
|
|
60
|
+
* only fields the user actually changes are written.
|
|
61
|
+
*/
|
|
62
|
+
export function bulkEditInitialValues(ctx, fields) {
|
|
63
|
+
const values = {};
|
|
64
|
+
const mixed = new Set();
|
|
65
|
+
const ids = ctx.selectionBarTarget?.ids ?? [];
|
|
66
|
+
const selected = new Set(ids);
|
|
67
|
+
const rowIndexes = [];
|
|
68
|
+
ctx.allRows.forEach((row, i) => {
|
|
69
|
+
if (selected.has(row.id))
|
|
70
|
+
rowIndexes.push(i);
|
|
71
|
+
});
|
|
72
|
+
for (const field of fields) {
|
|
73
|
+
let first;
|
|
74
|
+
let seen = false;
|
|
75
|
+
let differs = false;
|
|
76
|
+
for (const rowIndex of rowIndexes) {
|
|
77
|
+
const v = ctx.readCellRaw(rowIndex, field.id);
|
|
78
|
+
if (!seen) {
|
|
79
|
+
first = v;
|
|
80
|
+
seen = true;
|
|
81
|
+
}
|
|
82
|
+
else if (v !== first) {
|
|
83
|
+
differs = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (differs) {
|
|
88
|
+
mixed.add(field.id);
|
|
89
|
+
values[field.id] = field.editorType === 'checkbox' ? false : '';
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
values[field.id] = first ?? (field.editorType === 'checkbox' ? false : '');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { values, mixed };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Apply `edits` (columnId -> value) to every selected row.
|
|
99
|
+
*
|
|
100
|
+
* Every write goes through `writeCellRaw`, so `onCellValueChange` fires per
|
|
101
|
+
* cell exactly as it does for a single edit. The undo steps for the WHOLE run -
|
|
102
|
+
* across every field and every row - are appended together, so one bulk edit
|
|
103
|
+
* walks back the way a paste does rather than leaving the user pressing Ctrl+Z
|
|
104
|
+
* once per cell.
|
|
105
|
+
*/
|
|
106
|
+
export function applyBulkEdit(ctx, edits) {
|
|
107
|
+
const steps = [];
|
|
108
|
+
let skipped = 0;
|
|
109
|
+
let fieldsWritten = 0;
|
|
110
|
+
const ids = ctx.selectionBarTarget?.ids ?? [];
|
|
111
|
+
const selected = new Set(ids);
|
|
112
|
+
// Resolve indexes in ONE pass rather than searching per row. `writeCellRaw`
|
|
113
|
+
// swaps `internalData` on every call and `allRows` re-derives, so a per-row
|
|
114
|
+
// `findIndex` would walk a freshly rebuilt array for every cell - O(rows x
|
|
115
|
+
// selection) for a result that is already known. It would still be CORRECT,
|
|
116
|
+
// because row ids are stable across the swap; it is just needless work.
|
|
117
|
+
const targets = [];
|
|
118
|
+
ctx.allRows.forEach((row, rowIndex) => {
|
|
119
|
+
if (selected.has(row.id))
|
|
120
|
+
targets.push({ rowIndex, rowId: row.id });
|
|
121
|
+
});
|
|
122
|
+
for (const [columnId, value] of Object.entries(edits)) {
|
|
123
|
+
const column = ctx.allColumns.find((c) => c.id === columnId);
|
|
124
|
+
const field = column?.columnDef?.field;
|
|
125
|
+
if (!field)
|
|
126
|
+
continue;
|
|
127
|
+
fieldsWritten += 1;
|
|
128
|
+
const colIndex = ctx.allColumns.indexOf(column);
|
|
129
|
+
for (const { rowIndex, rowId } of targets) {
|
|
130
|
+
if (!ctx.isCellEditableAt(rowIndex, colIndex)) {
|
|
131
|
+
skipped += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const before = ctx.readCellRaw(rowIndex, columnId);
|
|
135
|
+
if (before === value)
|
|
136
|
+
continue;
|
|
137
|
+
ctx.writeCellRaw(rowIndex, columnId, value);
|
|
138
|
+
steps.push({ rowId, columnId, field, before, after: value });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (steps.length) {
|
|
142
|
+
let hist = ctx.history.slice(0, ctx.historyPtr + 1);
|
|
143
|
+
for (const step of steps)
|
|
144
|
+
hist.push(step);
|
|
145
|
+
if (hist.length > ctx.UNDO_LIMIT)
|
|
146
|
+
hist = hist.slice(hist.length - ctx.UNDO_LIMIT);
|
|
147
|
+
ctx.history = hist;
|
|
148
|
+
ctx.historyPtr = ctx.history.length - 1;
|
|
149
|
+
ctx.historyVersion += 1;
|
|
150
|
+
}
|
|
151
|
+
return { changed: steps.length, skipped, fields: fieldsWritten };
|
|
152
|
+
}
|