@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.
Files changed (35) hide show
  1. package/dist/esm/OGrid.js +189 -503
  2. package/dist/esm/OGridEventWiring.js +178 -0
  3. package/dist/esm/OGridRendering.js +269 -0
  4. package/dist/esm/components/ColumnChooser.js +26 -3
  5. package/dist/esm/components/InlineCellEditor.js +18 -36
  6. package/dist/esm/index.js +2 -0
  7. package/dist/esm/renderer/TableRenderer.js +102 -61
  8. package/dist/esm/state/ClipboardState.js +8 -54
  9. package/dist/esm/state/ColumnPinningState.js +1 -2
  10. package/dist/esm/state/ColumnReorderState.js +8 -1
  11. package/dist/esm/state/EventEmitter.js +3 -2
  12. package/dist/esm/state/FillHandleState.js +27 -41
  13. package/dist/esm/state/GridState.js +36 -10
  14. package/dist/esm/state/HeaderFilterState.js +19 -11
  15. package/dist/esm/state/KeyboardNavState.js +19 -132
  16. package/dist/esm/state/RowSelectionState.js +6 -15
  17. package/dist/esm/state/SideBarState.js +1 -1
  18. package/dist/esm/state/TableLayoutState.js +6 -4
  19. package/dist/esm/utils/getCellCoordinates.js +15 -0
  20. package/dist/esm/utils/index.js +1 -0
  21. package/dist/types/OGrid.d.ts +97 -9
  22. package/dist/types/OGridEventWiring.d.ts +60 -0
  23. package/dist/types/OGridRendering.d.ts +93 -0
  24. package/dist/types/components/ColumnChooser.d.ts +5 -0
  25. package/dist/types/components/InlineCellEditor.d.ts +5 -0
  26. package/dist/types/index.d.ts +6 -1
  27. package/dist/types/renderer/TableRenderer.d.ts +12 -5
  28. package/dist/types/state/EventEmitter.d.ts +1 -1
  29. package/dist/types/state/FillHandleState.d.ts +1 -1
  30. package/dist/types/state/GridState.d.ts +7 -1
  31. package/dist/types/state/HeaderFilterState.d.ts +2 -0
  32. package/dist/types/types/gridTypes.d.ts +15 -0
  33. package/dist/types/utils/getCellCoordinates.d.ts +8 -0
  34. package/dist/types/utils/index.d.ts +1 -0
  35. 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
- document.addEventListener('mousedown', this._clickOutsideHandler, { passive: true });
114
+ if (this._clickOutsideHandler) {
115
+ document.addEventListener('mousedown', this._clickOutsideHandler, { passive: true });
116
+ }
111
117
  }, 0);
112
- document.addEventListener('keydown', this._escapeHandler, true);
113
- this.emitter.emit('change', undefined);
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', undefined);
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', undefined);
141
+ this.emitter.emit('change');
134
142
  }
135
143
  setSearchText(v) {
136
144
  this._searchText = v;
137
- this.emitter.emit('change', undefined);
145
+ this.emitter.emit('change');
138
146
  }
139
147
  setTempDateFrom(v) {
140
148
  this._tempDateFrom = v;
141
- this.emitter.emit('change', undefined);
149
+ this.emitter.emit('change');
142
150
  }
143
151
  setTempDateTo(v) {
144
152
  this._tempDateTo = v;
145
- this.emitter.emit('change', undefined);
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', undefined);
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', undefined);
167
+ this.emitter.emit('change');
160
168
  }
161
169
  handleClearSelection() {
162
170
  this._tempSelected = new Set();
163
- this.emitter.emit('change', undefined);
171
+ this.emitter.emit('change');
164
172
  }
165
173
  // --- Apply/Clear ---
166
174
  applyTextFilter(filterField) {
@@ -1,5 +1,4 @@
1
- import { normalizeSelectionRange, getCellValue, findCtrlArrowTarget as findCtrlTarget, computeTabNavigation } from '@alaarab/ogrid-core';
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
- e.preventDefault();
51
- const ctrl = e.ctrlKey || e.metaKey;
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 ctrl = e.ctrlKey || e.metaKey;
132
- let newColLeft;
133
- if (ctrl && dataColIndex >= 0) {
134
- newColLeft = findCtrlTarget(dataColIndex, 0, -1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
135
- }
136
- else {
137
- newColLeft = Math.max(columnIndex - 1, colOffset);
138
- }
139
- const newDataColLeft = newColLeft - colOffset;
140
- this.setActiveCell({ rowIndex, columnIndex: newColLeft });
141
- if (shift) {
142
- this.setSelectionRange(normalizeSelectionRange({
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 norm = normalizeSelectionRange(range);
269
- for (let r = norm.startRow; r <= norm.endRow; r++) {
270
- for (let c = norm.startCol; c <= norm.endCol; c++) {
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
- const next = new Set(this._selectedRowIds);
34
+ let next;
34
35
  if (shiftKey && this._lastClickedRow >= 0 && this._lastClickedRow !== rowIndex) {
35
- const start = Math.min(this._lastClickedRow, rowIndex);
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 items.length > 0 && items.every((item) => this._selectedRowIds.has(this._getRowId(item)));
57
+ return computeRowSelectionState(this._selectedRowIds, items, this._getRowId).allSelected;
66
58
  }
67
59
  isSomeSelected(items) {
68
- const allSelected = this.isAllSelected(items);
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', undefined);
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 next = { ...this._columnSizingOverrides };
72
- for (const key of Object.keys(next)) {
73
- if (!validColumnIds.has(key)) {
74
- delete next[key];
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
+ }
@@ -1 +1,2 @@
1
1
  export { debounce } from './debounce';
2
+ export { getCellCoordinates } from './getCellCoordinates';
@@ -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 layoutVersion;
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
- private initializeInteraction;
43
- private attachGlobalHandlers;
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;