@alaarab/ogrid-core 2.1.10 → 2.1.11

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 CHANGED
@@ -583,20 +583,31 @@ function processClientSideData(data, columns, filters, sortBy, sortDirection) {
583
583
  if (Number.isNaN(bt)) return 1 * dir;
584
584
  return at === bt ? 0 : at > bt ? dir : -dir;
585
585
  });
586
- } else {
586
+ } else if (!compare) {
587
+ const keyCache = /* @__PURE__ */ new Map();
588
+ for (let i = 0; i < sortable.length; i++) {
589
+ const row = sortable[i];
590
+ const v = sortCol ? getCellValue(row, sortCol) : row[sortBy];
591
+ if (v == null) {
592
+ keyCache.set(row, void 0);
593
+ } else if (typeof v === "number") {
594
+ keyCache.set(row, v);
595
+ } else {
596
+ keyCache.set(row, String(v).toLowerCase());
597
+ }
598
+ }
587
599
  sortable.sort((a, b) => {
588
- if (compare) return compare(a, b) * dir;
589
- const av = sortCol ? getCellValue(a, sortCol) : a[sortBy];
590
- const bv = sortCol ? getCellValue(b, sortCol) : b[sortBy];
591
- if (av == null && bv == null) return 0;
592
- if (av == null) return -1 * dir;
593
- if (bv == null) return 1 * dir;
600
+ const av = keyCache.get(a);
601
+ const bv = keyCache.get(b);
602
+ if (av === void 0 && bv === void 0) return 0;
603
+ if (av === void 0) return -1 * dir;
604
+ if (bv === void 0) return 1 * dir;
594
605
  if (typeof av === "number" && typeof bv === "number")
595
606
  return av === bv ? 0 : av > bv ? dir : -dir;
596
- const as = String(av).toLowerCase();
597
- const bs = String(bv).toLowerCase();
598
- return as === bs ? 0 : as > bs ? dir : -dir;
607
+ return av === bv ? 0 : av > bv ? dir : -dir;
599
608
  });
609
+ } else {
610
+ sortable.sort((a, b) => compare(a, b) * dir);
600
611
  }
601
612
  return sortable;
602
613
  }
@@ -787,7 +798,83 @@ function getHeaderFilterConfig(col, input) {
787
798
  }
788
799
  return base;
789
800
  }
