@alaarab/ogrid-angular 2.0.23 → 2.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/esm/components/base-column-header-filter.component.js +26 -11
- package/dist/esm/components/base-datagrid-table.component.js +64 -46
- package/dist/esm/components/base-ogrid.component.js +36 -0
- package/dist/esm/components/base-popover-cell-editor.component.js +6 -7
- package/dist/esm/components/empty-state.component.js +2 -2
- package/dist/esm/components/grid-context-menu.component.js +17 -17
- package/dist/esm/components/ogrid-layout.component.js +2 -49
- package/dist/esm/components/sidebar.component.js +2 -6
- package/dist/esm/components/status-bar.component.js +6 -2
- package/dist/esm/index.js +5 -1
- package/dist/esm/services/datagrid-editing.service.js +52 -0
- package/dist/esm/services/datagrid-interaction.service.js +667 -0
- package/dist/esm/services/datagrid-layout.service.js +151 -0
- package/dist/esm/services/datagrid-state.service.js +130 -865
- package/dist/esm/services/ogrid.service.js +61 -26
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/latestRef.js +0 -5
- package/dist/types/components/base-column-header-filter.component.d.ts +2 -0
- package/dist/types/components/base-datagrid-table.component.d.ts +21 -5
- package/dist/types/components/base-ogrid.component.d.ts +10 -0
- package/dist/types/components/grid-context-menu.component.d.ts +5 -5
- package/dist/types/components/sidebar.component.d.ts +0 -2
- package/dist/types/components/status-bar.component.d.ts +4 -1
- package/dist/types/index.d.ts +5 -1
- package/dist/types/services/datagrid-editing.service.d.ts +31 -0
- package/dist/types/services/datagrid-interaction.service.d.ts +86 -0
- package/dist/types/services/datagrid-layout.service.d.ts +36 -0
- package/dist/types/services/datagrid-state.service.d.ts +20 -39
- package/dist/types/services/ogrid.service.d.ts +8 -3
- package/dist/types/types/dataGridTypes.d.ts +1 -1
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/latestRef.d.ts +0 -5
- package/package.json +10 -3
|
@@ -5,7 +5,16 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { Injectable, signal, computed, effect, DestroyRef, inject, NgZone } from '@angular/core';
|
|
8
|
-
import {
|
|
8
|
+
import { getDataGridStatusBarConfig, parseValue, computeAggregations, getCellValue, normalizeSelectionRange, } from '@alaarab/ogrid-core';
|
|
9
|
+
import { DataGridLayoutHelper } from './datagrid-layout.service';
|
|
10
|
+
import { DataGridEditingHelper } from './datagrid-editing.service';
|
|
11
|
+
import { DataGridInteractionHelper } from './datagrid-interaction.service';
|
|
12
|
+
// Stable no-op functions to avoid allocating new closures on every getState() call
|
|
13
|
+
const NOOP = () => { };
|
|
14
|
+
const NOOP_ASYNC = async () => { };
|
|
15
|
+
const NOOP_MOUSE = (_e, _r, _c) => { };
|
|
16
|
+
const NOOP_KEY = (_e) => { };
|
|
17
|
+
const NOOP_CTX = (_e) => { };
|
|
9
18
|
/**
|
|
10
19
|
* Single orchestration service for DataGridTable. Takes grid props,
|
|
11
20
|
* returns all derived state and handlers so Angular UI packages can be thin view layers.
|
|
@@ -19,130 +28,52 @@ let DataGridStateService = class DataGridStateService {
|
|
|
19
28
|
// --- Input signals ---
|
|
20
29
|
this.props = signal(null);
|
|
21
30
|
this.wrapperEl = signal(null);
|
|
22
|
-
// --- Internal state ---
|
|
23
|
-
this.editingCellSig = signal(null);
|
|
24
|
-
this.pendingEditorValueSig = signal(undefined);
|
|
25
|
-
this.activeCellSig = signal(null);
|
|
26
|
-
this.selectionRangeSig = signal(null);
|
|
27
|
-
this.isDraggingSig = signal(false);
|
|
28
|
-
this.contextMenuPositionSig = signal(null);
|
|
31
|
+
// --- Internal state (still owned by main service for backward compat) ---
|
|
29
32
|
this.internalSelectedRows = signal(new Set());
|
|
30
|
-
this.popoverAnchorElSig = signal(null);
|
|
31
|
-
this.containerWidthSig = signal(0);
|
|
32
|
-
this.columnSizingOverridesSig = signal({});
|
|
33
|
-
// Clipboard state
|
|
34
|
-
this.cutRangeSig = signal(null);
|
|
35
|
-
this.copyRangeSig = signal(null);
|
|
36
|
-
this.internalClipboard = null;
|
|
37
|
-
// Undo/redo state (backed by core UndoRedoStack)
|
|
38
|
-
this.undoRedoStack = new UndoRedoStack(100);
|
|
39
|
-
this.undoLengthSig = signal(0);
|
|
40
|
-
this.redoLengthSig = signal(0);
|
|
41
|
-
// Fill handle state
|
|
42
|
-
this.fillDragStart = null;
|
|
43
|
-
this.fillRafId = 0;
|
|
44
|
-
this.fillMoveHandler = null;
|
|
45
|
-
this.fillUpHandler = null;
|
|
46
33
|
// Row selection
|
|
47
34
|
this.lastClickedRow = -1;
|
|
48
|
-
// Drag selection refs
|
|
49
|
-
this.dragStartPos = null;
|
|
50
|
-
this.dragMoved = false;
|
|
51
|
-
this.isDraggingRef = false;
|
|
52
|
-
this.liveDragRange = null;
|
|
53
|
-
this.rafId = 0;
|
|
54
|
-
this.lastMousePos = null;
|
|
55
|
-
this.autoScrollInterval = null;
|
|
56
|
-
// ResizeObserver
|
|
57
|
-
this.resizeObserver = null;
|
|
58
35
|
// Header menu state (for column pinning UI)
|
|
59
36
|
this.headerMenuIsOpenSig = signal(false);
|
|
60
37
|
this.headerMenuOpenForColumnSig = signal(null);
|
|
61
38
|
this.headerMenuAnchorElementSig = signal(null);
|
|
62
39
|
// --- Derived computed ---
|
|
63
|
-
this.propsResolved = computed(() =>
|
|
40
|
+
this.propsResolved = computed(() => {
|
|
41
|
+
const p = this.props();
|
|
42
|
+
if (!p)
|
|
43
|
+
throw new Error('DataGridStateService: props must be set before use');
|
|
44
|
+
return p;
|
|
45
|
+
});
|
|
64
46
|
this.cellSelection = computed(() => {
|
|
65
47
|
const p = this.props();
|
|
66
48
|
return p ? p.cellSelection !== false : true;
|
|
67
49
|
});
|
|
68
50
|
// Narrow signal extractors — prevent full props() dependency in effects/computed
|
|
69
51
|
this.originalOnCellValueChanged = computed(() => this.props()?.onCellValueChanged);
|
|
70
|
-
this.initialColumnWidthsSig = computed(() => this.props()?.initialColumnWidths);
|
|
71
52
|
// Undo/redo wrapped callback — only recomputes when the actual callback reference changes
|
|
72
53
|
this.wrappedOnCellValueChanged = computed(() => {
|
|
73
54
|
const original = this.originalOnCellValueChanged();
|
|
74
55
|
if (!original)
|
|
75
56
|
return undefined;
|
|
76
57
|
return (event) => {
|
|
77
|
-
this.undoRedoStack.record(event);
|
|
78
|
-
if (!this.undoRedoStack.isBatching) {
|
|
79
|
-
this.undoLengthSig.set(this.undoRedoStack.historyLength);
|
|
80
|
-
this.redoLengthSig.set(this.undoRedoStack.redoLength);
|
|
58
|
+
this.interactionHelper.undoRedoStack.record(event);
|
|
59
|
+
if (!this.interactionHelper.undoRedoStack.isBatching) {
|
|
60
|
+
this.interactionHelper.undoLengthSig.set(this.interactionHelper.undoRedoStack.historyLength);
|
|
61
|
+
this.interactionHelper.redoLengthSig.set(this.interactionHelper.undoRedoStack.redoLength);
|
|
81
62
|
}
|
|
82
63
|
original(event);
|
|
83
64
|
};
|
|
84
65
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return raw;
|
|
97
|
-
return raw.map((col) => {
|
|
98
|
-
const override = pinnedColumns[col.columnId];
|
|
99
|
-
if (override && col.pinned !== override)
|
|
100
|
-
return { ...col, pinned: override };
|
|
101
|
-
return col;
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
this.visibleCols = computed(() => {
|
|
105
|
-
const p = this.props();
|
|
106
|
-
if (!p)
|
|
107
|
-
return [];
|
|
108
|
-
const flatCols = this.flatColumns();
|
|
109
|
-
const filtered = p.visibleColumns
|
|
110
|
-
? flatCols.filter((c) => p.visibleColumns.has(c.columnId))
|
|
111
|
-
: flatCols;
|
|
112
|
-
const order = p.columnOrder;
|
|
113
|
-
if (!order?.length)
|
|
114
|
-
return filtered;
|
|
115
|
-
// Build index map for O(1) lookup instead of repeated O(n) indexOf
|
|
116
|
-
const orderMap = new Map();
|
|
117
|
-
for (let i = 0; i < order.length; i++) {
|
|
118
|
-
orderMap.set(order[i], i);
|
|
119
|
-
}
|
|
120
|
-
return [...filtered].sort((a, b) => {
|
|
121
|
-
const ia = orderMap.get(a.columnId) ?? -1;
|
|
122
|
-
const ib = orderMap.get(b.columnId) ?? -1;
|
|
123
|
-
if (ia === -1 && ib === -1)
|
|
124
|
-
return 0;
|
|
125
|
-
if (ia === -1)
|
|
126
|
-
return 1;
|
|
127
|
-
if (ib === -1)
|
|
128
|
-
return -1;
|
|
129
|
-
return ia - ib;
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
this.visibleColumnCount = computed(() => this.visibleCols().length);
|
|
133
|
-
this.hasCheckboxCol = computed(() => (this.props()?.rowSelection ?? 'none') === 'multiple');
|
|
134
|
-
this.hasRowNumbersCol = computed(() => !!this.props()?.showRowNumbers);
|
|
135
|
-
this.specialColsCount = computed(() => (this.hasCheckboxCol() ? 1 : 0) + (this.hasRowNumbersCol() ? 1 : 0));
|
|
136
|
-
this.totalColCount = computed(() => this.visibleColumnCount() + this.specialColsCount());
|
|
137
|
-
this.colOffset = computed(() => this.specialColsCount());
|
|
138
|
-
this.rowIndexByRowId = computed(() => {
|
|
139
|
-
const p = this.props();
|
|
140
|
-
if (!p)
|
|
141
|
-
return new Map();
|
|
142
|
-
const m = new Map();
|
|
143
|
-
p.items.forEach((item, idx) => m.set(p.getRowId(item), idx));
|
|
144
|
-
return m;
|
|
145
|
-
});
|
|
66
|
+
// --- Delegated computed signals from layoutHelper ---
|
|
67
|
+
this.flatColumnsRaw = computed(() => this.layoutHelper.flatColumnsRaw());
|
|
68
|
+
this.flatColumns = computed(() => this.layoutHelper.flatColumns());
|
|
69
|
+
this.visibleCols = computed(() => this.layoutHelper.visibleCols());
|
|
70
|
+
this.visibleColumnCount = computed(() => this.layoutHelper.visibleColumnCount());
|
|
71
|
+
this.hasCheckboxCol = computed(() => this.layoutHelper.hasCheckboxCol());
|
|
72
|
+
this.hasRowNumbersCol = computed(() => this.layoutHelper.hasRowNumbersCol());
|
|
73
|
+
this.specialColsCount = computed(() => this.layoutHelper.specialColsCount());
|
|
74
|
+
this.totalColCount = computed(() => this.layoutHelper.totalColCount());
|
|
75
|
+
this.colOffset = computed(() => this.layoutHelper.colOffset());
|
|
76
|
+
this.rowIndexByRowId = computed(() => this.layoutHelper.rowIndexByRowId());
|
|
146
77
|
this.selectedRowIds = computed(() => {
|
|
147
78
|
const p = this.props();
|
|
148
79
|
if (!p)
|
|
@@ -167,30 +98,17 @@ let DataGridStateService = class DataGridStateService {
|
|
|
167
98
|
const selected = this.selectedRowIds();
|
|
168
99
|
return !this.allSelected() && p.items.some((item) => selected.has(p.getRowId(item)));
|
|
169
100
|
});
|
|
170
|
-
this.hasCellSelection = computed(() => this.
|
|
171
|
-
this.canUndo = computed(() => this.
|
|
172
|
-
this.canRedo = computed(() => this.
|
|
173
|
-
// Table layout
|
|
174
|
-
this.minTableWidth = computed(() =>
|
|
175
|
-
|
|
176
|
-
return this.visibleCols().reduce((sum, c) => sum + (c.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH) + CELL_PADDING, checkboxW);
|
|
177
|
-
});
|
|
178
|
-
this.desiredTableWidth = computed(() => {
|
|
179
|
-
const checkboxW = this.hasCheckboxCol() ? CHECKBOX_COLUMN_WIDTH : 0;
|
|
180
|
-
const overrides = this.columnSizingOverridesSig();
|
|
181
|
-
return this.visibleCols().reduce((sum, c) => {
|
|
182
|
-
const override = overrides[c.columnId];
|
|
183
|
-
const w = override
|
|
184
|
-
? override.widthPx
|
|
185
|
-
: (c.idealWidth ?? c.defaultWidth ?? c.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH);
|
|
186
|
-
return sum + Math.max(c.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH, w) + CELL_PADDING;
|
|
187
|
-
}, checkboxW);
|
|
188
|
-
});
|
|
101
|
+
this.hasCellSelection = computed(() => this.interactionHelper.hasCellSelection());
|
|
102
|
+
this.canUndo = computed(() => this.interactionHelper.canUndo());
|
|
103
|
+
this.canRedo = computed(() => this.interactionHelper.canRedo());
|
|
104
|
+
// Table layout (delegated to layoutHelper)
|
|
105
|
+
this.minTableWidth = computed(() => this.layoutHelper.minTableWidth());
|
|
106
|
+
this.desiredTableWidth = computed(() => this.layoutHelper.desiredTableWidth());
|
|
189
107
|
this.aggregation = computed(() => {
|
|
190
108
|
const p = this.props();
|
|
191
109
|
if (!p)
|
|
192
110
|
return null;
|
|
193
|
-
return computeAggregations(p.items, this.visibleCols(), this.cellSelection() ? this.selectionRangeSig() : null);
|
|
111
|
+
return computeAggregations(p.items, this.visibleCols(), this.cellSelection() ? this.interactionHelper.selectionRangeSig() : null);
|
|
194
112
|
});
|
|
195
113
|
this.statusBarConfig = computed(() => {
|
|
196
114
|
const p = this.props();
|
|
@@ -207,6 +125,10 @@ let DataGridStateService = class DataGridStateService {
|
|
|
207
125
|
return false;
|
|
208
126
|
return p.items.length === 0 && !!p.emptyState && !p.isLoading;
|
|
209
127
|
});
|
|
128
|
+
// --- Instantiate sub-helpers ---
|
|
129
|
+
this.layoutHelper = new DataGridLayoutHelper(this.props, this.wrapperEl, this.ngZone);
|
|
130
|
+
this.interactionHelper = new DataGridInteractionHelper();
|
|
131
|
+
this.editingHelper = new DataGridEditingHelper(() => this.visibleCols(), () => this.props()?.items ?? [], () => this.wrappedOnCellValueChanged(), (cell) => this.setActiveCell(cell));
|
|
210
132
|
// Setup window event listeners for cell selection drag
|
|
211
133
|
// Run outside NgZone to avoid 60Hz change detection during drag
|
|
212
134
|
effect((onCleanup) => {
|
|
@@ -221,85 +143,10 @@ let DataGridStateService = class DataGridStateService {
|
|
|
221
143
|
window.removeEventListener('mouseup', onUp, true);
|
|
222
144
|
});
|
|
223
145
|
});
|
|
224
|
-
// Initialize column sizing overrides from initial widths
|
|
225
|
-
// Only track initialColumnWidths, not all props
|
|
226
|
-
effect(() => {
|
|
227
|
-
const widths = this.initialColumnWidthsSig();
|
|
228
|
-
if (widths) {
|
|
229
|
-
const result = {};
|
|
230
|
-
for (const [id, width] of Object.entries(widths)) {
|
|
231
|
-
result[id] = { widthPx: width };
|
|
232
|
-
}
|
|
233
|
-
this.columnSizingOverridesSig.set(result);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
// Container width measurement via ResizeObserver
|
|
237
|
-
// Run outside NgZone — signal.set() inside still triggers Angular reactivity
|
|
238
|
-
effect(() => {
|
|
239
|
-
const el = this.wrapperEl();
|
|
240
|
-
if (this.resizeObserver) {
|
|
241
|
-
this.resizeObserver.disconnect();
|
|
242
|
-
this.resizeObserver = null;
|
|
243
|
-
}
|
|
244
|
-
if (!el)
|
|
245
|
-
return;
|
|
246
|
-
const measure = () => {
|
|
247
|
-
const rect = el.getBoundingClientRect();
|
|
248
|
-
const cs = window.getComputedStyle(el);
|
|
249
|
-
const borderX = (parseFloat(cs.borderLeftWidth || '0') || 0) +
|
|
250
|
-
(parseFloat(cs.borderRightWidth || '0') || 0);
|
|
251
|
-
this.containerWidthSig.set(Math.max(0, rect.width - borderX));
|
|
252
|
-
};
|
|
253
|
-
this.ngZone.runOutsideAngular(() => {
|
|
254
|
-
this.resizeObserver = new ResizeObserver(measure);
|
|
255
|
-
this.resizeObserver.observe(el);
|
|
256
|
-
});
|
|
257
|
-
measure();
|
|
258
|
-
});
|
|
259
146
|
// Cleanup on destroy — cancel pending work and release references
|
|
260
147
|
this.destroyRef.onDestroy(() => {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
this.rafId = 0;
|
|
264
|
-
}
|
|
265
|
-
if (this.fillRafId) {
|
|
266
|
-
cancelAnimationFrame(this.fillRafId);
|
|
267
|
-
this.fillRafId = 0;
|
|
268
|
-
}
|
|
269
|
-
if (this.autoScrollInterval) {
|
|
270
|
-
clearInterval(this.autoScrollInterval);
|
|
271
|
-
this.autoScrollInterval = null;
|
|
272
|
-
}
|
|
273
|
-
if (this.resizeObserver) {
|
|
274
|
-
this.resizeObserver.disconnect();
|
|
275
|
-
this.resizeObserver = null;
|
|
276
|
-
}
|
|
277
|
-
// Remove fill-handle window listeners if active
|
|
278
|
-
if (this.fillMoveHandler) {
|
|
279
|
-
window.removeEventListener('mousemove', this.fillMoveHandler, true);
|
|
280
|
-
this.fillMoveHandler = null;
|
|
281
|
-
}
|
|
282
|
-
if (this.fillUpHandler) {
|
|
283
|
-
window.removeEventListener('mouseup', this.fillUpHandler, true);
|
|
284
|
-
this.fillUpHandler = null;
|
|
285
|
-
}
|
|
286
|
-
// Clear undo/redo stack to release closure references
|
|
287
|
-
this.undoRedoStack.clear();
|
|
288
|
-
});
|
|
289
|
-
// Clean up column sizing overrides for removed columns
|
|
290
|
-
effect(() => {
|
|
291
|
-
const colIds = new Set(this.flatColumns().map((c) => c.columnId));
|
|
292
|
-
this.columnSizingOverridesSig.update((prev) => {
|
|
293
|
-
const next = { ...prev };
|
|
294
|
-
let changed = false;
|
|
295
|
-
for (const id of Object.keys(next)) {
|
|
296
|
-
if (!colIds.has(id)) {
|
|
297
|
-
delete next[id];
|
|
298
|
-
changed = true;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return changed ? next : prev;
|
|
302
|
-
});
|
|
148
|
+
this.interactionHelper.destroy();
|
|
149
|
+
this.layoutHelper.destroy();
|
|
303
150
|
});
|
|
304
151
|
}
|
|
305
152
|
// --- Row selection methods ---
|
|
@@ -359,556 +206,92 @@ let DataGridStateService = class DataGridStateService {
|
|
|
359
206
|
this.updateSelection(new Set());
|
|
360
207
|
}
|
|
361
208
|
}
|
|
362
|
-
// --- Cell editing ---
|
|
209
|
+
// --- Cell editing (delegated to editingHelper) ---
|
|
363
210
|
setEditingCell(cell) {
|
|
364
|
-
this.
|
|
211
|
+
this.editingHelper.setEditingCell(cell);
|
|
365
212
|
}
|
|
366
213
|
setPendingEditorValue(value) {
|
|
367
|
-
this.
|
|
214
|
+
this.editingHelper.setPendingEditorValue(value);
|
|
368
215
|
}
|
|
369
216
|
setActiveCell(cell) {
|
|
370
|
-
|
|
371
|
-
if (prev === cell)
|
|
372
|
-
return;
|
|
373
|
-
if (prev && cell && prev.rowIndex === cell.rowIndex && prev.columnIndex === cell.columnIndex)
|
|
374
|
-
return;
|
|
375
|
-
this.activeCellSig.set(cell);
|
|
217
|
+
this.interactionHelper.setActiveCell(cell);
|
|
376
218
|
}
|
|
377
219
|
setSelectionRange(range) {
|
|
378
|
-
|
|
379
|
-
if (rangesEqual(prev, range))
|
|
380
|
-
return;
|
|
381
|
-
this.selectionRangeSig.set(range);
|
|
220
|
+
this.interactionHelper.setSelectionRange(range);
|
|
382
221
|
}
|
|
383
222
|
commitCellEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex) {
|
|
384
|
-
|
|
385
|
-
if (col) {
|
|
386
|
-
const result = parseValue(newValue, oldValue, item, col);
|
|
387
|
-
if (!result.valid) {
|
|
388
|
-
this.editingCellSig.set(null);
|
|
389
|
-
this.popoverAnchorElSig.set(null);
|
|
390
|
-
this.pendingEditorValueSig.set(undefined);
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
newValue = result.value;
|
|
394
|
-
}
|
|
395
|
-
const onCellValueChanged = this.wrappedOnCellValueChanged();
|
|
396
|
-
onCellValueChanged?.({ item, columnId, oldValue, newValue, rowIndex });
|
|
397
|
-
this.editingCellSig.set(null);
|
|
398
|
-
this.popoverAnchorElSig.set(null);
|
|
399
|
-
this.pendingEditorValueSig.set(undefined);
|
|
400
|
-
const p = this.props();
|
|
401
|
-
if (p && rowIndex < p.items.length - 1) {
|
|
402
|
-
this.setActiveCell({ rowIndex: rowIndex + 1, columnIndex: globalColIndex });
|
|
403
|
-
}
|
|
223
|
+
this.editingHelper.commitCellEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex);
|
|
404
224
|
}
|
|
405
225
|
cancelPopoverEdit() {
|
|
406
|
-
this.
|
|
407
|
-
this.popoverAnchorElSig.set(null);
|
|
408
|
-
this.pendingEditorValueSig.set(undefined);
|
|
226
|
+
this.editingHelper.cancelPopoverEdit();
|
|
409
227
|
}
|
|
410
|
-
// --- Cell selection / mouse handling ---
|
|
228
|
+
// --- Cell selection / mouse handling (delegated to interactionHelper) ---
|
|
411
229
|
handleCellMouseDown(e, rowIndex, globalColIndex) {
|
|
412
|
-
|
|
413
|
-
return;
|
|
414
|
-
const wrapper = this.wrapperEl();
|
|
415
|
-
wrapper?.focus({ preventScroll: true });
|
|
416
|
-
this.clearClipboardRanges();
|
|
417
|
-
const colOffset = this.colOffset();
|
|
418
|
-
if (globalColIndex < colOffset)
|
|
419
|
-
return;
|
|
420
|
-
e.preventDefault();
|
|
421
|
-
const dataColIndex = globalColIndex - colOffset;
|
|
422
|
-
const currentRange = this.selectionRangeSig();
|
|
423
|
-
if (e.shiftKey && currentRange != null) {
|
|
424
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
425
|
-
startRow: currentRange.startRow,
|
|
426
|
-
startCol: currentRange.startCol,
|
|
427
|
-
endRow: rowIndex,
|
|
428
|
-
endCol: dataColIndex,
|
|
429
|
-
}));
|
|
430
|
-
this.setActiveCell({ rowIndex, columnIndex: globalColIndex });
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
this.dragStartPos = { row: rowIndex, col: dataColIndex };
|
|
434
|
-
this.dragMoved = false;
|
|
435
|
-
const initial = {
|
|
436
|
-
startRow: rowIndex, startCol: dataColIndex,
|
|
437
|
-
endRow: rowIndex, endCol: dataColIndex,
|
|
438
|
-
};
|
|
439
|
-
this.setSelectionRange(initial);
|
|
440
|
-
this.liveDragRange = initial;
|
|
441
|
-
this.setActiveCell({ rowIndex, columnIndex: globalColIndex });
|
|
442
|
-
this.isDraggingRef = true;
|
|
443
|
-
}
|
|
230
|
+
this.interactionHelper.handleCellMouseDown(e, rowIndex, globalColIndex, this.colOffset(), this.wrapperEl());
|
|
444
231
|
}
|
|
445
232
|
handleSelectAllCells() {
|
|
446
233
|
const p = this.props();
|
|
447
234
|
if (!p)
|
|
448
235
|
return;
|
|
449
|
-
|
|
450
|
-
const visibleColCount = this.visibleColumnCount();
|
|
451
|
-
if (rowCount === 0 || visibleColCount === 0)
|
|
452
|
-
return;
|
|
453
|
-
this.setSelectionRange({
|
|
454
|
-
startRow: 0, startCol: 0,
|
|
455
|
-
endRow: rowCount - 1, endCol: visibleColCount - 1,
|
|
456
|
-
});
|
|
457
|
-
this.setActiveCell({ rowIndex: 0, columnIndex: this.colOffset() });
|
|
236
|
+
this.interactionHelper.handleSelectAllCells(p.items.length, this.visibleColumnCount(), this.colOffset());
|
|
458
237
|
}
|
|
459
|
-
// --- Context menu ---
|
|
238
|
+
// --- Context menu (delegated to interactionHelper) ---
|
|
460
239
|
setContextMenuPosition(pos) {
|
|
461
|
-
this.
|
|
240
|
+
this.interactionHelper.setContextMenuPosition(pos);
|
|
462
241
|
}
|
|
463
242
|
handleCellContextMenu(e) {
|
|
464
|
-
|
|
465
|
-
this.contextMenuPositionSig.set({ x: e.clientX, y: e.clientY });
|
|
243
|
+
this.interactionHelper.handleCellContextMenu(e);
|
|
466
244
|
}
|
|
467
245
|
closeContextMenu() {
|
|
468
|
-
this.
|
|
246
|
+
this.interactionHelper.closeContextMenu();
|
|
469
247
|
}
|
|
470
|
-
// --- Clipboard ---
|
|
248
|
+
// --- Clipboard (delegated to interactionHelper) ---
|
|
471
249
|
handleCopy() {
|
|
472
250
|
const p = this.props();
|
|
473
251
|
if (!p)
|
|
474
252
|
return;
|
|
475
|
-
|
|
476
|
-
if (range == null)
|
|
477
|
-
return;
|
|
478
|
-
const norm = normalizeSelectionRange(range);
|
|
479
|
-
const tsv = formatSelectionAsTsv(p.items, this.visibleCols(), norm);
|
|
480
|
-
this.internalClipboard = tsv;
|
|
481
|
-
this.copyRangeSig.set(norm);
|
|
482
|
-
void navigator.clipboard.writeText(tsv).catch(() => { });
|
|
253
|
+
this.interactionHelper.handleCopy(p.items, this.visibleCols(), this.colOffset());
|
|
483
254
|
}
|
|
484
255
|
handleCut() {
|
|
485
256
|
const p = this.props();
|
|
486
|
-
if (!p
|
|
487
|
-
return;
|
|
488
|
-
const range = this.getEffectiveRange();
|
|
489
|
-
if (range == null || !this.wrappedOnCellValueChanged())
|
|
257
|
+
if (!p)
|
|
490
258
|
return;
|
|
491
|
-
|
|
492
|
-
this.cutRangeSig.set(norm);
|
|
493
|
-
this.copyRangeSig.set(null);
|
|
494
|
-
this.handleCopy();
|
|
495
|
-
this.copyRangeSig.set(null);
|
|
259
|
+
this.interactionHelper.handleCut(p.items, this.visibleCols(), this.colOffset(), p.editable, this.wrappedOnCellValueChanged());
|
|
496
260
|
}
|
|
497
261
|
async handlePaste() {
|
|
498
262
|
const p = this.props();
|
|
499
|
-
if (!p
|
|
500
|
-
return;
|
|
501
|
-
const onCellValueChanged = this.wrappedOnCellValueChanged();
|
|
502
|
-
if (!onCellValueChanged)
|
|
503
|
-
return;
|
|
504
|
-
let text;
|
|
505
|
-
try {
|
|
506
|
-
text = await navigator.clipboard.readText();
|
|
507
|
-
}
|
|
508
|
-
catch {
|
|
509
|
-
text = '';
|
|
510
|
-
}
|
|
511
|
-
if (!text.trim() && this.internalClipboard != null) {
|
|
512
|
-
text = this.internalClipboard;
|
|
513
|
-
}
|
|
514
|
-
if (!text.trim())
|
|
263
|
+
if (!p)
|
|
515
264
|
return;
|
|
516
|
-
|
|
517
|
-
const anchorRow = norm ? norm.startRow : 0;
|
|
518
|
-
const anchorCol = norm ? norm.startCol : 0;
|
|
519
|
-
const visibleCols = this.visibleCols();
|
|
520
|
-
const parsedRows = parseTsvClipboard(text);
|
|
521
|
-
this.beginBatch();
|
|
522
|
-
for (let r = 0; r < parsedRows.length; r++) {
|
|
523
|
-
const cells = parsedRows[r];
|
|
524
|
-
for (let c = 0; c < cells.length; c++) {
|
|
525
|
-
const targetRow = anchorRow + r;
|
|
526
|
-
const targetCol = anchorCol + c;
|
|
527
|
-
if (targetRow >= p.items.length || targetCol >= visibleCols.length)
|
|
528
|
-
continue;
|
|
529
|
-
const item = p.items[targetRow];
|
|
530
|
-
const col = visibleCols[targetCol];
|
|
531
|
-
const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
|
|
532
|
-
if (!colEditable)
|
|
533
|
-
continue;
|
|
534
|
-
const rawValue = cells[c] ?? '';
|
|
535
|
-
const oldValue = getCellValue(item, col);
|
|
536
|
-
const result = parseValue(rawValue, oldValue, item, col);
|
|
537
|
-
if (!result.valid)
|
|
538
|
-
continue;
|
|
539
|
-
onCellValueChanged({ item, columnId: col.columnId, oldValue, newValue: result.value, rowIndex: targetRow });
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
const cutRange = this.cutRangeSig();
|
|
543
|
-
if (cutRange) {
|
|
544
|
-
for (let r = cutRange.startRow; r <= cutRange.endRow; r++) {
|
|
545
|
-
for (let c = cutRange.startCol; c <= cutRange.endCol; c++) {
|
|
546
|
-
if (r >= p.items.length || c >= visibleCols.length)
|
|
547
|
-
continue;
|
|
548
|
-
const item = p.items[r];
|
|
549
|
-
const col = visibleCols[c];
|
|
550
|
-
const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
|
|
551
|
-
if (!colEditable)
|
|
552
|
-
continue;
|
|
553
|
-
const oldValue = getCellValue(item, col);
|
|
554
|
-
const result = parseValue('', oldValue, item, col);
|
|
555
|
-
if (!result.valid)
|
|
556
|
-
continue;
|
|
557
|
-
onCellValueChanged({ item, columnId: col.columnId, oldValue, newValue: result.value, rowIndex: r });
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
this.cutRangeSig.set(null);
|
|
561
|
-
}
|
|
562
|
-
this.endBatch();
|
|
563
|
-
this.copyRangeSig.set(null);
|
|
265
|
+
await this.interactionHelper.handlePaste(p.items, this.visibleCols(), this.colOffset(), p.editable, this.wrappedOnCellValueChanged());
|
|
564
266
|
}
|
|
565
267
|
clearClipboardRanges() {
|
|
566
|
-
this.
|
|
567
|
-
this.cutRangeSig.set(null);
|
|
268
|
+
this.interactionHelper.clearClipboardRanges();
|
|
568
269
|
}
|
|
569
|
-
// --- Undo/Redo ---
|
|
270
|
+
// --- Undo/Redo (delegated to interactionHelper) ---
|
|
570
271
|
beginBatch() {
|
|
571
|
-
this.
|
|
272
|
+
this.interactionHelper.beginBatch();
|
|
572
273
|
}
|
|
573
274
|
endBatch() {
|
|
574
|
-
this.
|
|
575
|
-
this.undoLengthSig.set(this.undoRedoStack.historyLength);
|
|
576
|
-
this.redoLengthSig.set(this.undoRedoStack.redoLength);
|
|
275
|
+
this.interactionHelper.endBatch();
|
|
577
276
|
}
|
|
578
277
|
undo() {
|
|
579
278
|
const p = this.props();
|
|
580
|
-
|
|
581
|
-
if (!original)
|
|
582
|
-
return;
|
|
583
|
-
const lastBatch = this.undoRedoStack.undo();
|
|
584
|
-
if (!lastBatch)
|
|
585
|
-
return;
|
|
586
|
-
this.undoLengthSig.set(this.undoRedoStack.historyLength);
|
|
587
|
-
this.redoLengthSig.set(this.undoRedoStack.redoLength);
|
|
588
|
-
for (let i = lastBatch.length - 1; i >= 0; i--) {
|
|
589
|
-
const ev = lastBatch[i];
|
|
590
|
-
original({ ...ev, oldValue: ev.newValue, newValue: ev.oldValue });
|
|
591
|
-
}
|
|
279
|
+
this.interactionHelper.undo(p?.onCellValueChanged);
|
|
592
280
|
}
|
|
593
281
|
redo() {
|
|
594
282
|
const p = this.props();
|
|
595
|
-
|
|
596
|
-
if (!original)
|
|
597
|
-
return;
|
|
598
|
-
const nextBatch = this.undoRedoStack.redo();
|
|
599
|
-
if (!nextBatch)
|
|
600
|
-
return;
|
|
601
|
-
this.undoLengthSig.set(this.undoRedoStack.historyLength);
|
|
602
|
-
this.redoLengthSig.set(this.undoRedoStack.redoLength);
|
|
603
|
-
for (const ev of nextBatch) {
|
|
604
|
-
original(ev);
|
|
605
|
-
}
|
|
283
|
+
this.interactionHelper.redo(p?.onCellValueChanged);
|
|
606
284
|
}
|
|
607
|
-
// --- Keyboard navigation ---
|
|
285
|
+
// --- Keyboard navigation (delegated to interactionHelper) ---
|
|
608
286
|
handleGridKeyDown(e) {
|
|
609
287
|
const p = this.props();
|
|
610
288
|
if (!p)
|
|
611
289
|
return;
|
|
612
|
-
|
|
613
|
-
const visibleCols = this.visibleCols();
|
|
614
|
-
const colOffset = this.colOffset();
|
|
615
|
-
const hasCheckboxCol = this.hasCheckboxCol();
|
|
616
|
-
const visibleColumnCount = this.visibleColumnCount();
|
|
617
|
-
const activeCell = this.activeCellSig();
|
|
618
|
-
const selectionRange = this.selectionRangeSig();
|
|
619
|
-
const editingCell = this.editingCellSig();
|
|
620
|
-
const selectedRowIds = this.selectedRowIds();
|
|
621
|
-
const editable = p.editable;
|
|
622
|
-
const onCellValueChanged = this.wrappedOnCellValueChanged();
|
|
623
|
-
const rowSelection = p.rowSelection ?? 'none';
|
|
624
|
-
const maxRowIndex = items.length - 1;
|
|
625
|
-
const maxColIndex = visibleColumnCount - 1 + colOffset;
|
|
626
|
-
if (items.length === 0)
|
|
627
|
-
return;
|
|
628
|
-
if (activeCell === null) {
|
|
629
|
-
if (['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Tab', 'Enter', 'Home', 'End'].includes(e.key)) {
|
|
630
|
-
this.setActiveCell({ rowIndex: 0, columnIndex: colOffset });
|
|
631
|
-
e.preventDefault();
|
|
632
|
-
}
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
const { rowIndex, columnIndex } = activeCell;
|
|
636
|
-
const dataColIndex = columnIndex - colOffset;
|
|
637
|
-
const shift = e.shiftKey;
|
|
638
|
-
const ctrl = e.ctrlKey || e.metaKey;
|
|
639
|
-
const isEmptyAt = (r, c) => {
|
|
640
|
-
if (r < 0 || r >= items.length || c < 0 || c >= visibleCols.length)
|
|
641
|
-
return true;
|
|
642
|
-
const v = getCellValue(items[r], visibleCols[c]);
|
|
643
|
-
return v == null || v === '';
|
|
644
|
-
};
|
|
645
|
-
const findCtrlTarget = findCtrlArrowTarget;
|
|
646
|
-
switch (e.key) {
|
|
647
|
-
case 'c':
|
|
648
|
-
if (ctrl) {
|
|
649
|
-
if (editingCell != null)
|
|
650
|
-
break;
|
|
651
|
-
e.preventDefault();
|
|
652
|
-
this.handleCopy();
|
|
653
|
-
}
|
|
654
|
-
break;
|
|
655
|
-
case 'x':
|
|
656
|
-
if (ctrl) {
|
|
657
|
-
if (editingCell != null)
|
|
658
|
-
break;
|
|
659
|
-
e.preventDefault();
|
|
660
|
-
this.handleCut();
|
|
661
|
-
}
|
|
662
|
-
break;
|
|
663
|
-
case 'v':
|
|
664
|
-
if (ctrl) {
|
|
665
|
-
if (editingCell != null)
|
|
666
|
-
break;
|
|
667
|
-
e.preventDefault();
|
|
668
|
-
void this.handlePaste();
|
|
669
|
-
}
|
|
670
|
-
break;
|
|
671
|
-
case 'ArrowDown': {
|
|
672
|
-
e.preventDefault();
|
|
673
|
-
const newRow = ctrl
|
|
674
|
-
? findCtrlTarget(rowIndex, maxRowIndex, 1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
|
|
675
|
-
: Math.min(rowIndex + 1, maxRowIndex);
|
|
676
|
-
if (shift) {
|
|
677
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
678
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
679
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
680
|
-
endRow: newRow,
|
|
681
|
-
endCol: selectionRange?.endCol ?? dataColIndex,
|
|
682
|
-
}));
|
|
683
|
-
}
|
|
684
|
-
else {
|
|
685
|
-
this.setSelectionRange({ startRow: newRow, startCol: dataColIndex, endRow: newRow, endCol: dataColIndex });
|
|
686
|
-
}
|
|
687
|
-
this.setActiveCell({ rowIndex: newRow, columnIndex });
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
case 'ArrowUp': {
|
|
691
|
-
e.preventDefault();
|
|
692
|
-
const newRowUp = ctrl
|
|
693
|
-
? findCtrlTarget(rowIndex, 0, -1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
|
|
694
|
-
: Math.max(rowIndex - 1, 0);
|
|
695
|
-
if (shift) {
|
|
696
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
697
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
698
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
699
|
-
endRow: newRowUp,
|
|
700
|
-
endCol: selectionRange?.endCol ?? dataColIndex,
|
|
701
|
-
}));
|
|
702
|
-
}
|
|
703
|
-
else {
|
|
704
|
-
this.setSelectionRange({ startRow: newRowUp, startCol: dataColIndex, endRow: newRowUp, endCol: dataColIndex });
|
|
705
|
-
}
|
|
706
|
-
this.setActiveCell({ rowIndex: newRowUp, columnIndex });
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
case 'ArrowRight': {
|
|
710
|
-
e.preventDefault();
|
|
711
|
-
let newCol;
|
|
712
|
-
if (ctrl && dataColIndex >= 0) {
|
|
713
|
-
newCol = findCtrlTarget(dataColIndex, visibleCols.length - 1, 1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
newCol = Math.min(columnIndex + 1, maxColIndex);
|
|
717
|
-
}
|
|
718
|
-
const newDataCol = newCol - colOffset;
|
|
719
|
-
if (shift) {
|
|
720
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
721
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
722
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
723
|
-
endRow: selectionRange?.endRow ?? rowIndex,
|
|
724
|
-
endCol: newDataCol,
|
|
725
|
-
}));
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
|
-
this.setSelectionRange({ startRow: rowIndex, startCol: newDataCol, endRow: rowIndex, endCol: newDataCol });
|
|
729
|
-
}
|
|
730
|
-
this.setActiveCell({ rowIndex, columnIndex: newCol });
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
case 'ArrowLeft': {
|
|
734
|
-
e.preventDefault();
|
|
735
|
-
let newColLeft;
|
|
736
|
-
if (ctrl && dataColIndex >= 0) {
|
|
737
|
-
newColLeft = findCtrlTarget(dataColIndex, 0, -1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
|
|
738
|
-
}
|
|
739
|
-
else {
|
|
740
|
-
newColLeft = Math.max(columnIndex - 1, colOffset);
|
|
741
|
-
}
|
|
742
|
-
const newDataColLeft = newColLeft - colOffset;
|
|
743
|
-
if (shift) {
|
|
744
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
745
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
746
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
747
|
-
endRow: selectionRange?.endRow ?? rowIndex,
|
|
748
|
-
endCol: newDataColLeft,
|
|
749
|
-
}));
|
|
750
|
-
}
|
|
751
|
-
else {
|
|
752
|
-
this.setSelectionRange({ startRow: rowIndex, startCol: newDataColLeft, endRow: rowIndex, endCol: newDataColLeft });
|
|
753
|
-
}
|
|
754
|
-
this.setActiveCell({ rowIndex, columnIndex: newColLeft });
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
|
-
case 'Tab': {
|
|
758
|
-
e.preventDefault();
|
|
759
|
-
const tabResult = computeTabNavigation(rowIndex, columnIndex, maxRowIndex, maxColIndex, colOffset, e.shiftKey);
|
|
760
|
-
const newDataColTab = tabResult.columnIndex - colOffset;
|
|
761
|
-
this.setSelectionRange({ startRow: tabResult.rowIndex, startCol: newDataColTab, endRow: tabResult.rowIndex, endCol: newDataColTab });
|
|
762
|
-
this.setActiveCell({ rowIndex: tabResult.rowIndex, columnIndex: tabResult.columnIndex });
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
case 'Home': {
|
|
766
|
-
e.preventDefault();
|
|
767
|
-
const newRowHome = ctrl ? 0 : rowIndex;
|
|
768
|
-
this.setSelectionRange({ startRow: newRowHome, startCol: 0, endRow: newRowHome, endCol: 0 });
|
|
769
|
-
this.setActiveCell({ rowIndex: newRowHome, columnIndex: colOffset });
|
|
770
|
-
break;
|
|
771
|
-
}
|
|
772
|
-
case 'End': {
|
|
773
|
-
e.preventDefault();
|
|
774
|
-
const newRowEnd = ctrl ? maxRowIndex : rowIndex;
|
|
775
|
-
this.setSelectionRange({ startRow: newRowEnd, startCol: visibleColumnCount - 1, endRow: newRowEnd, endCol: visibleColumnCount - 1 });
|
|
776
|
-
this.setActiveCell({ rowIndex: newRowEnd, columnIndex: maxColIndex });
|
|
777
|
-
break;
|
|
778
|
-
}
|
|
779
|
-
case 'Enter':
|
|
780
|
-
case 'F2': {
|
|
781
|
-
e.preventDefault();
|
|
782
|
-
if (dataColIndex >= 0 && dataColIndex < visibleCols.length) {
|
|
783
|
-
const col = visibleCols[dataColIndex];
|
|
784
|
-
const item = items[rowIndex];
|
|
785
|
-
if (item && col) {
|
|
786
|
-
const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
|
|
787
|
-
if (editable !== false && colEditable && onCellValueChanged != null) {
|
|
788
|
-
this.setEditingCell({ rowId: getRowId(item), columnId: col.columnId });
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
break;
|
|
793
|
-
}
|
|
794
|
-
case 'Escape':
|
|
795
|
-
e.preventDefault();
|
|
796
|
-
if (editingCell != null) {
|
|
797
|
-
this.setEditingCell(null);
|
|
798
|
-
}
|
|
799
|
-
else {
|
|
800
|
-
this.clearClipboardRanges();
|
|
801
|
-
this.setActiveCell(null);
|
|
802
|
-
this.setSelectionRange(null);
|
|
803
|
-
}
|
|
804
|
-
break;
|
|
805
|
-
case ' ':
|
|
806
|
-
if (rowSelection !== 'none' && columnIndex === 0 && hasCheckboxCol) {
|
|
807
|
-
e.preventDefault();
|
|
808
|
-
const item = items[rowIndex];
|
|
809
|
-
if (item) {
|
|
810
|
-
const id = getRowId(item);
|
|
811
|
-
const isSelected = selectedRowIds.has(id);
|
|
812
|
-
this.handleRowCheckboxChange(id, !isSelected, rowIndex, e.shiftKey);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
break;
|
|
816
|
-
case 'z':
|
|
817
|
-
if (ctrl) {
|
|
818
|
-
if (editingCell == null) {
|
|
819
|
-
if (e.shiftKey) {
|
|
820
|
-
e.preventDefault();
|
|
821
|
-
this.redo();
|
|
822
|
-
}
|
|
823
|
-
else {
|
|
824
|
-
e.preventDefault();
|
|
825
|
-
this.undo();
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
break;
|
|
830
|
-
case 'y':
|
|
831
|
-
if (ctrl && editingCell == null) {
|
|
832
|
-
e.preventDefault();
|
|
833
|
-
this.redo();
|
|
834
|
-
}
|
|
835
|
-
break;
|
|
836
|
-
case 'a':
|
|
837
|
-
if (ctrl) {
|
|
838
|
-
if (editingCell != null)
|
|
839
|
-
break;
|
|
840
|
-
e.preventDefault();
|
|
841
|
-
if (items.length > 0 && visibleColumnCount > 0) {
|
|
842
|
-
this.setSelectionRange({ startRow: 0, startCol: 0, endRow: items.length - 1, endCol: visibleColumnCount - 1 });
|
|
843
|
-
this.setActiveCell({ rowIndex: 0, columnIndex: colOffset });
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
break;
|
|
847
|
-
case 'Delete':
|
|
848
|
-
case 'Backspace': {
|
|
849
|
-
if (editingCell != null)
|
|
850
|
-
break;
|
|
851
|
-
if (editable === false)
|
|
852
|
-
break;
|
|
853
|
-
if (onCellValueChanged == null)
|
|
854
|
-
break;
|
|
855
|
-
const range = selectionRange ?? (activeCell != null
|
|
856
|
-
? { startRow: activeCell.rowIndex, startCol: activeCell.columnIndex - colOffset, endRow: activeCell.rowIndex, endCol: activeCell.columnIndex - colOffset }
|
|
857
|
-
: null);
|
|
858
|
-
if (range == null)
|
|
859
|
-
break;
|
|
860
|
-
e.preventDefault();
|
|
861
|
-
const norm = normalizeSelectionRange(range);
|
|
862
|
-
for (let r = norm.startRow; r <= norm.endRow; r++) {
|
|
863
|
-
for (let c = norm.startCol; c <= norm.endCol; c++) {
|
|
864
|
-
if (r >= items.length || c >= visibleCols.length)
|
|
865
|
-
continue;
|
|
866
|
-
const item = items[r];
|
|
867
|
-
const col = visibleCols[c];
|
|
868
|
-
const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
|
|
869
|
-
if (!colEditable)
|
|
870
|
-
continue;
|
|
871
|
-
const oldValue = getCellValue(item, col);
|
|
872
|
-
const result = parseValue('', oldValue, item, col);
|
|
873
|
-
if (!result.valid)
|
|
874
|
-
continue;
|
|
875
|
-
onCellValueChanged({ item, columnId: col.columnId, oldValue, newValue: result.value, rowIndex: r });
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
break;
|
|
879
|
-
}
|
|
880
|
-
case 'F10':
|
|
881
|
-
if (e.shiftKey) {
|
|
882
|
-
e.preventDefault();
|
|
883
|
-
const wrapper = this.wrapperEl();
|
|
884
|
-
if (activeCell != null && wrapper) {
|
|
885
|
-
const sel = `[data-row-index="${activeCell.rowIndex}"][data-col-index="${activeCell.columnIndex}"]`;
|
|
886
|
-
const cell = wrapper.querySelector(sel);
|
|
887
|
-
if (cell) {
|
|
888
|
-
const rect = cell.getBoundingClientRect();
|
|
889
|
-
this.setContextMenuPosition({ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 });
|
|
890
|
-
}
|
|
891
|
-
else {
|
|
892
|
-
this.setContextMenuPosition({ x: 100, y: 100 });
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
else {
|
|
896
|
-
this.setContextMenuPosition({ x: 100, y: 100 });
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
break;
|
|
900
|
-
default:
|
|
901
|
-
break;
|
|
902
|
-
}
|
|
290
|
+
this.interactionHelper.handleGridKeyDown(e, p.items, p.getRowId, this.visibleCols(), this.colOffset(), this.hasCheckboxCol(), this.visibleColumnCount(), p.editable, this.wrappedOnCellValueChanged(), p.onCellValueChanged, p.rowSelection ?? 'none', this.selectedRowIds(), this.wrapperEl(), (rowId, checked, rowIndex, shiftKey) => this.handleRowCheckboxChange(rowId, checked, rowIndex, shiftKey), this.editingHelper.editingCellSig(), (cell) => this.setEditingCell(cell));
|
|
903
291
|
}
|
|
904
|
-
// --- Fill handle ---
|
|
292
|
+
// --- Fill handle (delegated to interactionHelper + setupFillHandleDrag) ---
|
|
905
293
|
handleFillHandleMouseDown(e) {
|
|
906
|
-
|
|
907
|
-
e.stopPropagation();
|
|
908
|
-
const range = this.selectionRangeSig();
|
|
909
|
-
if (!range)
|
|
910
|
-
return;
|
|
911
|
-
this.fillDragStart = { startRow: range.startRow, startCol: range.startCol };
|
|
294
|
+
this.interactionHelper.handleFillHandleMouseDown(e);
|
|
912
295
|
this.setupFillHandleDrag();
|
|
913
296
|
}
|
|
914
297
|
// --- Column pinning ---
|
|
@@ -977,11 +360,11 @@ let DataGridStateService = class DataGridStateService {
|
|
|
977
360
|
hasCheckboxCol: this.hasCheckboxCol(),
|
|
978
361
|
hasRowNumbersCol: this.hasRowNumbersCol(),
|
|
979
362
|
rowIndexByRowId: this.rowIndexByRowId(),
|
|
980
|
-
containerWidth: this.containerWidthSig(),
|
|
363
|
+
containerWidth: this.layoutHelper.containerWidthSig(),
|
|
981
364
|
minTableWidth: this.minTableWidth(),
|
|
982
365
|
desiredTableWidth: this.desiredTableWidth(),
|
|
983
|
-
columnSizingOverrides: this.columnSizingOverridesSig(),
|
|
984
|
-
setColumnSizingOverrides: (overrides) => this.columnSizingOverridesSig.set(overrides),
|
|
366
|
+
columnSizingOverrides: this.layoutHelper.columnSizingOverridesSig(),
|
|
367
|
+
setColumnSizingOverrides: (overrides) => this.layoutHelper.columnSizingOverridesSig.set(overrides),
|
|
985
368
|
onColumnResized: p?.onColumnResized,
|
|
986
369
|
onAutosizeColumn: p?.onAutosizeColumn,
|
|
987
370
|
};
|
|
@@ -994,47 +377,42 @@ let DataGridStateService = class DataGridStateService {
|
|
|
994
377
|
someSelected: this.someSelected(),
|
|
995
378
|
};
|
|
996
379
|
const editing = {
|
|
997
|
-
editingCell: this.editingCellSig(),
|
|
380
|
+
editingCell: this.editingHelper.editingCellSig(),
|
|
998
381
|
setEditingCell: (cell) => this.setEditingCell(cell),
|
|
999
|
-
pendingEditorValue: this.pendingEditorValueSig(),
|
|
382
|
+
pendingEditorValue: this.editingHelper.pendingEditorValueSig(),
|
|
1000
383
|
setPendingEditorValue: (v) => this.setPendingEditorValue(v),
|
|
1001
384
|
commitCellEdit: (item, colId, oldVal, newVal, rowIdx, globalColIdx) => this.commitCellEdit(item, colId, oldVal, newVal, rowIdx, globalColIdx),
|
|
1002
385
|
cancelPopoverEdit: () => this.cancelPopoverEdit(),
|
|
1003
|
-
popoverAnchorEl: this.popoverAnchorElSig(),
|
|
1004
|
-
setPopoverAnchorEl: (el) => this.popoverAnchorElSig.set(el),
|
|
386
|
+
popoverAnchorEl: this.editingHelper.popoverAnchorElSig(),
|
|
387
|
+
setPopoverAnchorEl: (el) => this.editingHelper.popoverAnchorElSig.set(el),
|
|
1005
388
|
};
|
|
1006
|
-
const noop = () => { };
|
|
1007
|
-
const noopAsync = async () => { };
|
|
1008
|
-
const noopMouse = (_e, _r, _c) => { };
|
|
1009
|
-
const noopKey = (_e) => { };
|
|
1010
|
-
const noopCtx = (_e) => { };
|
|
1011
389
|
const interaction = {
|
|
1012
|
-
activeCell: cellSel ? this.activeCellSig() : null,
|
|
1013
|
-
setActiveCell: cellSel ? (cell) => this.setActiveCell(cell) :
|
|
1014
|
-
selectionRange: cellSel ? this.selectionRangeSig() : null,
|
|
1015
|
-
setSelectionRange: cellSel ? (range) => this.setSelectionRange(range) :
|
|
1016
|
-
handleCellMouseDown: cellSel ? (e, r, c) => this.handleCellMouseDown(e, r, c) :
|
|
1017
|
-
handleSelectAllCells: cellSel ? () => this.handleSelectAllCells() :
|
|
390
|
+
activeCell: cellSel ? this.interactionHelper.activeCellSig() : null,
|
|
391
|
+
setActiveCell: cellSel ? (cell) => this.setActiveCell(cell) : undefined,
|
|
392
|
+
selectionRange: cellSel ? this.interactionHelper.selectionRangeSig() : null,
|
|
393
|
+
setSelectionRange: cellSel ? (range) => this.setSelectionRange(range) : undefined,
|
|
394
|
+
handleCellMouseDown: cellSel ? (e, r, c) => this.handleCellMouseDown(e, r, c) : NOOP_MOUSE,
|
|
395
|
+
handleSelectAllCells: cellSel ? () => this.handleSelectAllCells() : NOOP,
|
|
1018
396
|
hasCellSelection: cellSel ? this.hasCellSelection() : false,
|
|
1019
|
-
handleGridKeyDown: cellSel ? (e) => this.handleGridKeyDown(e) :
|
|
1020
|
-
handleFillHandleMouseDown: cellSel ? (e) => this.handleFillHandleMouseDown(e) :
|
|
1021
|
-
handleCopy: cellSel ? () => this.handleCopy() :
|
|
1022
|
-
handleCut: cellSel ? () => this.handleCut() :
|
|
1023
|
-
handlePaste: cellSel ? () => this.handlePaste() :
|
|
1024
|
-
cutRange: cellSel ? this.cutRangeSig() : null,
|
|
1025
|
-
copyRange: cellSel ? this.copyRangeSig() : null,
|
|
1026
|
-
clearClipboardRanges: cellSel ? () => this.clearClipboardRanges() :
|
|
397
|
+
handleGridKeyDown: cellSel ? (e) => this.handleGridKeyDown(e) : NOOP_KEY,
|
|
398
|
+
handleFillHandleMouseDown: cellSel ? (e) => this.handleFillHandleMouseDown(e) : undefined,
|
|
399
|
+
handleCopy: cellSel ? () => this.handleCopy() : NOOP,
|
|
400
|
+
handleCut: cellSel ? () => this.handleCut() : NOOP,
|
|
401
|
+
handlePaste: cellSel ? () => this.handlePaste() : NOOP_ASYNC,
|
|
402
|
+
cutRange: cellSel ? this.interactionHelper.cutRangeSig() : null,
|
|
403
|
+
copyRange: cellSel ? this.interactionHelper.copyRangeSig() : null,
|
|
404
|
+
clearClipboardRanges: cellSel ? () => this.clearClipboardRanges() : NOOP,
|
|
1027
405
|
canUndo: this.canUndo(),
|
|
1028
406
|
canRedo: this.canRedo(),
|
|
1029
407
|
onUndo: () => this.undo(),
|
|
1030
408
|
onRedo: () => this.redo(),
|
|
1031
|
-
isDragging: cellSel ? this.isDraggingSig() : false,
|
|
409
|
+
isDragging: cellSel ? this.interactionHelper.isDraggingSig() : false,
|
|
1032
410
|
};
|
|
1033
411
|
const contextMenu = {
|
|
1034
|
-
menuPosition: cellSel ? this.contextMenuPositionSig() : null,
|
|
1035
|
-
setMenuPosition: cellSel ? (pos) => this.setContextMenuPosition(pos) :
|
|
1036
|
-
handleCellContextMenu: cellSel ? (e) => this.handleCellContextMenu(e) :
|
|
1037
|
-
closeContextMenu: cellSel ? () => this.closeContextMenu() :
|
|
412
|
+
menuPosition: cellSel ? this.interactionHelper.contextMenuPositionSig() : null,
|
|
413
|
+
setMenuPosition: cellSel ? (pos) => this.setContextMenuPosition(pos) : undefined,
|
|
414
|
+
handleCellContextMenu: cellSel ? (e) => this.handleCellContextMenu(e) : NOOP_CTX,
|
|
415
|
+
closeContextMenu: cellSel ? () => this.closeContextMenu() : NOOP,
|
|
1038
416
|
};
|
|
1039
417
|
const viewModels = {
|
|
1040
418
|
headerFilterInput: {
|
|
@@ -1048,17 +426,17 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1048
426
|
peopleSearch: p?.peopleSearch,
|
|
1049
427
|
},
|
|
1050
428
|
cellDescriptorInput: {
|
|
1051
|
-
editingCell: this.editingCellSig(),
|
|
1052
|
-
activeCell: cellSel ? this.activeCellSig() : null,
|
|
1053
|
-
selectionRange: cellSel ? this.selectionRangeSig() : null,
|
|
1054
|
-
cutRange: cellSel ? this.cutRangeSig() : null,
|
|
1055
|
-
copyRange: cellSel ? this.copyRangeSig() : null,
|
|
429
|
+
editingCell: this.editingHelper.editingCellSig(),
|
|
430
|
+
activeCell: cellSel ? this.interactionHelper.activeCellSig() : null,
|
|
431
|
+
selectionRange: cellSel ? this.interactionHelper.selectionRangeSig() : null,
|
|
432
|
+
cutRange: cellSel ? this.interactionHelper.cutRangeSig() : null,
|
|
433
|
+
copyRange: cellSel ? this.interactionHelper.copyRangeSig() : null,
|
|
1056
434
|
colOffset: this.colOffset(),
|
|
1057
435
|
itemsLength: p?.items.length ?? 0,
|
|
1058
436
|
getRowId: p?.getRowId ?? ((item) => item['id']),
|
|
1059
437
|
editable: p?.editable,
|
|
1060
438
|
onCellValueChanged: this.wrappedOnCellValueChanged(),
|
|
1061
|
-
isDragging: cellSel ? this.isDraggingSig() : false,
|
|
439
|
+
isDragging: cellSel ? this.interactionHelper.isDraggingSig() : false,
|
|
1062
440
|
},
|
|
1063
441
|
statusBarConfig: this.statusBarConfig(),
|
|
1064
442
|
showEmptyInGrid: this.showEmptyInGrid(),
|
|
@@ -1088,135 +466,20 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1088
466
|
};
|
|
1089
467
|
return { layout, rowSelection, editing, interaction, contextMenu, viewModels, pinning };
|
|
1090
468
|
}
|
|
1091
|
-
// --- Private helpers ---
|
|
1092
|
-
getEffectiveRange() {
|
|
1093
|
-
const sel = this.selectionRangeSig();
|
|
1094
|
-
const ac = this.activeCellSig();
|
|
1095
|
-
const colOffset = this.colOffset();
|
|
1096
|
-
return sel ?? (ac != null
|
|
1097
|
-
? { startRow: ac.rowIndex, startCol: ac.columnIndex - colOffset, endRow: ac.rowIndex, endCol: ac.columnIndex - colOffset }
|
|
1098
|
-
: null);
|
|
1099
|
-
}
|
|
469
|
+
// --- Private helpers (drag selection delegated to interactionHelper) ---
|
|
1100
470
|
onWindowMouseMove(e) {
|
|
1101
|
-
|
|
1102
|
-
return;
|
|
1103
|
-
if (!this.dragMoved) {
|
|
1104
|
-
this.dragMoved = true;
|
|
1105
|
-
this.isDraggingSig.set(true);
|
|
1106
|
-
}
|
|
1107
|
-
this.lastMousePos = { cx: e.clientX, cy: e.clientY };
|
|
1108
|
-
if (this.rafId)
|
|
1109
|
-
cancelAnimationFrame(this.rafId);
|
|
1110
|
-
this.rafId = requestAnimationFrame(() => {
|
|
1111
|
-
this.rafId = 0;
|
|
1112
|
-
const pos = this.lastMousePos;
|
|
1113
|
-
if (!pos)
|
|
1114
|
-
return;
|
|
1115
|
-
const newRange = this.resolveRangeFromMouse(pos.cx, pos.cy);
|
|
1116
|
-
if (!newRange)
|
|
1117
|
-
return;
|
|
1118
|
-
const prev = this.liveDragRange;
|
|
1119
|
-
if (prev && prev.startRow === newRange.startRow && prev.startCol === newRange.startCol &&
|
|
1120
|
-
prev.endRow === newRange.endRow && prev.endCol === newRange.endCol)
|
|
1121
|
-
return;
|
|
1122
|
-
this.liveDragRange = newRange;
|
|
1123
|
-
this.applyDragAttrs(newRange);
|
|
1124
|
-
});
|
|
471
|
+
this.interactionHelper.onWindowMouseMove(e, this.colOffset(), this.wrapperEl());
|
|
1125
472
|
}
|
|
1126
473
|
onWindowMouseUp() {
|
|
1127
|
-
|
|
1128
|
-
return;
|
|
1129
|
-
if (this.autoScrollInterval) {
|
|
1130
|
-
clearInterval(this.autoScrollInterval);
|
|
1131
|
-
this.autoScrollInterval = null;
|
|
1132
|
-
}
|
|
1133
|
-
if (this.rafId) {
|
|
1134
|
-
cancelAnimationFrame(this.rafId);
|
|
1135
|
-
this.rafId = 0;
|
|
1136
|
-
}
|
|
1137
|
-
this.isDraggingRef = false;
|
|
1138
|
-
const wasDrag = this.dragMoved;
|
|
1139
|
-
if (wasDrag) {
|
|
1140
|
-
const pos = this.lastMousePos;
|
|
1141
|
-
if (pos) {
|
|
1142
|
-
const flushed = this.resolveRangeFromMouse(pos.cx, pos.cy);
|
|
1143
|
-
if (flushed)
|
|
1144
|
-
this.liveDragRange = flushed;
|
|
1145
|
-
}
|
|
1146
|
-
const finalRange = this.liveDragRange;
|
|
1147
|
-
if (finalRange) {
|
|
1148
|
-
this.setSelectionRange(finalRange);
|
|
1149
|
-
this.setActiveCell({
|
|
1150
|
-
rowIndex: finalRange.endRow,
|
|
1151
|
-
columnIndex: finalRange.endCol + this.colOffset(),
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
this.clearDragAttrs();
|
|
1156
|
-
this.liveDragRange = null;
|
|
1157
|
-
this.lastMousePos = null;
|
|
1158
|
-
this.dragStartPos = null;
|
|
1159
|
-
if (wasDrag)
|
|
1160
|
-
this.isDraggingSig.set(false);
|
|
1161
|
-
}
|
|
1162
|
-
resolveRangeFromMouse(cx, cy) {
|
|
1163
|
-
if (!this.dragStartPos)
|
|
1164
|
-
return null;
|
|
1165
|
-
const target = document.elementFromPoint(cx, cy);
|
|
1166
|
-
const cell = target?.closest?.('[data-row-index][data-col-index]');
|
|
1167
|
-
if (!cell)
|
|
1168
|
-
return null;
|
|
1169
|
-
const r = parseInt(cell.getAttribute('data-row-index') ?? '', 10);
|
|
1170
|
-
const c = parseInt(cell.getAttribute('data-col-index') ?? '', 10);
|
|
1171
|
-
const colOff = this.colOffset();
|
|
1172
|
-
if (Number.isNaN(r) || Number.isNaN(c) || c < colOff)
|
|
1173
|
-
return null;
|
|
1174
|
-
const dataCol = c - colOff;
|
|
1175
|
-
const start = this.dragStartPos;
|
|
1176
|
-
return normalizeSelectionRange({
|
|
1177
|
-
startRow: start.row, startCol: start.col,
|
|
1178
|
-
endRow: r, endCol: dataCol,
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
applyDragAttrs(range) {
|
|
1182
|
-
const wrapper = this.wrapperEl();
|
|
1183
|
-
if (!wrapper)
|
|
1184
|
-
return;
|
|
1185
|
-
const colOff = this.colOffset();
|
|
1186
|
-
const minR = Math.min(range.startRow, range.endRow);
|
|
1187
|
-
const maxR = Math.max(range.startRow, range.endRow);
|
|
1188
|
-
const minC = Math.min(range.startCol, range.endCol);
|
|
1189
|
-
const maxC = Math.max(range.startCol, range.endCol);
|
|
1190
|
-
const cells = wrapper.querySelectorAll('[data-row-index][data-col-index]');
|
|
1191
|
-
for (let i = 0; i < cells.length; i++) {
|
|
1192
|
-
const el = cells[i];
|
|
1193
|
-
const r = parseInt(el.getAttribute('data-row-index'), 10);
|
|
1194
|
-
const c = parseInt(el.getAttribute('data-col-index'), 10) - colOff;
|
|
1195
|
-
const inRange = r >= minR && r <= maxR && c >= minC && c <= maxC;
|
|
1196
|
-
if (inRange) {
|
|
1197
|
-
if (!el.hasAttribute('data-drag-range'))
|
|
1198
|
-
el.setAttribute('data-drag-range', '');
|
|
1199
|
-
}
|
|
1200
|
-
else {
|
|
1201
|
-
if (el.hasAttribute('data-drag-range'))
|
|
1202
|
-
el.removeAttribute('data-drag-range');
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
clearDragAttrs() {
|
|
1207
|
-
const wrapper = this.wrapperEl();
|
|
1208
|
-
if (!wrapper)
|
|
1209
|
-
return;
|
|
1210
|
-
const marked = wrapper.querySelectorAll('[data-drag-range]');
|
|
1211
|
-
for (let i = 0; i < marked.length; i++)
|
|
1212
|
-
marked[i].removeAttribute('data-drag-range');
|
|
474
|
+
this.interactionHelper.onWindowMouseUp(this.colOffset(), this.wrapperEl());
|
|
1213
475
|
}
|
|
1214
476
|
setupFillHandleDrag() {
|
|
1215
477
|
const p = this.props();
|
|
1216
|
-
|
|
478
|
+
const fillDragStart = this.interactionHelper.fillDragStart;
|
|
479
|
+
if (!fillDragStart || p?.editable === false || !this.wrappedOnCellValueChanged())
|
|
1217
480
|
return;
|
|
1218
481
|
const colOff = this.colOffset();
|
|
1219
|
-
const fillStart =
|
|
482
|
+
const fillStart = fillDragStart;
|
|
1220
483
|
let fillDragEnd = { endRow: fillStart.startRow, endCol: fillStart.startCol };
|
|
1221
484
|
let liveFillRange = null;
|
|
1222
485
|
let lastFillMousePos = null;
|
|
@@ -1238,10 +501,10 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1238
501
|
};
|
|
1239
502
|
const onMove = (e) => {
|
|
1240
503
|
lastFillMousePos = { cx: e.clientX, cy: e.clientY };
|
|
1241
|
-
if (this.fillRafId)
|
|
1242
|
-
cancelAnimationFrame(this.fillRafId);
|
|
1243
|
-
this.fillRafId = requestAnimationFrame(() => {
|
|
1244
|
-
this.fillRafId = 0;
|
|
504
|
+
if (this.interactionHelper.fillRafId)
|
|
505
|
+
cancelAnimationFrame(this.interactionHelper.fillRafId);
|
|
506
|
+
this.interactionHelper.fillRafId = requestAnimationFrame(() => {
|
|
507
|
+
this.interactionHelper.fillRafId = 0;
|
|
1245
508
|
if (!lastFillMousePos)
|
|
1246
509
|
return;
|
|
1247
510
|
const newRange = resolveRange(lastFillMousePos.cx, lastFillMousePos.cy);
|
|
@@ -1254,17 +517,17 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1254
517
|
return;
|
|
1255
518
|
liveFillRange = newRange;
|
|
1256
519
|
fillDragEnd = { endRow: newRange.endRow, endCol: newRange.endCol };
|
|
1257
|
-
this.applyDragAttrs(newRange);
|
|
520
|
+
this.interactionHelper.applyDragAttrs(newRange, colOff, this.wrapperEl());
|
|
1258
521
|
});
|
|
1259
522
|
};
|
|
1260
523
|
const onUp = () => {
|
|
1261
524
|
window.removeEventListener('mousemove', onMove, true);
|
|
1262
525
|
window.removeEventListener('mouseup', onUp, true);
|
|
1263
|
-
this.fillMoveHandler = null;
|
|
1264
|
-
this.fillUpHandler = null;
|
|
1265
|
-
if (this.fillRafId) {
|
|
1266
|
-
cancelAnimationFrame(this.fillRafId);
|
|
1267
|
-
this.fillRafId = 0;
|
|
526
|
+
this.interactionHelper.fillMoveHandler = null;
|
|
527
|
+
this.interactionHelper.fillUpHandler = null;
|
|
528
|
+
if (this.interactionHelper.fillRafId) {
|
|
529
|
+
cancelAnimationFrame(this.interactionHelper.fillRafId);
|
|
530
|
+
this.interactionHelper.fillRafId = 0;
|
|
1268
531
|
}
|
|
1269
532
|
if (lastFillMousePos) {
|
|
1270
533
|
const flushed = resolveRange(lastFillMousePos.cx, lastFillMousePos.cy);
|
|
@@ -1273,7 +536,7 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1273
536
|
fillDragEnd = { endRow: flushed.endRow, endCol: flushed.endCol };
|
|
1274
537
|
}
|
|
1275
538
|
}
|
|
1276
|
-
this.clearDragAttrs();
|
|
539
|
+
this.interactionHelper.clearDragAttrs(this.wrapperEl());
|
|
1277
540
|
const norm = normalizeSelectionRange({
|
|
1278
541
|
startRow: fillStart.startRow, startCol: fillStart.startCol,
|
|
1279
542
|
endRow: fillDragEnd.endRow, endCol: fillDragEnd.endCol,
|
|
@@ -1281,12 +544,14 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1281
544
|
this.setSelectionRange(norm);
|
|
1282
545
|
this.setActiveCell({ rowIndex: fillDragEnd.endRow, columnIndex: fillDragEnd.endCol + colOff });
|
|
1283
546
|
// Apply fill values
|
|
547
|
+
if (!p)
|
|
548
|
+
return;
|
|
1284
549
|
const items = p.items;
|
|
1285
550
|
const visibleCols = this.visibleCols();
|
|
1286
551
|
const startItem = items[norm.startRow];
|
|
1287
552
|
const startColDef = visibleCols[norm.startCol];
|
|
1288
553
|
const onCellValueChanged = this.wrappedOnCellValueChanged();
|
|
1289
|
-
if (startItem && startColDef) {
|
|
554
|
+
if (startItem && startColDef && onCellValueChanged) {
|
|
1290
555
|
const startValue = getCellValue(startItem, startColDef);
|
|
1291
556
|
this.beginBatch();
|
|
1292
557
|
for (let row = norm.startRow; row <= norm.endRow; row++) {
|
|
@@ -1309,11 +574,11 @@ let DataGridStateService = class DataGridStateService {
|
|
|
1309
574
|
}
|
|
1310
575
|
this.endBatch();
|
|
1311
576
|
}
|
|
1312
|
-
this.fillDragStart = null;
|
|
577
|
+
this.interactionHelper.fillDragStart = null;
|
|
1313
578
|
};
|
|
1314
579
|
// Track handlers for cleanup on destroy
|
|
1315
|
-
this.fillMoveHandler = onMove;
|
|
1316
|
-
this.fillUpHandler = onUp;
|
|
580
|
+
this.interactionHelper.fillMoveHandler = onMove;
|
|
581
|
+
this.interactionHelper.fillUpHandler = onUp;
|
|
1317
582
|
this.ngZone.runOutsideAngular(() => {
|
|
1318
583
|
window.addEventListener('mousemove', onMove, true);
|
|
1319
584
|
window.addEventListener('mouseup', onUp, true);
|