@alaarab/ogrid-js 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +6343 -32
- package/package.json +7 -5
- package/dist/esm/OGrid.js +0 -578
- package/dist/esm/OGridEventWiring.js +0 -178
- package/dist/esm/OGridRendering.js +0 -269
- package/dist/esm/components/ColumnChooser.js +0 -91
- package/dist/esm/components/ContextMenu.js +0 -125
- package/dist/esm/components/HeaderFilter.js +0 -281
- package/dist/esm/components/InlineCellEditor.js +0 -434
- package/dist/esm/components/MarchingAntsOverlay.js +0 -156
- package/dist/esm/components/PaginationControls.js +0 -85
- package/dist/esm/components/SideBar.js +0 -353
- package/dist/esm/components/StatusBar.js +0 -34
- package/dist/esm/renderer/TableRenderer.js +0 -846
- package/dist/esm/state/ClipboardState.js +0 -111
- package/dist/esm/state/ColumnPinningState.js +0 -82
- package/dist/esm/state/ColumnReorderState.js +0 -135
- package/dist/esm/state/ColumnResizeState.js +0 -55
- package/dist/esm/state/EventEmitter.js +0 -28
- package/dist/esm/state/FillHandleState.js +0 -206
- package/dist/esm/state/GridState.js +0 -324
- package/dist/esm/state/HeaderFilterState.js +0 -213
- package/dist/esm/state/KeyboardNavState.js +0 -216
- package/dist/esm/state/RowSelectionState.js +0 -72
- package/dist/esm/state/SelectionState.js +0 -109
- package/dist/esm/state/SideBarState.js +0 -41
- package/dist/esm/state/TableLayoutState.js +0 -97
- package/dist/esm/state/UndoRedoState.js +0 -71
- package/dist/esm/state/VirtualScrollState.js +0 -128
- package/dist/esm/types/columnTypes.js +0 -1
- package/dist/esm/types/gridTypes.js +0 -1
- package/dist/esm/types/index.js +0 -2
- package/dist/esm/utils/debounce.js +0 -2
- package/dist/esm/utils/getCellCoordinates.js +0 -15
- package/dist/esm/utils/index.js +0 -2
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { getCellValue, computeTabNavigation, computeArrowNavigation, applyCellDeletion } from '@alaarab/ogrid-core';
|
|
2
|
-
export class KeyboardNavState {
|
|
3
|
-
constructor(params, getActiveCell, getSelectionRange, setActiveCell, setSelectionRange) {
|
|
4
|
-
this.wrapperRef = null;
|
|
5
|
-
this.handleKeyDown = (e) => {
|
|
6
|
-
const { items, visibleCols, colOffset, editable, onCellValueChanged, onCopy, onCut, onPaste, onUndo, onRedo, onContextMenu, onStartEdit, getRowId, clearClipboardRanges } = this.params;
|
|
7
|
-
const activeCell = this.getActiveCell();
|
|
8
|
-
const selectionRange = this.getSelectionRange();
|
|
9
|
-
const maxRowIndex = items.length - 1;
|
|
10
|
-
const maxColIndex = visibleCols.length - 1 + colOffset;
|
|
11
|
-
if (items.length === 0)
|
|
12
|
-
return;
|
|
13
|
-
if (activeCell === null) {
|
|
14
|
-
if (['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Tab', 'Enter', 'Home', 'End'].includes(e.key)) {
|
|
15
|
-
this.setActiveCell({ rowIndex: 0, columnIndex: colOffset });
|
|
16
|
-
e.preventDefault();
|
|
17
|
-
}
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const { rowIndex, columnIndex } = activeCell;
|
|
21
|
-
const dataColIndex = columnIndex - colOffset;
|
|
22
|
-
const shift = e.shiftKey;
|
|
23
|
-
const isEmptyAt = (r, c) => {
|
|
24
|
-
if (r < 0 || r >= items.length || c < 0 || c >= visibleCols.length)
|
|
25
|
-
return true;
|
|
26
|
-
const v = getCellValue(items[r], visibleCols[c]);
|
|
27
|
-
return v == null || v === '';
|
|
28
|
-
};
|
|
29
|
-
switch (e.key) {
|
|
30
|
-
case 'c':
|
|
31
|
-
if (e.ctrlKey || e.metaKey) {
|
|
32
|
-
e.preventDefault();
|
|
33
|
-
onCopy?.();
|
|
34
|
-
}
|
|
35
|
-
break;
|
|
36
|
-
case 'x':
|
|
37
|
-
if (e.ctrlKey || e.metaKey) {
|
|
38
|
-
e.preventDefault();
|
|
39
|
-
onCut?.();
|
|
40
|
-
}
|
|
41
|
-
break;
|
|
42
|
-
case 'v':
|
|
43
|
-
if (e.ctrlKey || e.metaKey) {
|
|
44
|
-
e.preventDefault();
|
|
45
|
-
void onPaste?.();
|
|
46
|
-
}
|
|
47
|
-
break;
|
|
48
|
-
case 'ArrowDown':
|
|
49
|
-
case 'ArrowUp':
|
|
50
|
-
case 'ArrowRight':
|
|
51
|
-
case 'ArrowLeft': {
|
|
52
|
-
e.preventDefault();
|
|
53
|
-
const { newRowIndex, newColumnIndex, newRange } = computeArrowNavigation({
|
|
54
|
-
direction: e.key,
|
|
55
|
-
rowIndex, columnIndex, dataColIndex, colOffset,
|
|
56
|
-
maxRowIndex, maxColIndex,
|
|
57
|
-
visibleColCount: visibleCols.length,
|
|
58
|
-
isCtrl: e.ctrlKey || e.metaKey,
|
|
59
|
-
isShift: shift,
|
|
60
|
-
selectionRange,
|
|
61
|
-
isEmptyAt,
|
|
62
|
-
});
|
|
63
|
-
this.setActiveCell({ rowIndex: newRowIndex, columnIndex: newColumnIndex });
|
|
64
|
-
this.setSelectionRange(newRange);
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
case 'Tab': {
|
|
68
|
-
e.preventDefault();
|
|
69
|
-
const tabResult = computeTabNavigation(rowIndex, columnIndex, maxRowIndex, maxColIndex, colOffset, e.shiftKey);
|
|
70
|
-
const newDataColTab = tabResult.columnIndex - colOffset;
|
|
71
|
-
this.setActiveCell({ rowIndex: tabResult.rowIndex, columnIndex: tabResult.columnIndex });
|
|
72
|
-
this.setSelectionRange({
|
|
73
|
-
startRow: tabResult.rowIndex,
|
|
74
|
-
startCol: newDataColTab,
|
|
75
|
-
endRow: tabResult.rowIndex,
|
|
76
|
-
endCol: newDataColTab,
|
|
77
|
-
});
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
case 'Home': {
|
|
81
|
-
e.preventDefault();
|
|
82
|
-
const newRowHome = e.ctrlKey ? 0 : rowIndex;
|
|
83
|
-
this.setActiveCell({ rowIndex: newRowHome, columnIndex: colOffset });
|
|
84
|
-
this.setSelectionRange({
|
|
85
|
-
startRow: newRowHome,
|
|
86
|
-
startCol: 0,
|
|
87
|
-
endRow: newRowHome,
|
|
88
|
-
endCol: 0,
|
|
89
|
-
});
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
case 'End': {
|
|
93
|
-
e.preventDefault();
|
|
94
|
-
const newRowEnd = e.ctrlKey ? maxRowIndex : rowIndex;
|
|
95
|
-
this.setActiveCell({ rowIndex: newRowEnd, columnIndex: maxColIndex });
|
|
96
|
-
this.setSelectionRange({
|
|
97
|
-
startRow: newRowEnd,
|
|
98
|
-
startCol: visibleCols.length - 1,
|
|
99
|
-
endRow: newRowEnd,
|
|
100
|
-
endCol: visibleCols.length - 1,
|
|
101
|
-
});
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case 'Enter':
|
|
105
|
-
case 'F2': {
|
|
106
|
-
e.preventDefault();
|
|
107
|
-
if (dataColIndex >= 0 && dataColIndex < visibleCols.length) {
|
|
108
|
-
const col = visibleCols[dataColIndex];
|
|
109
|
-
const item = items[rowIndex];
|
|
110
|
-
if (item && col) {
|
|
111
|
-
const colEditable = col.editable === true ||
|
|
112
|
-
(typeof col.editable === 'function' && col.editable(item));
|
|
113
|
-
if (editable !== false && colEditable) {
|
|
114
|
-
onStartEdit?.(getRowId(item), col.columnId);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
case 'Escape':
|
|
121
|
-
e.preventDefault();
|
|
122
|
-
clearClipboardRanges?.();
|
|
123
|
-
this.setActiveCell(null);
|
|
124
|
-
this.setSelectionRange(null);
|
|
125
|
-
break;
|
|
126
|
-
case 'z':
|
|
127
|
-
if (e.ctrlKey || e.metaKey) {
|
|
128
|
-
if (e.shiftKey && onRedo) {
|
|
129
|
-
e.preventDefault();
|
|
130
|
-
onRedo();
|
|
131
|
-
}
|
|
132
|
-
else if (!e.shiftKey && onUndo) {
|
|
133
|
-
e.preventDefault();
|
|
134
|
-
onUndo();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
break;
|
|
138
|
-
case 'y':
|
|
139
|
-
if (e.ctrlKey || e.metaKey) {
|
|
140
|
-
e.preventDefault();
|
|
141
|
-
onRedo?.();
|
|
142
|
-
}
|
|
143
|
-
break;
|
|
144
|
-
case 'a':
|
|
145
|
-
if (e.ctrlKey || e.metaKey) {
|
|
146
|
-
e.preventDefault();
|
|
147
|
-
if (items.length > 0 && visibleCols.length > 0) {
|
|
148
|
-
this.setActiveCell({ rowIndex: 0, columnIndex: colOffset });
|
|
149
|
-
this.setSelectionRange({
|
|
150
|
-
startRow: 0,
|
|
151
|
-
startCol: 0,
|
|
152
|
-
endRow: items.length - 1,
|
|
153
|
-
endCol: visibleCols.length - 1,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
case 'Delete':
|
|
159
|
-
case 'Backspace': {
|
|
160
|
-
if (editable === false)
|
|
161
|
-
break;
|
|
162
|
-
if (onCellValueChanged == null)
|
|
163
|
-
break;
|
|
164
|
-
const range = selectionRange ??
|
|
165
|
-
(activeCell != null
|
|
166
|
-
? {
|
|
167
|
-
startRow: activeCell.rowIndex,
|
|
168
|
-
startCol: activeCell.columnIndex - colOffset,
|
|
169
|
-
endRow: activeCell.rowIndex,
|
|
170
|
-
endCol: activeCell.columnIndex - colOffset,
|
|
171
|
-
}
|
|
172
|
-
: null);
|
|
173
|
-
if (range == null)
|
|
174
|
-
break;
|
|
175
|
-
e.preventDefault();
|
|
176
|
-
const deleteEvents = applyCellDeletion(range, items, visibleCols);
|
|
177
|
-
for (const evt of deleteEvents)
|
|
178
|
-
onCellValueChanged(evt);
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
case 'F10':
|
|
182
|
-
if (e.shiftKey) {
|
|
183
|
-
e.preventDefault();
|
|
184
|
-
if (activeCell != null && this.wrapperRef) {
|
|
185
|
-
const sel = `[data-row-index="${activeCell.rowIndex}"][data-col-index="${activeCell.columnIndex}"]`;
|
|
186
|
-
const cell = this.wrapperRef.querySelector(sel);
|
|
187
|
-
if (cell) {
|
|
188
|
-
const rect = cell.getBoundingClientRect();
|
|
189
|
-
onContextMenu?.(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
onContextMenu?.(100, 100);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
onContextMenu?.(100, 100);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
break;
|
|
200
|
-
default:
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
this.params = params;
|
|
205
|
-
this.getActiveCell = getActiveCell;
|
|
206
|
-
this.getSelectionRange = getSelectionRange;
|
|
207
|
-
this.setActiveCell = setActiveCell;
|
|
208
|
-
this.setSelectionRange = setSelectionRange;
|
|
209
|
-
}
|
|
210
|
-
setWrapperRef(ref) {
|
|
211
|
-
this.wrapperRef = ref;
|
|
212
|
-
}
|
|
213
|
-
updateParams(params) {
|
|
214
|
-
this.params = params;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { applyRangeRowSelection, computeRowSelectionState } from '@alaarab/ogrid-core';
|
|
2
|
-
import { EventEmitter } from './EventEmitter';
|
|
3
|
-
/**
|
|
4
|
-
* Manages row selection state for single or multiple selection modes with shift-click range support.
|
|
5
|
-
* Vanilla JS equivalent of React's `useRowSelection` hook.
|
|
6
|
-
*/
|
|
7
|
-
export class RowSelectionState {
|
|
8
|
-
constructor(rowSelection, getRowId) {
|
|
9
|
-
this.emitter = new EventEmitter();
|
|
10
|
-
this._selectedRowIds = new Set();
|
|
11
|
-
this._lastClickedRow = -1;
|
|
12
|
-
this._rowSelection = rowSelection;
|
|
13
|
-
this._getRowId = getRowId;
|
|
14
|
-
}
|
|
15
|
-
get selectedRowIds() {
|
|
16
|
-
return this._selectedRowIds;
|
|
17
|
-
}
|
|
18
|
-
get rowSelection() {
|
|
19
|
-
return this._rowSelection;
|
|
20
|
-
}
|
|
21
|
-
updateSelection(newSelectedIds, items) {
|
|
22
|
-
this._selectedRowIds = newSelectedIds;
|
|
23
|
-
this.emitter.emit('rowSelectionChange', {
|
|
24
|
-
selectedRowIds: Array.from(newSelectedIds),
|
|
25
|
-
selectedItems: items.filter((item) => newSelectedIds.has(this._getRowId(item))),
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
handleRowCheckboxChange(rowId, checked, rowIndex, shiftKey, items) {
|
|
29
|
-
if (this._rowSelection === 'single') {
|
|
30
|
-
this.updateSelection(checked ? new Set([rowId]) : new Set(), items);
|
|
31
|
-
this._lastClickedRow = rowIndex;
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
let next;
|
|
35
|
-
if (shiftKey && this._lastClickedRow >= 0 && this._lastClickedRow !== rowIndex) {
|
|
36
|
-
next = applyRangeRowSelection(this._lastClickedRow, rowIndex, checked, items, this._getRowId, this._selectedRowIds);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
next = new Set(this._selectedRowIds);
|
|
40
|
-
if (checked)
|
|
41
|
-
next.add(rowId);
|
|
42
|
-
else
|
|
43
|
-
next.delete(rowId);
|
|
44
|
-
}
|
|
45
|
-
this._lastClickedRow = rowIndex;
|
|
46
|
-
this.updateSelection(next, items);
|
|
47
|
-
}
|
|
48
|
-
handleSelectAll(checked, items) {
|
|
49
|
-
if (checked) {
|
|
50
|
-
this.updateSelection(new Set(items.map((item) => this._getRowId(item))), items);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
this.updateSelection(new Set(), items);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
isAllSelected(items) {
|
|
57
|
-
return computeRowSelectionState(this._selectedRowIds, items, this._getRowId).allSelected;
|
|
58
|
-
}
|
|
59
|
-
isSomeSelected(items) {
|
|
60
|
-
return computeRowSelectionState(this._selectedRowIds, items, this._getRowId).someSelected;
|
|
61
|
-
}
|
|
62
|
-
getSelectedRows(items) {
|
|
63
|
-
return items.filter((item) => this._selectedRowIds.has(this._getRowId(item)));
|
|
64
|
-
}
|
|
65
|
-
onRowSelectionChange(handler) {
|
|
66
|
-
this.emitter.on('rowSelectionChange', handler);
|
|
67
|
-
return () => this.emitter.off('rowSelectionChange', handler);
|
|
68
|
-
}
|
|
69
|
-
destroy() {
|
|
70
|
-
this.emitter.removeAllListeners();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { rangesEqual } from '@alaarab/ogrid-core';
|
|
2
|
-
import { EventEmitter } from './EventEmitter';
|
|
3
|
-
export class SelectionState {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.emitter = new EventEmitter();
|
|
6
|
-
this._activeCell = null;
|
|
7
|
-
this._selectionRange = null;
|
|
8
|
-
this._selectedRowIds = new Set();
|
|
9
|
-
this._isDragging = false;
|
|
10
|
-
this.dragStartCell = null;
|
|
11
|
-
this.rafHandle = null;
|
|
12
|
-
this.pendingRange = null;
|
|
13
|
-
}
|
|
14
|
-
get activeCell() {
|
|
15
|
-
return this._activeCell;
|
|
16
|
-
}
|
|
17
|
-
get dragAnchor() {
|
|
18
|
-
return this.dragStartCell;
|
|
19
|
-
}
|
|
20
|
-
get selectionRange() {
|
|
21
|
-
return this._selectionRange;
|
|
22
|
-
}
|
|
23
|
-
get selectedRowIds() {
|
|
24
|
-
return this._selectedRowIds;
|
|
25
|
-
}
|
|
26
|
-
get isDragging() {
|
|
27
|
-
return this._isDragging;
|
|
28
|
-
}
|
|
29
|
-
/** Get the current drag range (used during drag for DOM attribute updates). */
|
|
30
|
-
getDragRange() {
|
|
31
|
-
return this.pendingRange;
|
|
32
|
-
}
|
|
33
|
-
setActiveCell(cell) {
|
|
34
|
-
this._activeCell = cell;
|
|
35
|
-
this._selectionRange = cell != null
|
|
36
|
-
? { startRow: cell.rowIndex, startCol: cell.columnIndex, endRow: cell.rowIndex, endCol: cell.columnIndex }
|
|
37
|
-
: null;
|
|
38
|
-
this.emitter.emit('selectionChange', { activeCell: cell, selectionRange: this._selectionRange });
|
|
39
|
-
}
|
|
40
|
-
setSelectionRange(range) {
|
|
41
|
-
this._selectionRange = range;
|
|
42
|
-
this.emitter.emit('selectionChange', { activeCell: this._activeCell, selectionRange: range });
|
|
43
|
-
}
|
|
44
|
-
clearSelection() {
|
|
45
|
-
this._activeCell = null;
|
|
46
|
-
this._selectionRange = null;
|
|
47
|
-
this.emitter.emit('selectionChange', { activeCell: null, selectionRange: null });
|
|
48
|
-
}
|
|
49
|
-
startDrag(rowIndex, colIndex) {
|
|
50
|
-
this._isDragging = true;
|
|
51
|
-
this.dragStartCell = { rowIndex, columnIndex: colIndex };
|
|
52
|
-
this._activeCell = { rowIndex, columnIndex: colIndex };
|
|
53
|
-
this._selectionRange = { startRow: rowIndex, startCol: colIndex, endRow: rowIndex, endCol: colIndex };
|
|
54
|
-
}
|
|
55
|
-
updateDrag(rowIndex, colIndex, applyFn) {
|
|
56
|
-
if (!this._isDragging || !this.dragStartCell)
|
|
57
|
-
return;
|
|
58
|
-
const newRange = {
|
|
59
|
-
startRow: this.dragStartCell.rowIndex,
|
|
60
|
-
startCol: this.dragStartCell.columnIndex,
|
|
61
|
-
endRow: rowIndex,
|
|
62
|
-
endCol: colIndex,
|
|
63
|
-
};
|
|
64
|
-
// Skip RAF if range hasn't changed (deduplication optimization)
|
|
65
|
-
if (rangesEqual(this.pendingRange, newRange))
|
|
66
|
-
return;
|
|
67
|
-
this.pendingRange = newRange;
|
|
68
|
-
if (this.rafHandle === null) {
|
|
69
|
-
this.rafHandle = requestAnimationFrame(() => {
|
|
70
|
-
if (this.pendingRange) {
|
|
71
|
-
applyFn(this.pendingRange);
|
|
72
|
-
}
|
|
73
|
-
this.rafHandle = null;
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
endDrag() {
|
|
78
|
-
if (this.rafHandle !== null) {
|
|
79
|
-
// Flush pending RAF synchronously before ending drag (critical for jsdom tests)
|
|
80
|
-
cancelAnimationFrame(this.rafHandle);
|
|
81
|
-
this.rafHandle = null;
|
|
82
|
-
}
|
|
83
|
-
if (this.pendingRange) {
|
|
84
|
-
this._selectionRange = this.pendingRange;
|
|
85
|
-
this.pendingRange = null;
|
|
86
|
-
this.emitter.emit('selectionChange', { activeCell: this._activeCell, selectionRange: this._selectionRange });
|
|
87
|
-
}
|
|
88
|
-
this._isDragging = false;
|
|
89
|
-
this.dragStartCell = null;
|
|
90
|
-
}
|
|
91
|
-
setSelectedRowIds(ids) {
|
|
92
|
-
this._selectedRowIds = ids;
|
|
93
|
-
this.emitter.emit('rowSelectionChange', { selectedRowIds: ids });
|
|
94
|
-
}
|
|
95
|
-
onSelectionChange(handler) {
|
|
96
|
-
this.emitter.on('selectionChange', handler);
|
|
97
|
-
return () => this.emitter.off('selectionChange', handler);
|
|
98
|
-
}
|
|
99
|
-
onRowSelectionChange(handler) {
|
|
100
|
-
this.emitter.on('rowSelectionChange', handler);
|
|
101
|
-
return () => this.emitter.off('rowSelectionChange', handler);
|
|
102
|
-
}
|
|
103
|
-
destroy() {
|
|
104
|
-
if (this.rafHandle !== null) {
|
|
105
|
-
cancelAnimationFrame(this.rafHandle);
|
|
106
|
-
}
|
|
107
|
-
this.emitter.removeAllListeners();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from './EventEmitter';
|
|
2
|
-
const DEFAULT_PANELS = ['columns', 'filters'];
|
|
3
|
-
export class SideBarState {
|
|
4
|
-
constructor(config) {
|
|
5
|
-
this.emitter = new EventEmitter();
|
|
6
|
-
this._isEnabled = config != null && config !== false;
|
|
7
|
-
if (!this._isEnabled || config === true) {
|
|
8
|
-
this._panels = DEFAULT_PANELS;
|
|
9
|
-
this._position = 'right';
|
|
10
|
-
this._activePanel = null;
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
13
|
-
const def = config;
|
|
14
|
-
this._panels = def.panels ?? DEFAULT_PANELS;
|
|
15
|
-
this._position = def.position ?? 'right';
|
|
16
|
-
this._activePanel = def.defaultPanel ?? null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
get isEnabled() { return this._isEnabled; }
|
|
20
|
-
get panels() { return this._panels; }
|
|
21
|
-
get position() { return this._position; }
|
|
22
|
-
get activePanel() { return this._activePanel; }
|
|
23
|
-
get isOpen() { return this._activePanel !== null; }
|
|
24
|
-
setActivePanel(panel) {
|
|
25
|
-
this._activePanel = panel;
|
|
26
|
-
this.emitter.emit('change');
|
|
27
|
-
}
|
|
28
|
-
toggle(panel) {
|
|
29
|
-
this.setActivePanel(this._activePanel === panel ? null : panel);
|
|
30
|
-
}
|
|
31
|
-
close() {
|
|
32
|
-
this.setActivePanel(null);
|
|
33
|
-
}
|
|
34
|
-
onChange(handler) {
|
|
35
|
-
this.emitter.on('change', handler);
|
|
36
|
-
return () => this.emitter.off('change', handler);
|
|
37
|
-
}
|
|
38
|
-
destroy() {
|
|
39
|
-
this.emitter.removeAllListeners();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, CHECKBOX_COLUMN_WIDTH } from '@alaarab/ogrid-core';
|
|
2
|
-
import { EventEmitter } from './EventEmitter';
|
|
3
|
-
export class TableLayoutState {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.emitter = new EventEmitter();
|
|
6
|
-
this._containerWidth = 0;
|
|
7
|
-
this._columnSizingOverrides = {};
|
|
8
|
-
this._ro = null;
|
|
9
|
-
}
|
|
10
|
-
/** Start observing a container element for resize. */
|
|
11
|
-
observeContainer(el) {
|
|
12
|
-
this.disconnectObserver();
|
|
13
|
-
// ResizeObserver may not be available in jsdom test environments
|
|
14
|
-
if (typeof ResizeObserver !== 'undefined') {
|
|
15
|
-
this._ro = new ResizeObserver((entries) => {
|
|
16
|
-
for (const entry of entries) {
|
|
17
|
-
const rect = entry.contentRect;
|
|
18
|
-
this._containerWidth = rect.width;
|
|
19
|
-
this.emitter.emit('layoutChange', { type: 'containerResize' });
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
this._ro.observe(el);
|
|
23
|
-
}
|
|
24
|
-
// Measure initial size
|
|
25
|
-
this._containerWidth = el.clientWidth;
|
|
26
|
-
}
|
|
27
|
-
disconnectObserver() {
|
|
28
|
-
if (this._ro) {
|
|
29
|
-
this._ro.disconnect();
|
|
30
|
-
this._ro = null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
get containerWidth() {
|
|
34
|
-
return this._containerWidth;
|
|
35
|
-
}
|
|
36
|
-
get columnSizingOverrides() {
|
|
37
|
-
return this._columnSizingOverrides;
|
|
38
|
-
}
|
|
39
|
-
/** Set a column width override (from resize drag). */
|
|
40
|
-
setColumnOverride(columnId, widthPx) {
|
|
41
|
-
this._columnSizingOverrides[columnId] = widthPx;
|
|
42
|
-
this.emitter.emit('layoutChange', { type: 'columnOverride' });
|
|
43
|
-
}
|
|
44
|
-
/** Compute minimum table width from visible columns. */
|
|
45
|
-
computeMinTableWidth(visibleColumnCount, hasCheckboxColumn) {
|
|
46
|
-
const checkboxWidth = hasCheckboxColumn ? CHECKBOX_COLUMN_WIDTH : 0;
|
|
47
|
-
return checkboxWidth + visibleColumnCount * (DEFAULT_MIN_COLUMN_WIDTH + CELL_PADDING);
|
|
48
|
-
}
|
|
49
|
-
/** Compute desired table width respecting overrides. */
|
|
50
|
-
computeDesiredTableWidth(visibleColumns, hasCheckboxColumn) {
|
|
51
|
-
const checkboxWidth = hasCheckboxColumn ? CHECKBOX_COLUMN_WIDTH : 0;
|
|
52
|
-
let total = checkboxWidth;
|
|
53
|
-
for (const col of visibleColumns) {
|
|
54
|
-
const override = this._columnSizingOverrides[col.columnId];
|
|
55
|
-
if (override) {
|
|
56
|
-
total += override + CELL_PADDING;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
total += (col.width ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH) + CELL_PADDING;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return total;
|
|
63
|
-
}
|
|
64
|
-
/** Get all column widths (overrides only — non-overridden columns use CSS defaults). */
|
|
65
|
-
getAllColumnWidths() {
|
|
66
|
-
return { ...this._columnSizingOverrides };
|
|
67
|
-
}
|
|
68
|
-
/** Remove overrides for columns that no longer exist. */
|
|
69
|
-
cleanupOverrides(validColumnIds) {
|
|
70
|
-
const next = {};
|
|
71
|
-
let changed = false;
|
|
72
|
-
for (const [key, value] of Object.entries(this._columnSizingOverrides)) {
|
|
73
|
-
if (validColumnIds.has(key)) {
|
|
74
|
-
next[key] = value;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
changed = true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (changed) {
|
|
81
|
-
this._columnSizingOverrides = next;
|
|
82
|
-
this.emitter.emit('layoutChange', { type: 'columnOverride' });
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
/** Apply initial column widths from options. */
|
|
86
|
-
applyInitialWidths(initialWidths) {
|
|
87
|
-
this._columnSizingOverrides = { ...initialWidths };
|
|
88
|
-
}
|
|
89
|
-
onLayoutChange(handler) {
|
|
90
|
-
this.emitter.on('layoutChange', handler);
|
|
91
|
-
return () => this.emitter.off('layoutChange', handler);
|
|
92
|
-
}
|
|
93
|
-
destroy() {
|
|
94
|
-
this.disconnectObserver();
|
|
95
|
-
this.emitter.removeAllListeners();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { UndoRedoStack } from '@alaarab/ogrid-core';
|
|
2
|
-
import { EventEmitter } from './EventEmitter';
|
|
3
|
-
export class UndoRedoState {
|
|
4
|
-
constructor(onCellValueChanged, maxUndoDepth = 100) {
|
|
5
|
-
this.onCellValueChanged = onCellValueChanged;
|
|
6
|
-
this.emitter = new EventEmitter();
|
|
7
|
-
this.stack = new UndoRedoStack(maxUndoDepth);
|
|
8
|
-
if (onCellValueChanged) {
|
|
9
|
-
this.wrappedCallback = (event) => {
|
|
10
|
-
this.stack.record(event);
|
|
11
|
-
if (!this.stack.isBatching) {
|
|
12
|
-
this.emitStackChange();
|
|
13
|
-
}
|
|
14
|
-
onCellValueChanged(event);
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
get canUndo() {
|
|
19
|
-
return this.stack.canUndo;
|
|
20
|
-
}
|
|
21
|
-
get canRedo() {
|
|
22
|
-
return this.stack.canRedo;
|
|
23
|
-
}
|
|
24
|
-
getWrappedCallback() {
|
|
25
|
-
return this.wrappedCallback;
|
|
26
|
-
}
|
|
27
|
-
beginBatch() {
|
|
28
|
-
this.stack.beginBatch();
|
|
29
|
-
}
|
|
30
|
-
endBatch() {
|
|
31
|
-
this.stack.endBatch();
|
|
32
|
-
this.emitStackChange();
|
|
33
|
-
}
|
|
34
|
-
undo() {
|
|
35
|
-
if (!this.onCellValueChanged)
|
|
36
|
-
return;
|
|
37
|
-
const lastBatch = this.stack.undo();
|
|
38
|
-
if (!lastBatch)
|
|
39
|
-
return;
|
|
40
|
-
this.emitStackChange();
|
|
41
|
-
for (let i = lastBatch.length - 1; i >= 0; i--) {
|
|
42
|
-
const ev = lastBatch[i];
|
|
43
|
-
this.onCellValueChanged({
|
|
44
|
-
...ev,
|
|
45
|
-
oldValue: ev.newValue,
|
|
46
|
-
newValue: ev.oldValue,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
redo() {
|
|
51
|
-
if (!this.onCellValueChanged)
|
|
52
|
-
return;
|
|
53
|
-
const nextBatch = this.stack.redo();
|
|
54
|
-
if (!nextBatch)
|
|
55
|
-
return;
|
|
56
|
-
this.emitStackChange();
|
|
57
|
-
for (const ev of nextBatch) {
|
|
58
|
-
this.onCellValueChanged(ev);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
emitStackChange() {
|
|
62
|
-
this.emitter.emit('stackChange', { canUndo: this.canUndo, canRedo: this.canRedo });
|
|
63
|
-
}
|
|
64
|
-
onStackChange(handler) {
|
|
65
|
-
this.emitter.on('stackChange', handler);
|
|
66
|
-
return () => this.emitter.off('stackChange', handler);
|
|
67
|
-
}
|
|
68
|
-
destroy() {
|
|
69
|
-
this.emitter.removeAllListeners();
|
|
70
|
-
}
|
|
71
|
-
}
|