@alaarab/ogrid-js 2.0.9 → 2.0.12

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 CHANGED
@@ -22,7 +22,90 @@ import { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
22
22
  import { InlineCellEditor } from './components/InlineCellEditor';
23
23
  import { ContextMenu } from './components/ContextMenu';
24
24
  import { EventEmitter } from './state/EventEmitter';
25
- import { normalizeSelectionRange, isInSelectionRange, flattenColumns } from '@alaarab/ogrid-core';
25
+ import { normalizeSelectionRange, isInSelectionRange, flattenColumns, injectGlobalStyles } from '@alaarab/ogrid-core';
26
+ /** CSS variable definitions for light and dark themes (injected once per page). */
27
+ const OGRID_THEME_CSS = `
28
+ :root {
29
+ --ogrid-bg: #ffffff;
30
+ --ogrid-fg: rgba(0, 0, 0, 0.87);
31
+ --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
32
+ --ogrid-fg-muted: rgba(0, 0, 0, 0.5);
33
+ --ogrid-border: rgba(0, 0, 0, 0.12);
34
+ --ogrid-header-bg: rgba(0, 0, 0, 0.04);
35
+ --ogrid-hover-bg: rgba(0, 0, 0, 0.04);
36
+ --ogrid-selected-row-bg: #e6f0fb;
37
+ --ogrid-active-cell-bg: rgba(0, 0, 0, 0.02);
38
+ --ogrid-range-bg: rgba(33, 115, 70, 0.12);
39
+ --ogrid-accent: #0078d4;
40
+ --ogrid-selection-color: #217346;
41
+ --ogrid-loading-overlay: rgba(255, 255, 255, 0.7);
42
+ --ogrid-bg-subtle: #f3f2f1;
43
+ --ogrid-bg-hover: rgba(0, 0, 0, 0.04);
44
+ --ogrid-bg-selected: #e6f0fb;
45
+ --ogrid-bg-selected-hover: #dae8f8;
46
+ --ogrid-bg-range: rgba(33, 115, 70, 0.12);
47
+ --ogrid-muted: rgba(0, 0, 0, 0.5);
48
+ --ogrid-selection: #217346;
49
+ --ogrid-primary: #217346;
50
+ --ogrid-primary-fg: #fff;
51
+ --ogrid-loading-bg: rgba(255, 255, 255, 0.7);
52
+ --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
53
+ }
54
+ [data-theme='dark'] {
55
+ --ogrid-bg: #1e1e1e;
56
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
57
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
58
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
59
+ --ogrid-border: rgba(255, 255, 255, 0.12);
60
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
61
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
62
+ --ogrid-selected-row-bg: #1a3a5c;
63
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
64
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
65
+ --ogrid-accent: #4da6ff;
66
+ --ogrid-selection-color: #2ea043;
67
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
68
+ --ogrid-bg-subtle: #2a2a2a;
69
+ --ogrid-bg-hover: rgba(255, 255, 255, 0.08);
70
+ --ogrid-bg-selected: #1a3a5c;
71
+ --ogrid-bg-selected-hover: #1f426b;
72
+ --ogrid-bg-range: rgba(46, 160, 67, 0.15);
73
+ --ogrid-muted: rgba(255, 255, 255, 0.5);
74
+ --ogrid-selection: #2ea043;
75
+ --ogrid-primary: #2ea043;
76
+ --ogrid-primary-fg: #fff;
77
+ --ogrid-loading-bg: rgba(0, 0, 0, 0.7);
78
+ --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
79
+ }
80
+ @media (prefers-color-scheme: dark) {
81
+ :root:not([data-theme='light']) {
82
+ --ogrid-bg: #1e1e1e;
83
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
84
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
85
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
86
+ --ogrid-border: rgba(255, 255, 255, 0.12);
87
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
88
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
89
+ --ogrid-selected-row-bg: #1a3a5c;
90
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
91
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
92
+ --ogrid-accent: #4da6ff;
93
+ --ogrid-selection-color: #2ea043;
94
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
95
+ --ogrid-bg-subtle: #2a2a2a;
96
+ --ogrid-bg-hover: rgba(255, 255, 255, 0.08);
97
+ --ogrid-bg-selected: #1a3a5c;
98
+ --ogrid-bg-selected-hover: #1f426b;
99
+ --ogrid-bg-range: rgba(46, 160, 67, 0.15);
100
+ --ogrid-muted: rgba(255, 255, 255, 0.5);
101
+ --ogrid-selection: #2ea043;
102
+ --ogrid-primary: #2ea043;
103
+ --ogrid-primary-fg: #fff;
104
+ --ogrid-loading-bg: rgba(0, 0, 0, 0.7);
105
+ --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
106
+ }
107
+ }
108
+ `;
26
109
  export class OGrid {
27
110
  constructor(container, options) {
28
111
  // Sidebar
@@ -50,15 +133,21 @@ export class OGrid {
50
133
  this.contextMenu = null;
51
134
  this.events = new EventEmitter();
52
135
  this.unsubscribes = [];
136
+ this.layoutVersion = 0; // Incremented when items, columns, sizing, or order change
53
137
  this.options = options;
54
138
  this.state = new GridState(options);
55
139
  this.api = this.state.getApi();
140
+ // Inject theme CSS variables (light + dark) once per page
141
+ injectGlobalStyles('ogrid-theme-vars', OGRID_THEME_CSS);
56
142
  // Build layout
57
143
  this.containerEl = document.createElement('div');
58
144
  this.containerEl.className = 'ogrid-container';
59
145
  // Toolbar
60
146
  this.toolbarEl = document.createElement('div');
61
147
  this.toolbarEl.className = 'ogrid-toolbar';
148
+ // Left spacer keeps column chooser on the right via justify-content: space-between
149
+ const toolbarSpacer = document.createElement('div');
150
+ this.toolbarEl.appendChild(toolbarSpacer);
62
151
  this.containerEl.appendChild(this.toolbarEl);
63
152
  // Body area (holds sidebar + table, side by side)
64
153
  this.bodyArea = document.createElement('div');
@@ -384,7 +473,7 @@ export class OGrid {
384
473
  });
385
474
  this.renderer.update();
386
475
  // Update marching ants overlay
387
- this.marchingAnts?.update(this.selectionState.selectionRange, this.clipboardState.copyRange, this.clipboardState.cutRange);
476
+ this.marchingAnts?.update(this.selectionState.selectionRange, this.clipboardState.copyRange, this.clipboardState.cutRange, this.layoutVersion);
388
477
  }
389
478
  updateDragAttributes() {
390
479
  const wrapper = this.renderer.getWrapperElement();
@@ -640,7 +729,7 @@ export class OGrid {
640
729
  this.loadingOverlay.style.display = 'flex';
641
730
  this.loadingOverlay.style.alignItems = 'center';
642
731
  this.loadingOverlay.style.justifyContent = 'center';
643
- this.loadingOverlay.style.background = 'rgba(255,255,255,0.7)';
732
+ this.loadingOverlay.style.background = 'var(--ogrid-loading-overlay, rgba(255, 255, 255, 0.7))';
644
733
  this.loadingOverlay.style.zIndex = '100';
645
734
  const spinner = document.createElement('div');
646
735
  spinner.className = 'ogrid-loading-spinner';
@@ -658,6 +747,8 @@ export class OGrid {
658
747
  }
659
748
  }
660
749
  renderAll() {
750
+ // Increment layout version to trigger marching ants re-measurement
751
+ this.layoutVersion++;
661
752
  const colOffset = this.rowSelectionState ? 1 : 0;
662
753
  // Update header filter state with current filters and options
663
754
  this.headerFilterState.setFilters(this.state.filters);
@@ -26,7 +26,7 @@ export class HeaderFilter {
26
26
  this.popoverEl.style.color = 'var(--ogrid-fg, #242424)';
27
27
  this.popoverEl.style.border = '1px solid var(--ogrid-border, #e0e0e0)';
28
28
  this.popoverEl.style.borderRadius = '4px';
29
- this.popoverEl.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
29
+ this.popoverEl.style.boxShadow = 'var(--ogrid-shadow, 0 2px 8px rgba(0,0,0,0.15))';
30
30
  this.popoverEl.style.padding = '8px';
31
31
  this.popoverEl.style.minWidth = '200px';
32
32
  this.popoverEl.style.maxHeight = '320px';
@@ -243,6 +243,8 @@ export class InlineCellEditor {
243
243
  input.style.outline = 'none';
244
244
  input.style.padding = '4px';
245
245
  input.style.boxSizing = 'border-box';
246
+ input.style.background = 'var(--ogrid-bg, #fff)';
247
+ input.style.color = 'var(--ogrid-fg, rgba(0, 0, 0, 0.87))';
246
248
  wrapper.appendChild(input);
247
249
  const dropdown = document.createElement('div');
248
250
  dropdown.style.position = 'absolute';
@@ -251,8 +253,8 @@ export class InlineCellEditor {
251
253
  dropdown.style.width = '100%';
252
254
  dropdown.style.maxHeight = '200px';
253
255
  dropdown.style.overflowY = 'auto';
254
- dropdown.style.backgroundColor = 'white';
255
- dropdown.style.border = '1px solid #ccc';
256
+ dropdown.style.backgroundColor = 'var(--ogrid-bg, #fff)';
257
+ dropdown.style.border = '1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))';
256
258
  dropdown.style.zIndex = '1001';
257
259
  wrapper.appendChild(dropdown);
258
260
  const values = column.cellEditorParams?.values ?? [];
@@ -273,10 +275,10 @@ export class InlineCellEditor {
273
275
  this.closeEditor();
274
276
  });
275
277
  option.addEventListener('mouseenter', () => {
276
- option.style.backgroundColor = '#f0f0f0';
278
+ option.style.backgroundColor = 'var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04))';
277
279
  });
278
280
  option.addEventListener('mouseleave', () => {
279
- option.style.backgroundColor = 'white';
281
+ option.style.backgroundColor = 'var(--ogrid-bg, #fff)';
280
282
  });
281
283
  dropdown.appendChild(option);
282
284
  }
@@ -1,31 +1,18 @@
1
- /** Inject the @keyframes rule once into <head> (deduplicates across multiple instances). */
2
- function ensureKeyframes() {
3
- if (typeof document === 'undefined')
4
- return;
5
- if (document.getElementById('ogrid-marching-ants-keyframes'))
6
- return;
7
- const style = document.createElement('style');
8
- style.id = 'ogrid-marching-ants-keyframes';
9
- style.textContent =
10
- '@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}';
11
- document.head.appendChild(style);
12
- }
13
- /** Measure the bounding rect of a range within a container. */
1
+ import { injectGlobalStyles, measureRange as measureRangeCore } from '@alaarab/ogrid-core';
2
+ /**
3
+ * Measure the bounding rect of a range within a container, with scroll offsets.
4
+ * This variant adds scroll offsets for the JS implementation's scrollable container.
5
+ */
14
6
  function measureRange(container, range, colOffset) {
15
- const startGlobalCol = range.startCol + colOffset;
16
- const endGlobalCol = range.endCol + colOffset;
17
- const topLeft = container.querySelector(`[data-row-index="${range.startRow}"][data-col-index="${startGlobalCol}"]`);
18
- const bottomRight = container.querySelector(`[data-row-index="${range.endRow}"][data-col-index="${endGlobalCol}"]`);
19
- if (!topLeft || !bottomRight)
7
+ const rect = measureRangeCore(container, range, colOffset);
8
+ if (!rect)
20
9
  return null;
21
- const cRect = container.getBoundingClientRect();
22
- const tlRect = topLeft.getBoundingClientRect();
23
- const brRect = bottomRight.getBoundingClientRect();
10
+ // Add scroll offsets for JS implementation's scrollable container
24
11
  return {
25
- top: tlRect.top - cRect.top + container.scrollTop,
26
- left: tlRect.left - cRect.left + container.scrollLeft,
27
- width: brRect.right - tlRect.left,
28
- height: brRect.bottom - tlRect.top,
12
+ top: rect.top + container.scrollTop,
13
+ left: rect.left + container.scrollLeft,
14
+ width: rect.width,
15
+ height: rect.height,
29
16
  };
30
17
  }
31
18
  function rangesEqual(a, b) {
@@ -51,18 +38,25 @@ export class MarchingAntsOverlay {
51
38
  this.copyRange = null;
52
39
  this.cutRange = null;
53
40
  this.rafHandle = 0;
41
+ this.layoutVersion = 0; // Tracks layout changes to force re-measurement
54
42
  this.container = container;
55
43
  this.colOffset = colOffset;
56
- ensureKeyframes();
44
+ injectGlobalStyles('ogrid-marching-ants-keyframes', '@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}');
57
45
  // The container must be positioned for absolute SVGs
58
46
  const pos = getComputedStyle(container).position;
59
47
  if (pos === 'static' || pos === '') {
60
48
  container.style.position = 'relative';
61
49
  }
62
50
  }
63
- update(selectionRange, copyRange, cutRange) {
64
- // Skip if nothing changed
65
- if (rangesEqual(this.selectionRange, selectionRange) &&
51
+ update(selectionRange, copyRange, cutRange, layoutVersion) {
52
+ // Track layout changes separately from range changes
53
+ const layoutChanged = layoutVersion !== undefined && layoutVersion !== this.layoutVersion;
54
+ if (layoutChanged && layoutVersion !== undefined) {
55
+ this.layoutVersion = layoutVersion;
56
+ }
57
+ // Skip if nothing changed (ranges or layout)
58
+ if (!layoutChanged &&
59
+ rangesEqual(this.selectionRange, selectionRange) &&
66
60
  rangesEqual(this.copyRange, copyRange) &&
67
61
  rangesEqual(this.cutRange, cutRange)) {
68
62
  return;
@@ -110,7 +110,7 @@ export class TableRenderer {
110
110
  }
111
111
  // Background must be set on pinned cells to avoid showing content underneath
112
112
  if (!isHeader) {
113
- el.style.backgroundColor = el.style.backgroundColor || '#fff';
113
+ el.style.backgroundColor = el.style.backgroundColor || 'var(--ogrid-bg, #fff)';
114
114
  }
115
115
  }
116
116
  renderHeader() {
@@ -410,20 +410,20 @@ export class TableRenderer {
410
410
  // Active cell
411
411
  if (activeCell && activeCell.rowIndex === rowIndex && activeCell.columnIndex === globalColIndex) {
412
412
  td.setAttribute('data-active-cell', 'true');
413
- td.style.outline = '2px solid #0078d4';
413
+ td.style.outline = '2px solid var(--ogrid-accent, #0078d4)';
414
414
  }
415
415
  // Selection range
416
416
  if (selectionRange && isInSelectionRange(selectionRange, rowIndex, colIndex)) {
417
417
  td.setAttribute('data-in-range', 'true');
418
- td.style.backgroundColor = '#e3f2fd';
418
+ td.style.backgroundColor = 'var(--ogrid-range-bg, rgba(33, 115, 70, 0.12))';
419
419
  }
420
420
  // Copy range
421
421
  if (copyRange && isInSelectionRange(copyRange, rowIndex, colIndex)) {
422
- td.style.outline = '1px dashed #666';
422
+ td.style.outline = '1px dashed var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5))';
423
423
  }
424
424
  // Cut range
425
425
  if (cutRange && isInSelectionRange(cutRange, rowIndex, colIndex)) {
426
- td.style.outline = '1px dashed #d32f2f';
426
+ td.style.outline = '1px dashed var(--ogrid-accent, #0078d4)';
427
427
  }
428
428
  // Editing cell (hide content, editor overlay will be shown)
429
429
  if (editingCell && editingCell.rowId === rowId && editingCell.columnId === col.columnId) {
@@ -7,23 +7,31 @@
7
7
  /* ── Light Theme (default) ── */
8
8
  :root {
9
9
  --ogrid-bg: #ffffff;
10
- --ogrid-fg: #242424;
11
- --ogrid-border: #e0e0e0;
10
+ --ogrid-fg: rgba(0, 0, 0, 0.87);
11
+ --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
12
+ --ogrid-fg-muted: rgba(0, 0, 0, 0.5);
13
+ --ogrid-border: rgba(0, 0, 0, 0.12);
14
+ --ogrid-header-bg: rgba(0, 0, 0, 0.04);
15
+ --ogrid-hover-bg: rgba(0, 0, 0, 0.04);
16
+ --ogrid-selected-row-bg: #e6f0fb;
17
+ --ogrid-active-cell-bg: rgba(0, 0, 0, 0.02);
18
+ --ogrid-range-bg: rgba(33, 115, 70, 0.12);
19
+ --ogrid-accent: #0078d4;
20
+ --ogrid-selection-color: #217346;
21
+ --ogrid-loading-overlay: rgba(255, 255, 255, 0.7);
22
+ /* Aliases for backward compat */
12
23
  --ogrid-border-strong: #888;
13
24
  --ogrid-border-hover: #999;
14
25
  --ogrid-bg-subtle: #f3f2f1;
15
- --ogrid-bg-hover: #f5f5f5;
26
+ --ogrid-bg-hover: rgba(0, 0, 0, 0.04);
16
27
  --ogrid-bg-selected: #e6f0fb;
17
28
  --ogrid-bg-selected-hover: #dae8f8;
18
29
  --ogrid-bg-range: rgba(33, 115, 70, 0.12);
19
- --ogrid-header-bg: #f5f5f5;
20
- --ogrid-muted: #616161;
21
- --ogrid-fg-muted: rgba(0, 0, 0, 0.4);
30
+ --ogrid-muted: rgba(0, 0, 0, 0.5);
22
31
  --ogrid-selection: #217346;
23
32
  --ogrid-primary: #217346;
24
33
  --ogrid-primary-fg: #fff;
25
34
  --ogrid-primary-hover: #1b5f39;
26
- --ogrid-accent: #217346;
27
35
  --ogrid-accent-dark: #1b5f39;
28
36
  --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
29
37
  --ogrid-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.08);
@@ -33,55 +41,71 @@
33
41
 
34
42
  /* ── Dark Theme ── */
35
43
  [data-theme='dark'] {
36
- --ogrid-bg: #1a1a24;
37
- --ogrid-fg: #e0e0e0;
38
- --ogrid-border: #333340;
44
+ --ogrid-bg: #1e1e1e;
45
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
46
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
47
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
48
+ --ogrid-border: rgba(255, 255, 255, 0.12);
49
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
50
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
51
+ --ogrid-selected-row-bg: #1a3a5c;
52
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
53
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
54
+ --ogrid-accent: #4da6ff;
55
+ --ogrid-selection-color: #2ea043;
56
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
57
+ /* Aliases for backward compat */
39
58
  --ogrid-border-strong: #555;
40
59
  --ogrid-border-hover: #666;
41
- --ogrid-bg-subtle: #22222e;
42
- --ogrid-bg-hover: #2a2a38;
43
- --ogrid-bg-selected: #1a2f45;
44
- --ogrid-bg-selected-hover: #1f3650;
45
- --ogrid-bg-range: rgba(58, 184, 118, 0.12);
46
- --ogrid-header-bg: #22222e;
47
- --ogrid-muted: #999;
48
- --ogrid-fg-muted: rgba(255, 255, 255, 0.3);
49
- --ogrid-selection: #3ab876;
50
- --ogrid-primary: #3ab876;
51
- --ogrid-primary-fg: #0a0a0f;
52
- --ogrid-primary-hover: #4ec484;
53
- --ogrid-accent: #3ab876;
54
- --ogrid-accent-dark: #2fa768;
60
+ --ogrid-bg-subtle: #2a2a2a;
61
+ --ogrid-bg-hover: rgba(255, 255, 255, 0.08);
62
+ --ogrid-bg-selected: #1a3a5c;
63
+ --ogrid-bg-selected-hover: #1f426b;
64
+ --ogrid-bg-range: rgba(46, 160, 67, 0.15);
65
+ --ogrid-muted: rgba(255, 255, 255, 0.5);
66
+ --ogrid-selection: #2ea043;
67
+ --ogrid-primary: #2ea043;
68
+ --ogrid-primary-fg: #fff;
69
+ --ogrid-primary-hover: #3db852;
70
+ --ogrid-accent-dark: #3db852;
55
71
  --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
56
72
  --ogrid-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.25);
57
- --ogrid-loading-bg: rgba(26, 26, 36, 0.7);
73
+ --ogrid-loading-bg: rgba(0, 0, 0, 0.7);
58
74
  --ogrid-pinned-shadow: rgba(0, 0, 0, 0.4);
59
75
  }
60
76
 
61
77
  @media (prefers-color-scheme: dark) {
62
78
  :root:not([data-theme='light']) {
63
- --ogrid-bg: #1a1a24;
64
- --ogrid-fg: #e0e0e0;
65
- --ogrid-border: #333340;
79
+ --ogrid-bg: #1e1e1e;
80
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
81
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
82
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
83
+ --ogrid-border: rgba(255, 255, 255, 0.12);
84
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
85
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
86
+ --ogrid-selected-row-bg: #1a3a5c;
87
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
88
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
89
+ --ogrid-accent: #4da6ff;
90
+ --ogrid-selection-color: #2ea043;
91
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
92
+ /* Aliases for backward compat */
66
93
  --ogrid-border-strong: #555;
67
94
  --ogrid-border-hover: #666;
68
- --ogrid-bg-subtle: #22222e;
69
- --ogrid-bg-hover: #2a2a38;
70
- --ogrid-bg-selected: #1a2f45;
71
- --ogrid-bg-selected-hover: #1f3650;
72
- --ogrid-bg-range: rgba(58, 184, 118, 0.12);
73
- --ogrid-header-bg: #22222e;
74
- --ogrid-muted: #999;
75
- --ogrid-fg-muted: rgba(255, 255, 255, 0.3);
76
- --ogrid-selection: #3ab876;
77
- --ogrid-primary: #3ab876;
78
- --ogrid-primary-fg: #0a0a0f;
79
- --ogrid-primary-hover: #4ec484;
80
- --ogrid-accent: #3ab876;
81
- --ogrid-accent-dark: #2fa768;
95
+ --ogrid-bg-subtle: #2a2a2a;
96
+ --ogrid-bg-hover: rgba(255, 255, 255, 0.08);
97
+ --ogrid-bg-selected: #1a3a5c;
98
+ --ogrid-bg-selected-hover: #1f426b;
99
+ --ogrid-bg-range: rgba(46, 160, 67, 0.15);
100
+ --ogrid-muted: rgba(255, 255, 255, 0.5);
101
+ --ogrid-selection: #2ea043;
102
+ --ogrid-primary: #2ea043;
103
+ --ogrid-primary-fg: #fff;
104
+ --ogrid-primary-hover: #3db852;
105
+ --ogrid-accent-dark: #3db852;
82
106
  --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
83
107
  --ogrid-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.25);
84
- --ogrid-loading-bg: rgba(26, 26, 36, 0.7);
108
+ --ogrid-loading-bg: rgba(0, 0, 0, 0.7);
85
109
  --ogrid-pinned-shadow: rgba(0, 0, 0, 0.4);
86
110
  }
87
111
  }
@@ -35,6 +35,7 @@ export declare class OGrid<T> {
35
35
  private paginationContainer;
36
36
  private statusBarContainer;
37
37
  private options;
38
+ private layoutVersion;
38
39
  /** The imperative grid API (extends React's IOGridApi with JS-specific methods). */
39
40
  readonly api: IJsOGridApi<T>;
40
41
  constructor(container: HTMLElement, options: OGridOptions<T>);
@@ -1,4 +1,4 @@
1
- import type { ISelectionRange } from '@alaarab/ogrid-core';
1
+ import { type ISelectionRange } from '@alaarab/ogrid-core';
2
2
  /**
3
3
  * MarchingAntsOverlay — renders SVG overlays on top of the grid:
4
4
  * 1. Selection range: solid green border
@@ -15,8 +15,9 @@ export declare class MarchingAntsOverlay {
15
15
  private copyRange;
16
16
  private cutRange;
17
17
  private rafHandle;
18
+ private layoutVersion;
18
19
  constructor(container: HTMLElement, colOffset?: number);
19
- update(selectionRange: ISelectionRange | null, copyRange: ISelectionRange | null, cutRange: ISelectionRange | null): void;
20
+ update(selectionRange: ISelectionRange | null, copyRange: ISelectionRange | null, cutRange: ISelectionRange | null, layoutVersion?: number): void;
20
21
  private render;
21
22
  private createSvg;
22
23
  private positionSvg;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-js",
3
- "version": "2.0.9",
3
+ "version": "2.0.12",
4
4
  "description": "OGrid vanilla JS – framework-free data grid with sorting, filtering, pagination, and spreadsheet-style editing.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -36,7 +36,7 @@
36
36
  "node": ">=18"
37
37
  },
38
38
  "dependencies": {
39
- "@alaarab/ogrid-core": "2.0.9"
39
+ "@alaarab/ogrid-core": "2.0.12"
40
40
  },
41
41
  "sideEffects": false,
42
42
  "publishConfig": {