790
- function getCellRenderDescriptor(item, col, rowIndex, colIdx, input) {
801
+ var _CellDescriptorCache = class _CellDescriptorCache {
802
+ constructor() {
803
+ this.cache = /* @__PURE__ */ new Map();
804
+ /** Last seen volatile version string. Used to detect when to skip per-cell version checks. */
805
+ this.lastVersion = "";
806
+ }
807
+ /**
808
+ * Compute a version string from the volatile parts of CellRenderDescriptorInput.
809
+ * This string changes whenever any input that affects per-cell output changes.
810
+ * Cheap to compute (simple string concat) — O(1) regardless of grid size.
811
+ */
812
+ static computeVersion(input) {
813
+ const ec = input.editingCell;
814
+ const ac = input.activeCell;
815
+ const sr = input.selectionRange;
816
+ const cr = input.cutRange;
817
+ const cp = input.copyRange;
818
+ return (ec ? `${String(ec.rowId)}\0${ec.columnId}` : "") + "" + (ac ? `${ac.rowIndex}\0${ac.columnIndex}` : "") + "" + (sr ? `${sr.startRow}\0${sr.startCol}\0${sr.endRow}\0${sr.endCol}` : "") + "" + (cr ? `${cr.startRow}\0${cr.startCol}\0${cr.endRow}\0${cr.endCol}` : "") + "" + (cp ? `${cp.startRow}\0${cp.startCol}\0${cp.endRow}\0${cp.endCol}` : "") + "" + (input.isDragging ? "1" : "0") + "" + (input.editable !== false ? "1" : "0") + "" + (input.onCellValueChanged ? "1" : "0");
819
+ }
820
+ /**
821
+ * Get a cached descriptor or compute a new one.
822
+ *
823
+ * @param rowIndex - Row index in the dataset.
824
+ * @param colIdx - Column index within the visible columns.
825
+ * @param version - Volatile version string (from CellDescriptorCache.computeVersion).
826
+ * @param compute - Factory function called on cache miss.
827
+ * @returns The descriptor (cached or freshly computed).
828
+ */
829
+ get(rowIndex, colIdx, version, compute) {
830
+ const key = rowIndex * _CellDescriptorCache.MAX_COL_STRIDE + colIdx;
831
+ const entry = this.cache.get(key);
832
+ if (entry !== void 0 && entry.version === version) {
833
+ return entry.descriptor;
834
+ }
835
+ const descriptor = compute();
836
+ this.cache.set(key, { version, descriptor });
837
+ return descriptor;
838
+ }
839
+ /**
840
+ * Update the last-seen version and return it.
841
+ * Call once per render pass to track whether any volatile state changed.
842
+ * If the version is unchanged from last render, the entire render is a no-op for all cells.
843
+ */
844
+ updateVersion(version) {
845
+ this.lastVersion = version;
846
+ }
847
+ /** The last version string set via updateVersion(). */
848
+ get currentVersion() {
849
+ return this.lastVersion;
850
+ }
851
+ /**
852
+ * Clear all cached entries. Call when the grid's data changes (new items array,
853
+ * different column count, etc.) to prevent stale cell values from being served.
854
+ */
855
+ clear() {
856
+ this.cache.clear();
857
+ }
858
+ };
859
+ /**
860
+ * Stride used to compute a flat cache key: rowIndex * MAX_COL_STRIDE + colIdx.
861
+ * 1024 supports grids up to 1024 columns, which covers all realistic use cases.
862
+ * Using a power-of-2 stride lets the JS engine optimize the multiplication.
863
+ */
864
+ _CellDescriptorCache.MAX_COL_STRIDE = 1024;
865
+ var CellDescriptorCache = _CellDescriptorCache;
866
+ function getCellRenderDescriptor(item, col, rowIndex, colIdx, input, cache) {
867
+ if (cache !== void 0) {
868
+ return cache.get(
869
+ rowIndex,
870
+ colIdx,
871
+ cache.currentVersion,
872
+ () => computeCellDescriptor(item, col, rowIndex, colIdx, input)
873
+ );
874
+ }
875
+ return computeCellDescriptor(item, col, rowIndex, colIdx, input);
876
+ }
877
+ function computeCellDescriptor(item, col, rowIndex, colIdx, input) {
791
878
  const rowId = input.getRowId(item);
792
879
  const globalColIndex = colIdx + input.colOffset;
793
880
  const colEditable = isColumnEditable(col, item);
@@ -1384,6 +1471,7 @@ function validateColumns(columns) {
1384
1471
  console.warn("[OGrid] columns prop is empty or not an array");
1385
1472
  return;
1386
1473
  }
1474
+ const isDev = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1387
1475
  const ids = /* @__PURE__ */ new Set();
1388
1476
  for (const col of columns) {
1389
1477
  if (!col.columnId) {
@@ -1393,10 +1481,25 @@ function validateColumns(columns) {
1393
1481
  console.warn(`[OGrid] Duplicate columnId: "${col.columnId}"`);
1394
1482
  }
1395
1483
  ids.add(col.columnId);
1484
+ if (isDev && col.editable === true && col.cellEditor == null) {
1485
+ console.warn(
1486
+ `[OGrid] Column "${col.columnId}" has editable=true but no cellEditor defined. Cells will not open an editor on double-click. Set cellEditor to 'text', 'select', 'checkbox', 'date', or a custom component.`
1487
+ );
1488
+ }
1489
+ }
1490
+ }
1491
+ function validateVirtualScrollConfig(config) {
1492
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
1493
+ if (config.enabled !== true) return;
1494
+ if (!config.rowHeight || config.rowHeight <= 0) {
1495
+ console.warn(
1496
+ "[OGrid] virtualScroll.enabled is true but rowHeight is missing or <= 0. Set a positive rowHeight (e.g. virtualScroll: { enabled: true, rowHeight: 36 }) for correct virtual scrolling behavior."
1497
+ );
1396
1498
  }
1397
1499
  }
1398
1500
  function validateRowIds(items, getRowId) {
1399
1501
  if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
1502
+ if (!getRowId) return;
1400
1503
  const ids = /* @__PURE__ */ new Set();
1401
1504
  const limit = Math.min(items.length, 100);
1402
1505
  for (let i = 0; i < limit; i++) {
@@ -1422,16 +1525,44 @@ var SIDEBAR_TRANSITION_MS = 300;
1422
1525
 
1423
1526
  // src/constants/zIndex.ts
1424
1527
  var Z_INDEX = {
1528
+ /** Column resize drag handle */
1529
+ RESIZE_HANDLE: 1,
1530
+ /** Active/editing cell outline */
1531
+ ACTIVE_CELL: 2,
1532
+ /** Fill handle dot */
1533
+ FILL_HANDLE: 3,
1425
1534
  /** Selection range overlay (marching ants) */
1426
1535
  SELECTION_OVERLAY: 4,
1536
+ /** Row number column */
1537
+ ROW_NUMBER: 5,
1427
1538
  /** Clipboard overlay (copy/cut animation) */
1428
1539
  CLIPBOARD_OVERLAY: 5,
1540
+ /** Sticky pinned body cells */
1541
+ PINNED: 6,
1542
+ /** Selection checkbox column in body */
1543
+ SELECTION_CELL: 7,
1544
+ /** Sticky thead row */
1545
+ THEAD: 8,
1546
+ /** Pinned header cells (sticky both axes) */
1547
+ PINNED_HEADER: 10,
1548
+ /** Focused header cell */
1549
+ HEADER_FOCUS: 11,
1550
+ /** Checkbox column in sticky header (sticky both axes) */
1551
+ SELECTION_HEADER_PINNED: 12,
1552
+ /** Loading overlay within table */
1553
+ LOADING: 2,
1554
+ /** Column reorder drop indicator */
1555
+ DROP_INDICATOR: 100,
1429
1556
  /** Dropdown menus (column chooser, pagination size select) */
1430
1557
  DROPDOWN: 1e3,
1558
+ /** Filter popovers */
1559
+ FILTER_POPOVER: 1e3,
1431
1560
  /** Modal dialogs */
1432
1561
  MODAL: 2e3,
1562
+ /** Fullscreen grid container */
1563
+ FULLSCREEN: 9999,
1433
1564
  /** Context menus (right-click grid menu) */
1434
- CONTEXT_MENU: 9999
1565
+ CONTEXT_MENU: 1e4
1435
1566
  };
1436
1567
 
1437
- export { AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, MAX_PAGE_BUTTONS, PAGE_SIZE_OPTIONS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, UndoRedoStack, Z_INDEX, applyCellDeletion, applyCutClear, applyFillValues, applyPastedValues, applyRangeRowSelection, areGridRowPropsEqual, booleanParser, buildCellIndex, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, calculateDropTarget, clampSelectionToBounds, computeAggregations, computeArrowNavigation, computeAutoScrollSpeed, computeNextSortState, computeRowSelectionState, computeTabNavigation, computeTotalHeight, computeVisibleRange, currencyParser, dateParser, debounce, deriveFilterOptionsFromData, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellRenderDescriptor, getCellValue, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getPinStateForColumn, getScrollTopForRow, getStatusBarParts, injectGlobalStyles, isColumnEditable, isFilterConfig, isInSelectionRange, isRowInRange, measureColumnContentWidth, measureRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, processClientSideData, rangesEqual, reorderColumnArray, resolveCellDisplayContent, resolveCellStyle, toUserLike, triggerCsvDownload, validateColumns, validateRowIds };
1568
+ export { AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CellDescriptorCache, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, MAX_PAGE_BUTTONS, PAGE_SIZE_OPTIONS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, UndoRedoStack, Z_INDEX, applyCellDeletion, applyCutClear, applyFillValues, applyPastedValues, applyRangeRowSelection, areGridRowPropsEqual, booleanParser, buildCellIndex, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, calculateDropTarget, clampSelectionToBounds, computeAggregations, computeArrowNavigation, computeAutoScrollSpeed, computeNextSortState, computeRowSelectionState, computeTabNavigation, computeTotalHeight, computeVisibleRange, currencyParser, dateParser, debounce, deriveFilterOptionsFromData, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellRenderDescriptor, getCellValue, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getPinStateForColumn, getScrollTopForRow, getStatusBarParts, injectGlobalStyles, isColumnEditable, isFilterConfig, isInSelectionRange, isRowInRange, measureColumnContentWidth, measureRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, processClientSideData, rangesEqual, reorderColumnArray, resolveCellDisplayContent, resolveCellStyle, toUserLike, triggerCsvDownload, validateColumns, validateRowIds, validateVirtualScrollConfig };
@@ -1,18 +1,47 @@
1
1
  /**
2
2
  * Z-index constants for layering OGrid UI elements.
3
3
  * Ensures consistent stacking order across all packages.
4
+ * Values mirror --ogrid-z-* CSS custom properties in _ogrid-theme.scss.
4
5
  */
5
6
  export declare const Z_INDEX: {
7
+ /** Column resize drag handle */
8
+ readonly RESIZE_HANDLE: 1;
9
+ /** Active/editing cell outline */
10
+ readonly ACTIVE_CELL: 2;
11
+ /** Fill handle dot */
12
+ readonly FILL_HANDLE: 3;
6
13
  /** Selection range overlay (marching ants) */
7
14
  readonly SELECTION_OVERLAY: 4;
15
+ /** Row number column */
16
+ readonly ROW_NUMBER: 5;
8
17
  /** Clipboard overlay (copy/cut animation) */
9
18
  readonly CLIPBOARD_OVERLAY: 5;
19
+ /** Sticky pinned body cells */
20
+ readonly PINNED: 6;
21
+ /** Selection checkbox column in body */
22
+ readonly SELECTION_CELL: 7;
23
+ /** Sticky thead row */
24
+ readonly THEAD: 8;
25
+ /** Pinned header cells (sticky both axes) */
26
+ readonly PINNED_HEADER: 10;
27
+ /** Focused header cell */
28
+ readonly HEADER_FOCUS: 11;
29
+ /** Checkbox column in sticky header (sticky both axes) */
30
+ readonly SELECTION_HEADER_PINNED: 12;
31
+ /** Loading overlay within table */
32
+ readonly LOADING: 2;
33
+ /** Column reorder drop indicator */
34
+ readonly DROP_INDICATOR: 100;
10
35
  /** Dropdown menus (column chooser, pagination size select) */
11
36
  readonly DROPDOWN: 1000;
37
+ /** Filter popovers */
38
+ readonly FILTER_POPOVER: 1000;
12
39
  /** Modal dialogs */
13
40
  readonly MODAL: 2000;
41
+ /** Fullscreen grid container */
42
+ readonly FULLSCREEN: 9999;
14
43
  /** Context menus (right-click grid menu) */
15
- readonly CONTEXT_MENU: 9999;
44
+ readonly CONTEXT_MENU: 10000;
16
45
  };
17
46
  /** Type helper for z-index keys */
18
47
  export type ZIndexKey = keyof typeof Z_INDEX;
@@ -24,7 +24,7 @@ export { getPinStateForColumn, reorderColumnArray, calculateDropTarget, } from '
24
24
  export type { ColumnPinState, IDropTarget, ICalculateDropTargetParams } from './utils';
25
25
  export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, } from './utils';
26
26
  export type { IVisibleRange } from './utils';
27
- export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './utils';
27
+ export { getHeaderFilterConfig, getCellRenderDescriptor, CellDescriptorCache, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './utils';
28
28
  export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './utils';
29
29
  export { debounce } from './utils';
30
30
  export { measureRange, buildCellIndex, injectGlobalStyles } from './utils';
@@ -38,7 +38,7 @@ export { rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, applyRange
38
38
  export { formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, } from './utils';
39
39
  export { applyFillValues } from './utils';
40
40
  export { UndoRedoStack } from './utils';
41
- export { validateColumns, validateRowIds } from './utils';
41
+ export { validateColumns, validateRowIds, validateVirtualScrollConfig } from './utils';
42
42
  export { CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from './constants';
43
43
  export { DEFAULT_DEBOUNCE_MS, PEOPLE_SEARCH_DEBOUNCE_MS, SIDEBAR_TRANSITION_MS, } from './constants';
44
44
  export { Z_INDEX } from './constants';
@@ -136,6 +136,13 @@ export interface IVirtualScrollConfig {
136
136
  rowHeight?: number;
137
137
  /** Number of extra rows to render above/below the visible area (default: 5). */
138
138
  overscan?: number;
139
+ /**
140
+ * Minimum row count before virtual scrolling activates (default: 100).
141
+ * When totalRows < threshold, all rows are rendered without virtualization.
142
+ * Lower values activate virtualization earlier (more memory-efficient for mid-size grids);
143
+ * higher values keep small grids fully rendered (no scroll offset artifacts).
144
+ */
145
+ threshold?: number;
139
146
  }
140
147
  /** Configuration for column reordering via drag-and-drop. */
141
148
  export interface IColumnReorderConfig {
@@ -94,11 +94,85 @@ export interface CellRenderDescriptor {
94
94
  /** Raw value for display (when mode === 'display'). UI uses col.renderCell or col.valueFormatter. */
95
95
  displayValue?: unknown;
96
96
  }
97
+ /**
98
+ * Per-grid cache for cell render descriptors.
99
+ *
100
+ * Problem: A 50-column × 100-row grid calls getCellRenderDescriptor 5,000 times per render.
101
+ * Most cells don't change between renders — only cells in the active row, selection range,
102
+ * or editing row need recomputation. The cache skips recomputation for unchanged cells.
103
+ *
104
+ * Design:
105
+ * - Keyed by (rowIndex * MAX_COL_STRIDE + colIdx) for O(1) flat-array-style access.
106
+ * - Tracks a "volatile version" string derived from all inputs that affect per-cell output.
107
+ * - On version match (cache hit), returns the cached descriptor without recomputing.
108
+ * - On version mismatch (cache miss or first render), recomputes and stores the result.
109
+ *
110
+ * Usage: Create one instance per grid (e.g. useRef in React) and pass to getCellRenderDescriptor.
111
+ *
112
+ * @example
113
+ * const descriptorCache = useRef(new CellDescriptorCache());
114
+ * // In renderCellContent:
115
+ * getCellRenderDescriptor(item, col, rowIndex, colIdx, input, descriptorCache.current);
116
+ */
117
+ export declare class CellDescriptorCache {
118
+ /**
119
+ * Stride used to compute a flat cache key: rowIndex * MAX_COL_STRIDE + colIdx.
120
+ * 1024 supports grids up to 1024 columns, which covers all realistic use cases.
121
+ * Using a power-of-2 stride lets the JS engine optimize the multiplication.
122
+ */
123
+ private static readonly MAX_COL_STRIDE;
124
+ private readonly cache;
125
+ /** Last seen volatile version string. Used to detect when to skip per-cell version checks. */
126
+ private lastVersion;
127
+ /**
128
+ * Compute a version string from the volatile parts of CellRenderDescriptorInput.
129
+ * This string changes whenever any input that affects per-cell output changes.
130
+ * Cheap to compute (simple string concat) — O(1) regardless of grid size.
131
+ */
132
+ static computeVersion<T>(input: CellRenderDescriptorInput<T>): string;
133
+ /**
134
+ * Get a cached descriptor or compute a new one.
135
+ *
136
+ * @param rowIndex - Row index in the dataset.
137
+ * @param colIdx - Column index within the visible columns.
138
+ * @param version - Volatile version string (from CellDescriptorCache.computeVersion).
139
+ * @param compute - Factory function called on cache miss.
140
+ * @returns The descriptor (cached or freshly computed).
141
+ */
142
+ get(rowIndex: number, colIdx: number, version: string, compute: () => CellRenderDescriptor): CellRenderDescriptor;
143
+ /**
144
+ * Update the last-seen version and return it.
145
+ * Call once per render pass to track whether any volatile state changed.
146
+ * If the version is unchanged from last render, the entire render is a no-op for all cells.
147
+ */
148
+ updateVersion(version: string): void;
149
+ /** The last version string set via updateVersion(). */
150
+ get currentVersion(): string;
151
+ /**
152
+ * Clear all cached entries. Call when the grid's data changes (new items array,
153
+ * different column count, etc.) to prevent stale cell values from being served.
154
+ */
155
+ clear(): void;
156
+ }
97
157
  /**
98
158
  * Returns a descriptor for rendering a cell. UI uses this to decide editing-inline vs editing-popover vs display
99
159
  * and to apply isActive, isInRange, etc. without duplicating the boolean logic.
160
+ *
161
+ * @param item - The row data object.
162
+ * @param col - The column definition.
163
+ * @param rowIndex - Row index in the dataset.
164
+ * @param colIdx - Column index within the visible columns.
165
+ * @param input - Volatile state inputs (editing cell, active cell, selection ranges, etc.).
166
+ * @param cache - Optional descriptor cache. When provided and the volatile state is unchanged,
167
+ * the cached descriptor is returned without recomputation. Pass a `CellDescriptorCache`
168
+ * instance created once per grid (e.g. via `useRef`) and updated each render via
169
+ * `CellDescriptorCache.computeVersion`. This eliminates ~5,000 allocations/render on a
170
+ * 50-col × 100-row grid when selection/editing state hasn't changed for a given cell.
100
171
  */
101
- export declare function getCellRenderDescriptor<T>(item: T, col: IColumnDef<T>, rowIndex: number, colIdx: number, input: CellRenderDescriptorInput<T>): CellRenderDescriptor;
172
+ export declare function getCellRenderDescriptor<T>(item: T, col: IColumnDef<T>, rowIndex: number, colIdx: number, input: CellRenderDescriptorInput<T>, cache?: {
173
+ get(rowIndex: number, colIdx: number, version: string, compute: () => CellRenderDescriptor): CellRenderDescriptor;
174
+ currentVersion: string;
175
+ }): CellRenderDescriptor;
102
176
  /**
103
177
  * Resolves display content for a cell in display mode.
104
178
  * Handles the renderCell -> valueFormatter -> String() fallback chain.
@@ -21,7 +21,7 @@ export { getPinStateForColumn, reorderColumnArray, calculateDropTarget, } from '
21
21
  export type { ColumnPinState, IDropTarget, ICalculateDropTargetParams } from './columnReorder';
22
22
  export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, } from './virtualScroll';
23
23
  export type { IVisibleRange } from './virtualScroll';
24
- export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
24
+ export { getHeaderFilterConfig, getCellRenderDescriptor, CellDescriptorCache, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
25
25
  export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './dataGridViewModel';
26
26
  export { debounce } from './debounce';
27
27
  export { measureRange, buildCellIndex, injectGlobalStyles } from './dom';
@@ -35,4 +35,4 @@ export { rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, applyRange
35
35
  export { formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, } from './clipboardHelpers';
36
36
  export { applyFillValues } from './fillHelpers';
37
37
  export { UndoRedoStack } from './undoRedoStack';
38
- export { validateColumns, validateRowIds } from './validation';
38
+ export { validateColumns, validateRowIds, validateVirtualScrollConfig } from './validation';
@@ -1,13 +1,19 @@
1
1
  import type { IColumnDef } from '../types/columnTypes';
2
- import type { RowId } from '../types/dataGridTypes';
2
+ import type { RowId, IVirtualScrollConfig } from '../types/dataGridTypes';
3
3
  /**
4
4
  * Validate column definitions at grid initialization.
5
5
  * Called once (not per render). Warns on empty/missing/duplicate columnIds.
6
+ * In development mode, also warns when editable=true but no cellEditor is defined.
6
7
  */
7
8
  export declare function validateColumns<T>(columns: IColumnDef<T>[]): void;
9
+ /**
10
+ * Validate virtual scroll configuration.
11
+ * Dev-only warning when enabled=true but rowHeight is missing or <= 0.
12
+ */
13
+ export declare function validateVirtualScrollConfig(config: IVirtualScrollConfig): void;
8
14
  /**
9
15
  * Validate that getRowId returns unique, non-null values.
10
16
  * Dev-only (skipped in production). Samples the first 100 rows.
11
17
  * Called once on first data render via a hasValidated flag in the caller.
12
18
  */
13
- export declare function validateRowIds<T>(items: T[], getRowId: (item: T) => RowId): void;
19
+ export declare function validateRowIds<T>(items: T[], getRowId: ((item: T) => RowId) | null | undefined): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-core",
3
- "version": "2.1.10",
3
+ "version": "2.1.11",
4
4
  "description": "OGrid core – framework-agnostic types, algorithms, and utilities for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",