@alaarab/ogrid-js 2.0.22 → 2.1.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/esm/OGrid.js +189 -503
- package/dist/esm/OGridEventWiring.js +178 -0
- package/dist/esm/OGridRendering.js +269 -0
- package/dist/esm/components/ColumnChooser.js +26 -3
- package/dist/esm/components/InlineCellEditor.js +18 -36
- package/dist/esm/index.js +2 -0
- package/dist/esm/renderer/TableRenderer.js +102 -61
- package/dist/esm/state/ClipboardState.js +8 -54
- package/dist/esm/state/ColumnPinningState.js +1 -2
- package/dist/esm/state/ColumnReorderState.js +8 -1
- package/dist/esm/state/EventEmitter.js +3 -2
- package/dist/esm/state/FillHandleState.js +27 -41
- package/dist/esm/state/GridState.js +36 -10
- package/dist/esm/state/HeaderFilterState.js +19 -11
- package/dist/esm/state/KeyboardNavState.js +19 -132
- package/dist/esm/state/RowSelectionState.js +6 -15
- package/dist/esm/state/SideBarState.js +1 -1
- package/dist/esm/state/TableLayoutState.js +6 -4
- package/dist/esm/utils/getCellCoordinates.js +15 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/types/OGrid.d.ts +97 -9
- package/dist/types/OGridEventWiring.d.ts +60 -0
- package/dist/types/OGridRendering.d.ts +93 -0
- package/dist/types/components/ColumnChooser.d.ts +5 -0
- package/dist/types/components/InlineCellEditor.d.ts +5 -0
- package/dist/types/index.d.ts +6 -1
- package/dist/types/renderer/TableRenderer.d.ts +12 -5
- package/dist/types/state/EventEmitter.d.ts +1 -1
- package/dist/types/state/FillHandleState.d.ts +1 -1
- package/dist/types/state/GridState.d.ts +7 -1
- package/dist/types/state/HeaderFilterState.d.ts +2 -0
- package/dist/types/types/gridTypes.d.ts +15 -0
- package/dist/types/utils/getCellCoordinates.d.ts +8 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/package.json +11 -4
|
@@ -39,6 +39,10 @@ export class HeaderFilterState {
|
|
|
39
39
|
setFilterOptions(options) {
|
|
40
40
|
this._filterOptions = options;
|
|
41
41
|
}
|
|
42
|
+
/** Allow OGrid to update the popover element reference after rendering (for click-outside detection). */
|
|
43
|
+
setPopoverEl(el) {
|
|
44
|
+
this._popoverEl = el;
|
|
45
|
+
}
|
|
42
46
|
getFilterOptions(filterField) {
|
|
43
47
|
return this._filterOptions[filterField] ?? [];
|
|
44
48
|
}
|
|
@@ -107,10 +111,14 @@ export class HeaderFilterState {
|
|
|
107
111
|
}
|
|
108
112
|
};
|
|
109
113
|
setTimeout(() => {
|
|
110
|
-
|
|
114
|
+
if (this._clickOutsideHandler) {
|
|
115
|
+
document.addEventListener('mousedown', this._clickOutsideHandler, { passive: true });
|
|
116
|
+
}
|
|
111
117
|
}, 0);
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
if (this._escapeHandler) {
|
|
119
|
+
document.addEventListener('keydown', this._escapeHandler, true);
|
|
120
|
+
}
|
|
121
|
+
this.emitter.emit('change');
|
|
114
122
|
}
|
|
115
123
|
close() {
|
|
116
124
|
this._openColumnId = null;
|
|
@@ -125,24 +133,24 @@ export class HeaderFilterState {
|
|
|
125
133
|
document.removeEventListener('keydown', this._escapeHandler, true);
|
|
126
134
|
this._escapeHandler = null;
|
|
127
135
|
}
|
|
128
|
-
this.emitter.emit('change'
|
|
136
|
+
this.emitter.emit('change');
|
|
129
137
|
}
|
|
130
138
|
// --- Temp state setters ---
|
|
131
139
|
setTempTextValue(v) {
|
|
132
140
|
this._tempTextValue = v;
|
|
133
|
-
this.emitter.emit('change'
|
|
141
|
+
this.emitter.emit('change');
|
|
134
142
|
}
|
|
135
143
|
setSearchText(v) {
|
|
136
144
|
this._searchText = v;
|
|
137
|
-
this.emitter.emit('change'
|
|
145
|
+
this.emitter.emit('change');
|
|
138
146
|
}
|
|
139
147
|
setTempDateFrom(v) {
|
|
140
148
|
this._tempDateFrom = v;
|
|
141
|
-
this.emitter.emit('change'
|
|
149
|
+
this.emitter.emit('change');
|
|
142
150
|
}
|
|
143
151
|
setTempDateTo(v) {
|
|
144
152
|
this._tempDateTo = v;
|
|
145
|
-
this.emitter.emit('change'
|
|
153
|
+
this.emitter.emit('change');
|
|
146
154
|
}
|
|
147
155
|
// --- Checkbox handlers ---
|
|
148
156
|
handleCheckboxChange(option, checked) {
|
|
@@ -152,15 +160,15 @@ export class HeaderFilterState {
|
|
|
152
160
|
else
|
|
153
161
|
next.delete(option);
|
|
154
162
|
this._tempSelected = next;
|
|
155
|
-
this.emitter.emit('change'
|
|
163
|
+
this.emitter.emit('change');
|
|
156
164
|
}
|
|
157
165
|
handleSelectAll(filterField) {
|
|
158
166
|
this._tempSelected = new Set(this.getFilterOptions(filterField));
|
|
159
|
-
this.emitter.emit('change'
|
|
167
|
+
this.emitter.emit('change');
|
|
160
168
|
}
|
|
161
169
|
handleClearSelection() {
|
|
162
170
|
this._tempSelected = new Set();
|
|
163
|
-
this.emitter.emit('change'
|
|
171
|
+
this.emitter.emit('change');
|
|
164
172
|
}
|
|
165
173
|
// --- Apply/Clear ---
|
|
166
174
|
applyTextFilter(filterField) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { parseValue } from '@alaarab/ogrid-core';
|
|
1
|
+
import { getCellValue, computeTabNavigation, computeArrowNavigation, applyCellDeletion } from '@alaarab/ogrid-core';
|
|
3
2
|
export class KeyboardNavState {
|
|
4
3
|
constructor(params, getActiveCell, getSelectionRange, setActiveCell, setSelectionRange) {
|
|
5
4
|
this.wrapperRef = null;
|
|
@@ -46,114 +45,23 @@ export class KeyboardNavState {
|
|
|
46
45
|
void onPaste?.();
|
|
47
46
|
}
|
|
48
47
|
break;
|
|
49
|
-
case 'ArrowDown':
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const newRow = ctrl
|
|
53
|
-
? findCtrlTarget(rowIndex, maxRowIndex, 1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
|
|
54
|
-
: Math.min(rowIndex + 1, maxRowIndex);
|
|
55
|
-
this.setActiveCell({ rowIndex: newRow, columnIndex });
|
|
56
|
-
if (shift) {
|
|
57
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
58
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
59
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
60
|
-
endRow: newRow,
|
|
61
|
-
endCol: selectionRange?.endCol ?? dataColIndex,
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
this.setSelectionRange({
|
|
66
|
-
startRow: newRow,
|
|
67
|
-
startCol: dataColIndex,
|
|
68
|
-
endRow: newRow,
|
|
69
|
-
endCol: dataColIndex,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
case 'ArrowUp': {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
const ctrl = e.ctrlKey || e.metaKey;
|
|
77
|
-
const newRowUp = ctrl
|
|
78
|
-
? findCtrlTarget(rowIndex, 0, -1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
|
|
79
|
-
: Math.max(rowIndex - 1, 0);
|
|
80
|
-
this.setActiveCell({ rowIndex: newRowUp, columnIndex });
|
|
81
|
-
if (shift) {
|
|
82
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
83
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
84
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
85
|
-
endRow: newRowUp,
|
|
86
|
-
endCol: selectionRange?.endCol ?? dataColIndex,
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
this.setSelectionRange({
|
|
91
|
-
startRow: newRowUp,
|
|
92
|
-
startCol: dataColIndex,
|
|
93
|
-
endRow: newRowUp,
|
|
94
|
-
endCol: dataColIndex,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case 'ArrowRight': {
|
|
100
|
-
e.preventDefault();
|
|
101
|
-
const ctrl = e.ctrlKey || e.metaKey;
|
|
102
|
-
let newCol;
|
|
103
|
-
if (ctrl && dataColIndex >= 0) {
|
|
104
|
-
newCol = findCtrlTarget(dataColIndex, visibleCols.length - 1, 1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
newCol = Math.min(columnIndex + 1, maxColIndex);
|
|
108
|
-
}
|
|
109
|
-
const newDataCol = newCol - colOffset;
|
|
110
|
-
this.setActiveCell({ rowIndex, columnIndex: newCol });
|
|
111
|
-
if (shift) {
|
|
112
|
-
this.setSelectionRange(normalizeSelectionRange({
|
|
113
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
114
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
115
|
-
endRow: selectionRange?.endRow ?? rowIndex,
|
|
116
|
-
endCol: newDataCol,
|
|
117
|
-
}));
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
this.setSelectionRange({
|
|
121
|
-
startRow: rowIndex,
|
|
122
|
-
startCol: newDataCol,
|
|
123
|
-
endRow: rowIndex,
|
|
124
|
-
endCol: newDataCol,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
48
|
+
case 'ArrowDown':
|
|
49
|
+
case 'ArrowUp':
|
|
50
|
+
case 'ArrowRight':
|
|
129
51
|
case 'ArrowLeft': {
|
|
130
52
|
e.preventDefault();
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
startRow: selectionRange?.startRow ?? rowIndex,
|
|
144
|
-
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
145
|
-
endRow: selectionRange?.endRow ?? rowIndex,
|
|
146
|
-
endCol: newDataColLeft,
|
|
147
|
-
}));
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
this.setSelectionRange({
|
|
151
|
-
startRow: rowIndex,
|
|
152
|
-
startCol: newDataColLeft,
|
|
153
|
-
endRow: rowIndex,
|
|
154
|
-
endCol: newDataColLeft,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
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);
|
|
157
65
|
break;
|
|
158
66
|
}
|
|
159
67
|
case 'Tab': {
|
|
@@ -265,30 +173,9 @@ export class KeyboardNavState {
|
|
|
265
173
|
if (range == null)
|
|
266
174
|
break;
|
|
267
175
|
e.preventDefault();
|
|
268
|
-
const
|
|
269
|
-
for (
|
|
270
|
-
|
|
271
|
-
if (r >= items.length || c >= visibleCols.length)
|
|
272
|
-
continue;
|
|
273
|
-
const item = items[r];
|
|
274
|
-
const col = visibleCols[c];
|
|
275
|
-
const colEditable = col.editable === true ||
|
|
276
|
-
(typeof col.editable === 'function' && col.editable(item));
|
|
277
|
-
if (!colEditable)
|
|
278
|
-
continue;
|
|
279
|
-
const oldValue = getCellValue(item, col);
|
|
280
|
-
const result = parseValue('', oldValue, item, col);
|
|
281
|
-
if (!result.valid)
|
|
282
|
-
continue;
|
|
283
|
-
onCellValueChanged({
|
|
284
|
-
item,
|
|
285
|
-
columnId: col.columnId,
|
|
286
|
-
oldValue,
|
|
287
|
-
newValue: result.value,
|
|
288
|
-
rowIndex: r,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
176
|
+
const deleteEvents = applyCellDeletion(range, items, visibleCols);
|
|
177
|
+
for (const evt of deleteEvents)
|
|
178
|
+
onCellValueChanged(evt);
|
|
292
179
|
break;
|
|
293
180
|
}
|
|
294
181
|
case 'F10':
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyRangeRowSelection, computeRowSelectionState } from '@alaarab/ogrid-core';
|
|
1
2
|
import { EventEmitter } from './EventEmitter';
|
|
2
3
|
/**
|
|
3
4
|
* Manages row selection state for single or multiple selection modes with shift-click range support.
|
|
@@ -30,21 +31,12 @@ export class RowSelectionState {
|
|
|
30
31
|
this._lastClickedRow = rowIndex;
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
let next;
|
|
34
35
|
if (shiftKey && this._lastClickedRow >= 0 && this._lastClickedRow !== rowIndex) {
|
|
35
|
-
|
|
36
|
-
const end = Math.max(this._lastClickedRow, rowIndex);
|
|
37
|
-
for (let i = start; i <= end; i++) {
|
|
38
|
-
if (i < items.length) {
|
|
39
|
-
const id = this._getRowId(items[i]);
|
|
40
|
-
if (checked)
|
|
41
|
-
next.add(id);
|
|
42
|
-
else
|
|
43
|
-
next.delete(id);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
36
|
+
next = applyRangeRowSelection(this._lastClickedRow, rowIndex, checked, items, this._getRowId, this._selectedRowIds);
|
|
46
37
|
}
|
|
47
38
|
else {
|
|
39
|
+
next = new Set(this._selectedRowIds);
|
|
48
40
|
if (checked)
|
|
49
41
|
next.add(rowId);
|
|
50
42
|
else
|
|
@@ -62,11 +54,10 @@ export class RowSelectionState {
|
|
|
62
54
|
}
|
|
63
55
|
}
|
|
64
56
|
isAllSelected(items) {
|
|
65
|
-
return
|
|
57
|
+
return computeRowSelectionState(this._selectedRowIds, items, this._getRowId).allSelected;
|
|
66
58
|
}
|
|
67
59
|
isSomeSelected(items) {
|
|
68
|
-
|
|
69
|
-
return !allSelected && items.some((item) => this._selectedRowIds.has(this._getRowId(item)));
|
|
60
|
+
return computeRowSelectionState(this._selectedRowIds, items, this._getRowId).someSelected;
|
|
70
61
|
}
|
|
71
62
|
getSelectedRows(items) {
|
|
72
63
|
return items.filter((item) => this._selectedRowIds.has(this._getRowId(item)));
|
|
@@ -23,7 +23,7 @@ export class SideBarState {
|
|
|
23
23
|
get isOpen() { return this._activePanel !== null; }
|
|
24
24
|
setActivePanel(panel) {
|
|
25
25
|
this._activePanel = panel;
|
|
26
|
-
this.emitter.emit('change'
|
|
26
|
+
this.emitter.emit('change');
|
|
27
27
|
}
|
|
28
28
|
toggle(panel) {
|
|
29
29
|
this.setActivePanel(this._activePanel === panel ? null : panel);
|
|
@@ -67,11 +67,13 @@ export class TableLayoutState {
|
|
|
67
67
|
}
|
|
68
68
|
/** Remove overrides for columns that no longer exist. */
|
|
69
69
|
cleanupOverrides(validColumnIds) {
|
|
70
|
+
const next = {};
|
|
70
71
|
let changed = false;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
for (const [key, value] of Object.entries(this._columnSizingOverrides)) {
|
|
73
|
+
if (validColumnIds.has(key)) {
|
|
74
|
+
next[key] = value;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
75
77
|
changed = true;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts row and column indices from a cell element's data attributes.
|
|
3
|
+
* Returns null if the element lacks the attributes or they are not valid numbers.
|
|
4
|
+
*/
|
|
5
|
+
export function getCellCoordinates(cell) {
|
|
6
|
+
const rowStr = cell.getAttribute('data-row-index');
|
|
7
|
+
const colStr = cell.getAttribute('data-col-index');
|
|
8
|
+
if (rowStr == null || colStr == null)
|
|
9
|
+
return null;
|
|
10
|
+
const rowIndex = parseInt(rowStr, 10);
|
|
11
|
+
const colIndex = parseInt(colStr, 10);
|
|
12
|
+
if (Number.isNaN(rowIndex) || Number.isNaN(colIndex))
|
|
13
|
+
return null;
|
|
14
|
+
return { rowIndex, colIndex };
|
|
15
|
+
}
|
package/dist/esm/utils/index.js
CHANGED
package/dist/types/OGrid.d.ts
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module OGrid (Vanilla JS)
|
|
3
|
+
*
|
|
4
|
+
* Entry point for the vanilla JS data grid. Full feature parity with the React
|
|
5
|
+
* package, implemented as class-based state objects wired together by EventEmitter.
|
|
6
|
+
*
|
|
7
|
+
* ## Architecture
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* OGrid (orchestrator)
|
|
11
|
+
* |
|
|
12
|
+
* |-- GridState Core data state (sorting, filtering, pagination, columns)
|
|
13
|
+
* | \-- EventEmitter emits 'stateChange' --> OGrid.renderAll()
|
|
14
|
+
* |
|
|
15
|
+
* |-- TableRenderer DOM rendering (<table>, <thead>, <tbody>, pinning, selection CSS)
|
|
16
|
+
* | \-- reads GridState + InteractionState to build/patch DOM
|
|
17
|
+
* |
|
|
18
|
+
* +-- Interaction States (created if cellSelection or editable enabled)
|
|
19
|
+
* | |-- SelectionState Active cell + range selection + drag selection (RAF)
|
|
20
|
+
* | |-- KeyboardNavState Arrow/Tab/Home/End/Enter/Delete key handling
|
|
21
|
+
* | |-- ClipboardState Copy/Cut/Paste with TSV clipboard format
|
|
22
|
+
* | |-- UndoRedoState Edit history stack with batch support
|
|
23
|
+
* | |-- FillHandleState Drag-to-fill (Excel-style) with RAF + batch undo
|
|
24
|
+
* | |-- RowSelectionState Checkbox row selection (single/multiple, shift-range)
|
|
25
|
+
* | |-- ColumnResizeState Drag column borders to resize
|
|
26
|
+
* | |-- ColumnPinningState Sticky left/right column positioning
|
|
27
|
+
* | |-- ColumnReorderState Drag-to-reorder columns
|
|
28
|
+
* | \-- VirtualScrollState Windowed row rendering with overscan
|
|
29
|
+
* |
|
|
30
|
+
* +-- Layout & Filter States (always active)
|
|
31
|
+
* | |-- TableLayoutState ResizeObserver container measurement, column width overrides
|
|
32
|
+
* | |-- HeaderFilterState Per-column filter popover state (text/multiSelect/date)
|
|
33
|
+
* | \-- SideBarState Panel management (columns, filters), position (left/right)
|
|
34
|
+
* |
|
|
35
|
+
* +-- UI Components
|
|
36
|
+
* |-- PaginationControls Page navigation with page size dropdown
|
|
37
|
+
* |-- StatusBar Row count, filtered count
|
|
38
|
+
* |-- ColumnChooser Show/hide columns dropdown in toolbar
|
|
39
|
+
* |-- SideBar Sidebar with columns panel + filters panel
|
|
40
|
+
* |-- HeaderFilter Positioned filter popovers per column
|
|
41
|
+
* |-- InlineCellEditor Text/select/checkbox/date inline editors
|
|
42
|
+
* |-- ContextMenu Right-click menu (copy/cut/paste/undo/redo/select all)
|
|
43
|
+
* \-- MarchingAntsOverlay SVG animated copy/cut selection border
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* ## Event Flow
|
|
47
|
+
*
|
|
48
|
+
* Each state class owns a private EventEmitter<TEvents>. State mutations emit
|
|
49
|
+
* typed events that OGrid subscribes to during construction. The general flow:
|
|
50
|
+
*
|
|
51
|
+
* ```
|
|
52
|
+
* User action (click, keydown, drag, etc.)
|
|
53
|
+
* --> State class mutates internal state, emits event
|
|
54
|
+
* --> OGrid subscription handler fires
|
|
55
|
+
* --> updateRendererInteractionState() or renderAll()
|
|
56
|
+
* --> TableRenderer.update() rebuilds or patches DOM
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* For performance-critical paths (drag selection, fill handle, column resize),
|
|
60
|
+
* state classes throttle updates via requestAnimationFrame and use cached
|
|
61
|
+
* querySelectorAll results to avoid repeated DOM queries.
|
|
62
|
+
*
|
|
63
|
+
* ## Rendering Pipeline
|
|
64
|
+
*
|
|
65
|
+
* TableRenderer has two update paths:
|
|
66
|
+
* 1. Full rebuild -- renderHeader() + renderBody() when data, columns, or
|
|
67
|
+
* sorting/filtering changes (triggered by GridState 'stateChange').
|
|
68
|
+
* 2. CSS-only patch -- patchSelectionClasses() when only active cell,
|
|
69
|
+
* selection range, copy/cut range, or fill handle position changed.
|
|
70
|
+
* Uses a signature-based diff (isSelectionOnlyChange) to skip DOM rebuild.
|
|
71
|
+
*
|
|
72
|
+
* ## Lifecycle
|
|
73
|
+
*
|
|
74
|
+
* 1. Constructor: build DOM layout (toolbar, body area, table container,
|
|
75
|
+
* status bar, pagination). Create GridState + TableRenderer.
|
|
76
|
+
* 2. Initialize: header filters, sidebar, column pinning, row selection.
|
|
77
|
+
* 3. Initial render: TableRenderer.render() creates <table>/<thead>/<tbody>.
|
|
78
|
+
* 4. Interaction init: if cellSelection/editable, create SelectionState,
|
|
79
|
+
* KeyboardNavState, ClipboardState, UndoRedoState, FillHandleState,
|
|
80
|
+
* ColumnResizeState, ColumnReorderState. Attach global mouse handlers.
|
|
81
|
+
* 5. Event wiring: subscribe to all state emitters. Each fires renderAll()
|
|
82
|
+
* or updateRendererInteractionState() as appropriate.
|
|
83
|
+
* 6. Destroy: unsubscribe all listeners, destroy all state + components,
|
|
84
|
+
* remove container from DOM.
|
|
85
|
+
*
|
|
86
|
+
* ## Public API
|
|
87
|
+
*
|
|
88
|
+
* - `api: IJsOGridApi<T>` -- imperative grid API (setRowData, getSelectedRows,
|
|
89
|
+
* exportToCsv, scrollToRow, etc.), created by GridState.getApi() and
|
|
90
|
+
* extended by OGrid for row selection and virtual scroll methods.
|
|
91
|
+
* - `on(event, handler)` / `off(event, handler)` -- external event subscription.
|
|
92
|
+
* - `destroy()` -- full cleanup.
|
|
93
|
+
*/
|
|
1
94
|
import type { OGridOptions, OGridEvents, IJsOGridApi } from './types/gridTypes';
|
|
2
95
|
export declare class OGrid<T> {
|
|
3
96
|
private state;
|
|
@@ -35,14 +128,13 @@ export declare class OGrid<T> {
|
|
|
35
128
|
private paginationContainer;
|
|
36
129
|
private statusBarContainer;
|
|
37
130
|
private options;
|
|
38
|
-
private
|
|
131
|
+
private renderingHelper;
|
|
132
|
+
private eventWiringHelper;
|
|
39
133
|
/** The imperative grid API (extends React's IOGridApi with JS-specific methods). */
|
|
40
134
|
readonly api: IJsOGridApi<T>;
|
|
41
135
|
constructor(container: HTMLElement, options: OGridOptions<T>);
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
private updateRendererInteractionState;
|
|
45
|
-
private updateDragAttributes;
|
|
136
|
+
/** Creates the OGridRenderingContext that bridges OGrid state to the rendering helper. */
|
|
137
|
+
private createRenderingHelper;
|
|
46
138
|
private handleCellClick;
|
|
47
139
|
private handleCellMouseDown;
|
|
48
140
|
private handleCellContextMenu;
|
|
@@ -50,10 +142,6 @@ export declare class OGrid<T> {
|
|
|
50
142
|
private startCellEdit;
|
|
51
143
|
private buildFilterConfigs;
|
|
52
144
|
private handleFilterIconClick;
|
|
53
|
-
private renderHeaderFilterPopover;
|
|
54
|
-
private renderSideBar;
|
|
55
|
-
private renderLoadingOverlay;
|
|
56
|
-
private renderAll;
|
|
57
145
|
/** Subscribe to grid events. */
|
|
58
146
|
on<K extends keyof OGridEvents<T>>(event: K, handler: (data: OGridEvents<T>[K]) => void): void;
|
|
59
147
|
/** Unsubscribe from grid events. */
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event wiring helper for OGrid (Vanilla JS).
|
|
3
|
+
*
|
|
4
|
+
* Extracts event subscription setup from OGrid for modularity:
|
|
5
|
+
* - initializeInteraction() — creates interaction states and subscribes events
|
|
6
|
+
* - attachGlobalHandlers() — global mouse handlers for resize and drag
|
|
7
|
+
*
|
|
8
|
+
* Not exported publicly — instantiated and owned by OGrid.
|
|
9
|
+
*/
|
|
10
|
+
import type { OGridOptions } from './types/gridTypes';
|
|
11
|
+
import type { GridState } from './state/GridState';
|
|
12
|
+
import type { TableRenderer } from './renderer/TableRenderer';
|
|
13
|
+
import { SelectionState } from './state/SelectionState';
|
|
14
|
+
import { KeyboardNavState } from './state/KeyboardNavState';
|
|
15
|
+
import { ClipboardState } from './state/ClipboardState';
|
|
16
|
+
import { UndoRedoState } from './state/UndoRedoState';
|
|
17
|
+
import { ColumnResizeState } from './state/ColumnResizeState';
|
|
18
|
+
import { FillHandleState } from './state/FillHandleState';
|
|
19
|
+
import { ColumnReorderState } from './state/ColumnReorderState';
|
|
20
|
+
import { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
|
|
21
|
+
import { InlineCellEditor } from './components/InlineCellEditor';
|
|
22
|
+
import { ContextMenu } from './components/ContextMenu';
|
|
23
|
+
import type { RowSelectionState } from './state/RowSelectionState';
|
|
24
|
+
import type { TableLayoutState } from './state/TableLayoutState';
|
|
25
|
+
import type { ColumnPinningState } from './state/ColumnPinningState';
|
|
26
|
+
import type { RowId } from '@alaarab/ogrid-core';
|
|
27
|
+
/**
|
|
28
|
+
* Result of initializeInteraction — the created state objects and subscriptions.
|
|
29
|
+
*/
|
|
30
|
+
export interface InteractionResult<T> {
|
|
31
|
+
selectionState: SelectionState;
|
|
32
|
+
keyboardNavState: KeyboardNavState<T>;
|
|
33
|
+
clipboardState: ClipboardState<T>;
|
|
34
|
+
undoRedoState: UndoRedoState<T>;
|
|
35
|
+
resizeState: ColumnResizeState;
|
|
36
|
+
fillHandleState: FillHandleState<T>;
|
|
37
|
+
reorderState: ColumnReorderState;
|
|
38
|
+
marchingAnts: MarchingAntsOverlay | null;
|
|
39
|
+
cellEditor: InlineCellEditor<T>;
|
|
40
|
+
contextMenu: ContextMenu;
|
|
41
|
+
unsubscribes: (() => void)[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Callbacks from OGrid needed by the event wiring.
|
|
45
|
+
*/
|
|
46
|
+
export interface EventWiringCallbacks<_T> {
|
|
47
|
+
updateRendererInteractionState: () => void;
|
|
48
|
+
updateDragAttributes: () => void;
|
|
49
|
+
clearCachedDragCells: () => void;
|
|
50
|
+
showContextMenu: (x: number, y: number) => void;
|
|
51
|
+
startCellEdit: (rowId: RowId, columnId: string) => void;
|
|
52
|
+
}
|
|
53
|
+
export declare class OGridEventWiring<T> {
|
|
54
|
+
/**
|
|
55
|
+
* Creates all interaction states, subscribes to their events, and returns
|
|
56
|
+
* the state objects so OGrid can store them.
|
|
57
|
+
*/
|
|
58
|
+
initializeInteraction(options: OGridOptions<T>, state: GridState<T>, renderer: TableRenderer<T>, tableContainer: HTMLElement, layoutState: TableLayoutState, rowSelectionState: RowSelectionState<T> | null, pinningState: ColumnPinningState | null, callbacks: EventWiringCallbacks<T>): InteractionResult<T>;
|
|
59
|
+
private attachGlobalHandlers;
|
|
60
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering helper for OGrid (Vanilla JS).
|
|
3
|
+
*
|
|
4
|
+
* Extracts rendering logic from OGrid for modularity:
|
|
5
|
+
* - updateRendererInteractionState()
|
|
6
|
+
* - updateDragAttributes()
|
|
7
|
+
* - renderAll()
|
|
8
|
+
* - renderHeaderFilterPopover()
|
|
9
|
+
* - renderSideBar()
|
|
10
|
+
* - renderLoadingOverlay()
|
|
11
|
+
*
|
|
12
|
+
* Not exported publicly — instantiated and owned by OGrid.
|
|
13
|
+
*/
|
|
14
|
+
import type { OGridOptions } from './types/gridTypes';
|
|
15
|
+
import type { GridState } from './state/GridState';
|
|
16
|
+
import type { TableRenderer } from './renderer/TableRenderer';
|
|
17
|
+
import type { PaginationControls } from './components/PaginationControls';
|
|
18
|
+
import type { StatusBar } from './components/StatusBar';
|
|
19
|
+
import type { ColumnChooser } from './components/ColumnChooser';
|
|
20
|
+
import type { SideBarState } from './state/SideBarState';
|
|
21
|
+
import type { SideBar } from './components/SideBar';
|
|
22
|
+
import type { HeaderFilterState, HeaderFilterConfig } from './state/HeaderFilterState';
|
|
23
|
+
import type { HeaderFilter } from './components/HeaderFilter';
|
|
24
|
+
import type { SelectionState } from './state/SelectionState';
|
|
25
|
+
import type { KeyboardNavState } from './state/KeyboardNavState';
|
|
26
|
+
import type { ClipboardState } from './state/ClipboardState';
|
|
27
|
+
import type { UndoRedoState } from './state/UndoRedoState';
|
|
28
|
+
import type { ColumnResizeState } from './state/ColumnResizeState';
|
|
29
|
+
import type { FillHandleState } from './state/FillHandleState';
|
|
30
|
+
import type { RowSelectionState } from './state/RowSelectionState';
|
|
31
|
+
import type { ColumnPinningState } from './state/ColumnPinningState';
|
|
32
|
+
import type { ColumnReorderState } from './state/ColumnReorderState';
|
|
33
|
+
import type { VirtualScrollState } from './state/VirtualScrollState';
|
|
34
|
+
import type { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
|
|
35
|
+
import type { InlineCellEditor } from './components/InlineCellEditor';
|
|
36
|
+
import type { TableLayoutState } from './state/TableLayoutState';
|
|
37
|
+
/**
|
|
38
|
+
* Context object providing access to OGrid's internal state for rendering.
|
|
39
|
+
* OGrid populates this once during construction and keeps it current.
|
|
40
|
+
*/
|
|
41
|
+
export interface OGridRenderingContext<T> {
|
|
42
|
+
options: OGridOptions<T>;
|
|
43
|
+
state: GridState<T>;
|
|
44
|
+
renderer: TableRenderer<T>;
|
|
45
|
+
pagination: PaginationControls<T>;
|
|
46
|
+
statusBar: StatusBar;
|
|
47
|
+
columnChooser: ColumnChooser<T>;
|
|
48
|
+
layoutState: TableLayoutState;
|
|
49
|
+
tableContainer: HTMLElement;
|
|
50
|
+
selectionState: SelectionState | null;
|
|
51
|
+
keyboardNavState: KeyboardNavState<T> | null;
|
|
52
|
+
clipboardState: ClipboardState<T> | null;
|
|
53
|
+
undoRedoState: UndoRedoState<T> | null;
|
|
54
|
+
resizeState: ColumnResizeState | null;
|
|
55
|
+
fillHandleState: FillHandleState<T> | null;
|
|
56
|
+
rowSelectionState: RowSelectionState<T> | null;
|
|
57
|
+
pinningState: ColumnPinningState | null;
|
|
58
|
+
reorderState: ColumnReorderState | null;
|
|
59
|
+
virtualScrollState: VirtualScrollState | null;
|
|
60
|
+
marchingAnts: MarchingAntsOverlay | null;
|
|
61
|
+
cellEditor: InlineCellEditor<T> | null;
|
|
62
|
+
sideBarState: SideBarState | null;
|
|
63
|
+
sideBarComponent: SideBar | null;
|
|
64
|
+
headerFilterState: HeaderFilterState;
|
|
65
|
+
headerFilterComponent: HeaderFilter;
|
|
66
|
+
filterConfigs: Map<string, HeaderFilterConfig>;
|
|
67
|
+
loadingOverlay: HTMLElement | null;
|
|
68
|
+
setLoadingOverlay: (el: HTMLElement | null) => void;
|
|
69
|
+
handleCellClick: (rowIndex: number, colIndex: number) => void;
|
|
70
|
+
handleCellMouseDown: (rowIndex: number, colIndex: number, e: MouseEvent) => void;
|
|
71
|
+
handleCellContextMenu: (rowIndex: number, colIndex: number, e: MouseEvent) => void;
|
|
72
|
+
startCellEdit: (rowId: import('@alaarab/ogrid-core').RowId, columnId: string) => void;
|
|
73
|
+
showContextMenu: (x: number, y: number) => void;
|
|
74
|
+
}
|
|
75
|
+
export declare class OGridRendering<T> {
|
|
76
|
+
private ctx;
|
|
77
|
+
private layoutVersion;
|
|
78
|
+
/** Cached DOM cells during drag to avoid querySelectorAll on every RAF frame. */
|
|
79
|
+
private cachedDragCells;
|
|
80
|
+
constructor(ctx: OGridRenderingContext<T>);
|
|
81
|
+
/** Increment layout version (e.g., when items, columns, sizing change). */
|
|
82
|
+
incrementLayoutVersion(): void;
|
|
83
|
+
/** Clear cached drag cells. */
|
|
84
|
+
clearCachedDragCells(): void;
|
|
85
|
+
/** Get current layout version. */
|
|
86
|
+
getLayoutVersion(): number;
|
|
87
|
+
updateRendererInteractionState(): void;
|
|
88
|
+
updateDragAttributes(): void;
|
|
89
|
+
renderAll(): void;
|
|
90
|
+
renderHeaderFilterPopover(): void;
|
|
91
|
+
renderSideBar(): void;
|
|
92
|
+
renderLoadingOverlay(): void;
|
|
93
|
+
}
|
|
@@ -5,8 +5,13 @@ export declare class ColumnChooser<T> {
|
|
|
5
5
|
private el;
|
|
6
6
|
private dropdown;
|
|
7
7
|
private isOpen;
|
|
8
|
+
private initialized;
|
|
8
9
|
constructor(container: HTMLElement, state: GridState<T>);
|
|
9
10
|
render(): void;
|
|
11
|
+
/** Initial DOM creation — called once. */
|
|
12
|
+
private createDOM;
|
|
13
|
+
/** Update checkbox checked states without destroying the dropdown. */
|
|
14
|
+
private updateDropdownState;
|
|
10
15
|
private toggle;
|
|
11
16
|
private open;
|
|
12
17
|
private close;
|
|
@@ -17,6 +17,11 @@ export declare class InlineCellEditor<T> {
|
|
|
17
17
|
} | null;
|
|
18
18
|
closeEditor(): void;
|
|
19
19
|
private createEditor;
|
|
20
|
+
/**
|
|
21
|
+
* Shared factory for text/date input editors — both types have identical event handling,
|
|
22
|
+
* differing only in input.type and initial value formatting.
|
|
23
|
+
*/
|
|
24
|
+
private createInputEditor;
|
|
20
25
|
private createTextEditor;
|
|
21
26
|
private createCheckboxEditor;
|
|
22
27
|
private createDateEditor;
|