@alaarab/ogrid-core 1.8.2 → 2.0.0-beta

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 (74) hide show
  1. package/README.md +28 -31
  2. package/dist/esm/constants.js +11 -0
  3. package/dist/esm/index.js +6 -11
  4. package/dist/esm/types/index.js +2 -1
  5. package/dist/esm/utils/clientSideData.js +25 -12
  6. package/dist/esm/utils/columnUtils.js +6 -0
  7. package/dist/esm/utils/gridRowComparator.js +78 -0
  8. package/dist/esm/utils/index.js +1 -1
  9. package/dist/esm/utils/ogridHelpers.js +2 -1
  10. package/dist/esm/utils/paginationHelpers.js +7 -1
  11. package/dist/types/constants.d.ts +11 -0
  12. package/dist/types/index.d.ts +3 -16
  13. package/dist/types/types/columnTypes.d.ts +5 -9
  14. package/dist/types/types/dataGridTypes.d.ts +12 -133
  15. package/dist/types/types/index.d.ts +3 -3
  16. package/dist/types/utils/gridRowComparator.d.ts +57 -0
  17. package/dist/types/utils/index.d.ts +2 -2
  18. package/package.json +6 -24
  19. package/dist/esm/components/GridContextMenu.js +0 -34
  20. package/dist/esm/components/MarchingAntsOverlay.js +0 -109
  21. package/dist/esm/components/OGridLayout.js +0 -90
  22. package/dist/esm/components/SideBar.js +0 -100
  23. package/dist/esm/components/StatusBar.js +0 -6
  24. package/dist/esm/hooks/index.js +0 -19
  25. package/dist/esm/hooks/useActiveCell.js +0 -46
  26. package/dist/esm/hooks/useCellEditing.js +0 -11
  27. package/dist/esm/hooks/useCellSelection.js +0 -318
  28. package/dist/esm/hooks/useClipboard.js +0 -162
  29. package/dist/esm/hooks/useColumnChooserState.js +0 -62
  30. package/dist/esm/hooks/useColumnHeaderFilterState.js +0 -228
  31. package/dist/esm/hooks/useColumnResize.js +0 -69
  32. package/dist/esm/hooks/useContextMenu.js +0 -17
  33. package/dist/esm/hooks/useDataGridState.js +0 -366
  34. package/dist/esm/hooks/useDebounce.js +0 -35
  35. package/dist/esm/hooks/useFillHandle.js +0 -191
  36. package/dist/esm/hooks/useFilterOptions.js +0 -40
  37. package/dist/esm/hooks/useInlineCellEditorState.js +0 -44
  38. package/dist/esm/hooks/useKeyboardNavigation.js +0 -436
  39. package/dist/esm/hooks/useOGrid.js +0 -414
  40. package/dist/esm/hooks/useRichSelectState.js +0 -53
  41. package/dist/esm/hooks/useRowSelection.js +0 -68
  42. package/dist/esm/hooks/useSideBarState.js +0 -34
  43. package/dist/esm/hooks/useUndoRedo.js +0 -82
  44. package/dist/esm/storybook/index.js +0 -1
  45. package/dist/esm/storybook/mockData.js +0 -73
  46. package/dist/esm/utils/dataGridViewModel.js +0 -233
  47. package/dist/types/components/GridContextMenu.d.ts +0 -18
  48. package/dist/types/components/MarchingAntsOverlay.d.ts +0 -15
  49. package/dist/types/components/OGridLayout.d.ts +0 -37
  50. package/dist/types/components/SideBar.d.ts +0 -30
  51. package/dist/types/components/StatusBar.d.ts +0 -24
  52. package/dist/types/hooks/index.d.ts +0 -37
  53. package/dist/types/hooks/useActiveCell.d.ts +0 -13
  54. package/dist/types/hooks/useCellEditing.d.ts +0 -12
  55. package/dist/types/hooks/useCellSelection.d.ts +0 -17
  56. package/dist/types/hooks/useClipboard.d.ts +0 -25
  57. package/dist/types/hooks/useColumnChooserState.d.ts +0 -27
  58. package/dist/types/hooks/useColumnHeaderFilterState.d.ts +0 -72
  59. package/dist/types/hooks/useColumnResize.d.ts +0 -18
  60. package/dist/types/hooks/useContextMenu.d.ts +0 -15
  61. package/dist/types/hooks/useDataGridState.d.ts +0 -136
  62. package/dist/types/hooks/useDebounce.d.ts +0 -9
  63. package/dist/types/hooks/useFillHandle.d.ts +0 -28
  64. package/dist/types/hooks/useFilterOptions.d.ts +0 -16
  65. package/dist/types/hooks/useInlineCellEditorState.d.ts +0 -24
  66. package/dist/types/hooks/useKeyboardNavigation.d.ts +0 -34
  67. package/dist/types/hooks/useOGrid.d.ts +0 -31
  68. package/dist/types/hooks/useRichSelectState.d.ts +0 -17
  69. package/dist/types/hooks/useRowSelection.d.ts +0 -17
  70. package/dist/types/hooks/useSideBarState.d.ts +0 -15
  71. package/dist/types/hooks/useUndoRedo.d.ts +0 -21
  72. package/dist/types/storybook/index.d.ts +0 -2
  73. package/dist/types/storybook/mockData.d.ts +0 -37
  74. package/dist/types/utils/dataGridViewModel.d.ts +0 -169
package/README.md CHANGED
@@ -1,48 +1,45 @@
1
- # @alaarab/ogrid-core
1
+ <p align="center">
2
+ <strong>OGrid Core</strong> — Pure TypeScript types, algorithms, and utilities for OGrid data grids.
3
+ </p>
2
4
 
3
- Framework-agnostic types, hooks, and utilities for [OGrid](https://github.com/alaarab/ogrid) data tables.
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@alaarab/ogrid-core"><img src="https://img.shields.io/npm/v/@alaarab/ogrid-core?color=%23217346&label=npm" alt="npm version" /></a>
7
+ <a href="https://github.com/alaarab/ogrid/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License" /></a>
8
+ <img src="https://img.shields.io/badge/TypeScript-strict-blue" alt="TypeScript strict" />
9
+ </p>
4
10
 
5
- This package is the shared foundation used by `@alaarab/ogrid-fluent` and `@alaarab/ogrid-material`. You typically don't need to install it directly -- both framework packages re-export everything from core.
11
+ <p align="center">
12
+ <a href="https://alaarab.github.io/ogrid/">Documentation</a> · <a href="https://alaarab.github.io/ogrid/docs/getting-started/overview">Getting Started</a> · <a href="https://alaarab.github.io/ogrid/docs/api/ogrid-props">API Reference</a>
13
+ </p>
6
14
 
7
- ## Install
15
+ ---
8
16
 
9
- ```bash
10
- npm install @alaarab/ogrid-core
11
- ```
17
+ Framework-agnostic foundation for [OGrid](https://github.com/alaarab/ogrid) data grids. **Zero dependencies** — pure TypeScript types and utilities shared by [`@alaarab/ogrid-react`](https://www.npmjs.com/package/@alaarab/ogrid-react) and [`@alaarab/ogrid-js`](https://www.npmjs.com/package/@alaarab/ogrid-js).
18
+
19
+ You typically don't need to install this directly — the UI packages re-export everything from core.
12
20
 
13
- ## What's Included
21
+ ## What's Inside
14
22
 
15
23
  ### Types
16
24
 
17
- - `IColumnDef<T>` -- Column definition with sorting, filtering, and rendering
18
- - `IDataSource<T>` -- Server-side data source interface
19
- - `IFetchParams` -- Parameters for `fetchPage()`
20
- - `IFilters` -- Unified filter values (text, multi-select, people)
21
- - `UserLike` -- Minimal user shape for people picker
22
- - `IColumnFilterDef`, `IColumnMeta`, `IPageResult`, `ColumnFilterType`
25
+ `IColumnDef<T>` · `IColumnGroupDef` · `IDataSource<T>` · `IFilters` · `FilterValue` · `IDateFilterValue` · `UserLike` · `IOGridApi<T>` · `IOGridProps<T>` · `IOGridDataGridProps<T>` · `ICellEditorProps<T>` · `IGridColumnState` · `ISideBarDef` · `ColumnFilterType` · `IColumnMeta`
26
+
27
+ ### Utilities
23
28
 
24
- ### Hooks
29
+ `getCellValue` · `buildHeaderRows` · `parseValue` · `normalizeSelectionRange` · `isInSelectionRange` · `toUserLike`
25
30
 
26
- - `useOGrid(props, ref)` -- Page/sort/filter/visibleColumns + `dataGridProps` for DataGridTable (used by OGrid wrappers)
27
- - `useDataGridState({ props, wrapperRef })` -- Orchestrator for grid state, selection, editing, clipboard, keyboard, fill handle, status bar
28
- - `useFilterOptions(dataSource, fields)` -- Loads filter options for multi-select columns
29
- - `useColumnHeaderFilterState(params)` -- Headless filter popover state (open, temp values, apply/clear, people search)
30
- - `useColumnChooserState({ columns, visibleColumns, onVisibilityChange })` -- Column visibility dropdown (open, Escape, select all/clear)
31
- - `useInlineCellEditorState({ value, editorType, onCommit, onCancel })` -- Inline cell editor (localValue, keydown, blur/commit)
31
+ ## Install
32
32
 
33
- ### Components
33
+ ```bash
34
+ npm install @alaarab/ogrid-core
35
+ ```
34
36
 
35
- - `OGridLayout` -- Unified bordered layout: toolbar strip (custom content + column chooser), optional secondary toolbar row, sidebar, grid area, and footer strip (pagination).
37
+ No peer dependencies. No framework dependencies.
36
38
 
37
- ### Utilities
39
+ ## Documentation
38
40
 
39
- - `getPaginationViewModel(...)` -- Page numbers, ellipsis, start/end item for PaginationControls
40
- - `getHeaderFilterConfig(col, input)` -- ColumnHeaderFilter props from column + filter/sort state
41
- - `getCellRenderDescriptor(item, col, rowIndex, colIdx, input)` -- Cell mode (editing-inline / editing-popover / display) and flags for DataGridTable
42
- - `toUserLike(user)` -- Converts a user-like object to `UserLike`
43
- - `exportToCsv(items, columns, getValue, filename)` -- Full CSV export
44
- - `buildCsvHeader`, `buildCsvRows`, `triggerCsvDownload`, `escapeCsvValue` -- Low-level CSV helpers
41
+ Full docs at **[alaarab.github.io/ogrid](https://alaarab.github.io/ogrid/)**.
45
42
 
46
43
  ## License
47
44
 
48
- MIT
45
+ MIT — Free forever.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Core OGrid constants — magic numbers centralized for consistency and maintainability.
3
+ */
4
+ /** Width of the row selection checkbox column in pixels. */
5
+ export const CHECKBOX_COLUMN_WIDTH = 48;
6
+ /** Default minimum width for resizable columns in pixels. */
7
+ export const DEFAULT_MIN_COLUMN_WIDTH = 80;
8
+ /** Horizontal padding inside cells, used for width calculations. */
9
+ export const CELL_PADDING = 16;
10
+ /** Border radius for the grid container in pixels. */
11
+ export const GRID_BORDER_RADIUS = 6;
package/dist/esm/index.js CHANGED
@@ -1,11 +1,6 @@
1
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
2
- // Hooks
3
- export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, } from './hooks';
4
- // Components
5
- export { OGridLayout } from './components/OGridLayout';
6
- export { StatusBar } from './components/StatusBar';
7
- export { GridContextMenu } from './components/GridContextMenu';
8
- export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
9
- export { SideBar } from './components/SideBar';
10
- // Utilities
11
- export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, } from './utils';
1
+ // Types
2
+ export * from './types';
3
+ // Utils
4
+ export * from './utils';
5
+ // Constants
6
+ export * from './constants';
@@ -1 +1,2 @@
1
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
1
+ // Utility functions
2
+ export { toUserLike, isInSelectionRange, normalizeSelectionRange, } from './dataGridTypes';
@@ -12,33 +12,37 @@ import { getFilterField } from './ogridHelpers';
12
12
  * @returns Filtered and sorted array
13
13
  */
14
14
  export function processClientSideData(data, columns, filters, sortBy, sortDirection) {
15
- let rows = data.slice();
16
- // --- Filtering ---
17
- columns.forEach((col) => {
15
+ // --- Filtering (single-pass: build predicates, then one .filter()) ---
16
+ const predicates = [];
17
+ for (let i = 0; i < columns.length; i++) {
18
+ const col = columns[i];
18
19
  const filterKey = getFilterField(col);
19
20
  const val = filters[filterKey];
20
21
  if (!val)
21
- return;
22
+ continue;
22
23
  switch (val.type) {
23
24
  case 'multiSelect':
24
25
  if (val.value.length > 0) {
25
- rows = rows.filter((r) => val.value.includes(String(getCellValue(r, col))));
26
+ const allowedSet = new Set(val.value);
27
+ predicates.push((r) => allowedSet.has(String(getCellValue(r, col))));
26
28
  }
27
29
  break;
28
- case 'text':
29
- if (val.value.trim()) {
30
- const lower = val.value.trim().toLowerCase();
31
- rows = rows.filter((r) => String(getCellValue(r, col) ?? '').toLowerCase().includes(lower));
30
+ case 'text': {
31
+ const trimmed = val.value.trim();
32
+ if (trimmed) {
33
+ const lower = trimmed.toLowerCase();
34
+ predicates.push((r) => String(getCellValue(r, col) ?? '').toLowerCase().includes(lower));
32
35
  }
33
36
  break;
37
+ }
34
38
  case 'people': {
35
39
  const email = val.value.email.toLowerCase();
36
- rows = rows.filter((r) => String(getCellValue(r, col) ?? '').toLowerCase() === email);
40
+ predicates.push((r) => String(getCellValue(r, col) ?? '').toLowerCase() === email);
37
41
  break;
38
42
  }
39
43
  case 'date': {
40
44
  const dv = val.value;
41
- rows = rows.filter((r) => {
45
+ predicates.push((r) => {
42
46
  const cellVal = getCellValue(r, col);
43
47
  if (cellVal == null)
44
48
  return false;
@@ -55,7 +59,16 @@ export function processClientSideData(data, columns, filters, sortBy, sortDirect
55
59
  break;
56
60
  }
57
61
  }
58
- });
62
+ }
63
+ const rows = predicates.length > 0
64
+ ? data.filter((row) => {
65
+ for (let i = 0; i < predicates.length; i++) {
66
+ if (!predicates[i](row))
67
+ return false;
68
+ }
69
+ return true;
70
+ })
71
+ : data.slice();
59
72
  // --- Sorting ---
60
73
  if (sortBy) {
61
74
  const sortCol = columns.find((c) => c.columnId === sortBy);
@@ -66,7 +66,12 @@ export function buildHeaderRows(columns, visibleColumns) {
66
66
  const totalRows = maxDepth + 1;
67
67
  const rows = Array.from({ length: totalRows }, () => []);
68
68
  // Step 3: Walk the tree and place cells
69
+ // Cache leaf counts by children array ref to avoid O(n²) repeated traversals
70
+ const leafCountCache = new Map();
69
71
  function countVisibleLeaves(cols) {
72
+ const cached = leafCountCache.get(cols);
73
+ if (cached !== undefined)
74
+ return cached;
70
75
  let count = 0;
71
76
  for (const c of cols) {
72
77
  if (isColumnGroupDef(c)) {
@@ -78,6 +83,7 @@ export function buildHeaderRows(columns, visibleColumns) {
78
83
  }
79
84
  }
80
85
  }
86
+ leafCountCache.set(cols, count);
81
87
  return count;
82
88
  }
83
89
  function walk(cols, depth) {
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Checks whether a given row index falls within a selection range.
3
+ * O(1) — used by React.memo comparators to skip unchanged rows.
4
+ */
5
+ export function isRowInRange(range, rowIndex) {
6
+ if (!range)
7
+ return false;
8
+ const minR = Math.min(range.startRow, range.endRow);
9
+ const maxR = Math.max(range.startRow, range.endRow);
10
+ return rowIndex >= minR && rowIndex <= maxR;
11
+ }
12
+ /**
13
+ * Shared React.memo comparator for GridRow components across all 3 UI packages.
14
+ * Skips re-render for rows unaffected by selection/editing/interaction changes.
15
+ *
16
+ * Used by:
17
+ * - packages/radix/src/DataGridTable/DataGridTable.tsx
18
+ * - packages/fluent/src/DataGridTable/DataGridTable.tsx
19
+ * - packages/material/src/DataGridTable/DataGridTable.tsx
20
+ */
21
+ export function areGridRowPropsEqual(prev, next) {
22
+ // Data / structure changes — always re-render
23
+ if (prev.item !== next.item)
24
+ return false;
25
+ if (prev.isSelected !== next.isSelected)
26
+ return false;
27
+ if (prev.hasCheckboxCol !== next.hasCheckboxCol)
28
+ return false;
29
+ // Framework-specific structure props (compared by identity)
30
+ if (prev.visibleCols !== next.visibleCols)
31
+ return false;
32
+ if (prev.columnMeta !== next.columnMeta)
33
+ return false;
34
+ if (prev.cellClassMap !== next.cellClassMap)
35
+ return false;
36
+ if (prev.columnLayouts !== next.columnLayouts)
37
+ return false;
38
+ const ri = prev.rowIndex;
39
+ // Editing cell in this row?
40
+ if (prev.editingRowId !== next.editingRowId) {
41
+ if (prev.editingRowId === prev.rowId || next.editingRowId === next.rowId)
42
+ return false;
43
+ }
44
+ // Active cell in this row?
45
+ const prevActive = prev.activeCell?.rowIndex === ri;
46
+ const nextActive = next.activeCell?.rowIndex === ri;
47
+ if (prevActive !== nextActive)
48
+ return false;
49
+ if (prevActive && nextActive && prev.activeCell.columnIndex !== next.activeCell.columnIndex)
50
+ return false;
51
+ // Selection range touches this row?
52
+ const prevInSel = isRowInRange(prev.selectionRange, ri);
53
+ const nextInSel = isRowInRange(next.selectionRange, ri);
54
+ if (prevInSel !== nextInSel)
55
+ return false;
56
+ if (prevInSel && nextInSel) {
57
+ if (prev.selectionRange.startCol !== next.selectionRange.startCol ||
58
+ prev.selectionRange.endCol !== next.selectionRange.endCol)
59
+ return false;
60
+ }
61
+ // Fill handle (selection end row) + isDragging
62
+ const prevIsEnd = prev.selectionRange?.endRow === ri;
63
+ const nextIsEnd = next.selectionRange?.endRow === ri;
64
+ if (prevIsEnd !== nextIsEnd)
65
+ return false;
66
+ if ((prevIsEnd || nextIsEnd) && prev.isDragging !== next.isDragging)
67
+ return false;
68
+ // Cut/copy ranges touch this row?
69
+ if (prev.cutRange !== next.cutRange) {
70
+ if (isRowInRange(prev.cutRange, ri) || isRowInRange(next.cutRange, ri))
71
+ return false;
72
+ }
73
+ if (prev.copyRange !== next.copyRange) {
74
+ if (isRowInRange(prev.copyRange, ri) || isRowInRange(next.copyRange, ri))
75
+ return false;
76
+ }
77
+ return true;
78
+ }
@@ -6,7 +6,7 @@ export { getStatusBarParts } from './statusBarHelpers';
6
6
  export { getDataGridStatusBarConfig } from './dataGridStatusBar';
7
7
  export { getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, } from './paginationHelpers';
8
8
  export { GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from './gridContextMenuHelpers';
9
- export { getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './dataGridViewModel';
10
9
  export { parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, } from './valueParsers';
11
10
  export { computeAggregations } from './aggregationUtils';
12
11
  export { processClientSideData } from './clientSideData';
12
+ export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
@@ -10,7 +10,8 @@ export function mergeFilter(prev, key, value) {
10
10
  const isEmpty = value === undefined ||
11
11
  (value.type === 'text' && value.value.trim() === '') ||
12
12
  (value.type === 'multiSelect' && value.value.length === 0) ||
13
- (value.type === 'date' && !value.value.from && !value.value.to);
13
+ (value.type === 'date' && !value.value.from && !value.value.to) ||
14
+ (value.type === 'people' && !value.value);
14
15
  if (isEmpty) {
15
16
  delete next[key];
16
17
  }
@@ -3,6 +3,12 @@
3
3
  * UI packages use this and render only presentation.
4
4
  */
5
5
  export const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
6
+ /** Ensures the active pageSize is included in the options list, inserting it in sorted order if missing. */
7
+ function ensurePageSizeInOptions(pageSize, options) {
8
+ if (options.includes(pageSize))
9
+ return options;
10
+ return [...options, pageSize].sort((a, b) => a - b);
11
+ }
6
12
  export const MAX_PAGE_BUTTONS = 5;
7
13
  /**
8
14
  * Returns a view model for pagination UI. Use in Fluent/Material/Radix PaginationControls
@@ -47,6 +53,6 @@ export function getPaginationViewModel(currentPage, pageSize, totalCount, option
47
53
  showEndEllipsis,
48
54
  startItem,
49
55
  endItem,
50
- pageSizeOptions: options?.pageSizeOptions ?? PAGE_SIZE_OPTIONS,
56
+ pageSizeOptions: ensurePageSizeInOptions(pageSize, options?.pageSizeOptions ?? PAGE_SIZE_OPTIONS),
51
57
  };
52
58
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Core OGrid constants — magic numbers centralized for consistency and maintainability.
3
+ */
4
+ /** Width of the row selection checkbox column in pixels. */
5
+ export declare const CHECKBOX_COLUMN_WIDTH = 48;
6
+ /** Default minimum width for resizable columns in pixels. */
7
+ export declare const DEFAULT_MIN_COLUMN_WIDTH = 80;
8
+ /** Horizontal padding inside cells, used for width calculations. */
9
+ export declare const CELL_PADDING = 16;
10
+ /** Border radius for the grid container in pixels. */
11
+ export declare const GRID_BORDER_RADIUS = 6;
@@ -1,16 +1,3 @@
1
- export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridDataGridProps, RowSelectionMode, RowId, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, HeaderCell, HeaderRow, SideBarPanelId, ISideBarDef, IDateFilterValue, } from './types';
2
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
3
- export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, } from './hooks';
4
- export type { UseFilterOptionsResult, UseOGridResult, ColumnChooserPlacement, UseActiveCellResult, UseCellEditingResult, EditingCell, UseContextMenuResult, ContextMenuPosition, UseCellSelectionResult, UseCellSelectionParams, UseClipboardResult, UseClipboardParams, UseRowSelectionResult, UseRowSelectionParams, UseKeyboardNavigationResult, UseKeyboardNavigationParams, UseUndoRedoResult, UseUndoRedoParams, UseFillHandleResult, UseFillHandleParams, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, } from './hooks';
5
- export { OGridLayout } from './components/OGridLayout';
6
- export type { OGridLayoutProps } from './components/OGridLayout';
7
- export { StatusBar } from './components/StatusBar';
8
- export type { StatusBarProps, StatusBarClassNames } from './components/StatusBar';
9
- export { GridContextMenu } from './components/GridContextMenu';
10
- export type { GridContextMenuProps, GridContextMenuClassNames } from './components/GridContextMenu';
11
- export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
12
- export type { MarchingAntsOverlayProps } from './components/MarchingAntsOverlay';
13
- export { SideBar } from './components/SideBar';
14
- export type { SideBarProps, SideBarFilterColumn } from './components/SideBar';
15
- export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, } from './utils';
16
- export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, ParseValueResult, AggregationResult, } from './utils';
1
+ export * from './types';
2
+ export * from './utils';
3
+ export * from './constants';
@@ -1,4 +1,3 @@
1
- import * as React from 'react';
2
1
  export type ColumnFilterType = 'none' | 'text' | 'multiSelect' | 'people' | 'date';
3
2
  /** Date range filter value (ISO YYYY-MM-DD strings). Both fields optional for open-ended ranges. */
4
3
  export interface IDateFilterValue {
@@ -40,7 +39,6 @@ export interface IValueParserParams<T = unknown> {
40
39
  column: IColumnDef<T>;
41
40
  }
42
41
  export interface IColumnDef<T = unknown> extends IColumnMeta {
43
- renderCell?: (item: T) => React.ReactNode;
44
42
  compare?: (a: T, b: T) => number;
45
43
  /** Compute cell value from row data (used for filtering, sorting, display when no renderCell). */
46
44
  valueGetter?: (item: T) => unknown;
@@ -52,12 +50,11 @@ export interface IColumnDef<T = unknown> extends IColumnMeta {
52
50
  * Return the parsed value to use, or `undefined` to reject (skip) the change.
53
51
  */
54
52
  valueParser?: (params: IValueParserParams<T>) => unknown;
55
- /** Static or per-row cell inline styles. */
56
- cellStyle?: React.CSSProperties | ((item: T) => React.CSSProperties);
57
53
  /** Whether the cell is editable (per-column or per-row). */
58
54
  editable?: boolean | ((item: T) => boolean);
59
- /** Built-in editor type or custom React component. */
60
- cellEditor?: 'text' | 'select' | 'checkbox' | 'richSelect' | 'date' | React.ComponentType<ICellEditorProps<T>>;
55
+ /** Built-in editor type or framework-specific custom editor (e.g. React component).
56
+ * Core utilities never inspect this value framework packages narrow the type. */
57
+ cellEditor?: unknown;
61
58
  /** When true, custom cell editor is rendered in a popover/popper instead of inline. */
62
59
  cellEditorPopup?: boolean;
63
60
  /** Params passed to the cell editor (e.g. { values: string[] } for select). */
@@ -67,7 +64,6 @@ export interface IColumnDef<T = unknown> extends IColumnMeta {
67
64
  export interface ICellValueChangedEvent<T> {
68
65
  item: T;
69
66
  columnId: string;
70
- field: string;
71
67
  oldValue: unknown;
72
68
  newValue: unknown;
73
69
  rowIndex: number;
@@ -82,12 +78,12 @@ export interface ICellEditorProps<T> {
82
78
  column: IColumnDef<T>;
83
79
  cellEditorParams?: CellEditorParams;
84
80
  }
85
- /** Params for built-in cell editors (e.g. select: { values: string[] }). Extend for custom editors. */
81
+ /** Params for built-in cell editors (e.g. select: { values: string[] }). */
86
82
  export interface CellEditorParams {
83
+ /** Array of allowed values for select/richSelect editors. */
87
84
  values?: unknown[];
88
85
  /** Format a value for display in rich select editor. */
89
86
  formatValue?: (value: unknown) => string;
90
- [key: string]: unknown;
91
87
  }
92
88
  /** Column group for multi-row header (has children, no columnId for data). */
93
89
  export interface IColumnGroupDef<T = unknown> {
@@ -1,5 +1,4 @@
1
- import type { ReactNode } from 'react';
2
- import type { IColumnDef, IColumnGroupDef, ICellValueChangedEvent, IDateFilterValue } from './columnTypes';
1
+ import type { IDateFilterValue } from './columnTypes';
3
2
  /** Row identifier type — grids accept string or number IDs. */
4
3
  export type RowId = string | number;
5
4
  export interface UserLike {
@@ -149,136 +148,16 @@ export interface IOGridApi<T> {
149
148
  selectAll: () => void;
150
149
  /** Deselect all rows. */
151
150
  deselectAll: () => void;
152
- }
153
- /** Props for the OGrid wrapper component (shared across Fluent, Material, Radix). */
154
- export interface IOGridProps<T> {
155
- columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
156
- getRowId: (item: T) => RowId;
157
- data?: T[];
158
- dataSource?: IDataSource<T>;
159
- page?: number;
160
- pageSize?: number;
161
- sort?: {
162
- field: string;
163
- direction: 'asc' | 'desc';
164
- };
165
- filters?: IFilters;
166
- visibleColumns?: Set<string>;
167
- isLoading?: boolean;
168
- onPageChange?: (page: number) => void;
169
- onPageSizeChange?: (size: number) => void;
170
- onSortChange?: (sort: {
171
- field: string;
172
- direction: 'asc' | 'desc';
151
+ /** Clear all filters (shorthand for setFilterModel({})). */
152
+ clearFilters: () => void;
153
+ /** Reset sort to the default (first column, ascending). */
154
+ clearSort: () => void;
155
+ /** Reset all grid state (filters, sort, selection). */
156
+ resetGridState: (options?: {
157
+ keepSelection?: boolean;
173
158
  }) => void;
174
- onFiltersChange?: (filters: IFilters) => void;
175
- onVisibleColumnsChange?: (cols: Set<string>) => void;
176
- columnOrder?: string[];
177
- onColumnOrderChange?: (order: string[]) => void;
178
- /** Called when a column is resized by the user. */
179
- onColumnResized?: (columnId: string, width: number) => void;
180
- /** Called when a column is pinned or unpinned. */
181
- onColumnPinned?: (columnId: string, pinned: 'left' | 'right' | null) => void;
182
- freezeRows?: number;
183
- freezeCols?: number;
184
- editable?: boolean;
185
- /** Enable spreadsheet-like cell selection (active cell, range, fill handle, clipboard, context menu). Default: true. */
186
- cellSelection?: boolean;
187
- onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
188
- onUndo?: () => void;
189
- onRedo?: () => void;
190
- canUndo?: boolean;
191
- canRedo?: boolean;
192
- rowSelection?: RowSelectionMode;
193
- selectedRows?: Set<RowId>;
194
- onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
195
- statusBar?: boolean | IStatusBarProps;
196
- defaultPageSize?: number;
197
- defaultSortBy?: string;
198
- defaultSortDirection?: 'asc' | 'desc';
199
- toolbar?: ReactNode;
200
- /** Secondary toolbar row rendered below the primary toolbar (e.g. active filter chips). */
201
- toolbarBelow?: ReactNode;
202
- emptyState?: {
203
- message?: ReactNode;
204
- render?: () => ReactNode;
205
- };
206
- entityLabelPlural?: string;
207
- className?: string;
208
- /** Where the column chooser renders.
209
- * - `true` or `'toolbar'` (default): column chooser button in the toolbar strip.
210
- * - `'sidebar'`: column chooser only available via the sidebar columns panel.
211
- * - `false`: column chooser hidden entirely. */
212
- columnChooser?: boolean | 'toolbar' | 'sidebar';
213
- layoutMode?: 'content' | 'fill';
214
- /** When true, horizontal scrolling is suppressed (overflow-x hidden). */
215
- suppressHorizontalScroll?: boolean;
216
- /** Side bar configuration. `true` shows default panels (columns + filters). Pass ISideBarDef for options. */
217
- sideBar?: boolean | ISideBarDef;
218
- /** Page size options shown in the pagination dropdown. Default: [10, 20, 50, 100]. */
219
- pageSizeOptions?: number[];
220
- /** Fires once when the grid first renders with data (useful for restoring column state). */
221
- onFirstDataRendered?: () => void;
222
- /** Called when server-side fetchPage fails. */
223
- onError?: (error: unknown) => void;
224
- 'aria-label'?: string;
225
- 'aria-labelledby'?: string;
226
- }
227
- /** Props passed from useOGrid to the framework-specific DataGridTable. */
228
- export interface IOGridDataGridProps<T> {
229
- items: T[];
230
- columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
231
- getRowId: (item: T) => RowId;
232
- sortBy?: string;
233
- sortDirection: 'asc' | 'desc';
234
- onColumnSort: (columnKey: string) => void;
235
- visibleColumns: Set<string>;
236
- /** Optional column display order (column ids). When set, visible columns are ordered by this array. */
237
- columnOrder?: string[];
238
- onColumnOrderChange?: (order: string[]) => void;
239
- /** Called when a column is resized by the user. */
240
- onColumnResized?: (columnId: string, width: number) => void;
241
- /** Called when a column is pinned or unpinned. */
242
- onColumnPinned?: (columnId: string, pinned: 'left' | 'right' | null) => void;
243
- /** Runtime pin overrides (from restored state or programmatic changes). */
244
- pinnedColumns?: Record<string, 'left' | 'right'>;
245
- /** Initial column width overrides (from restored state). */
246
- initialColumnWidths?: Record<string, number>;
247
- /** Number of rows to freeze (sticky), e.g. 1 = header row. */
248
- freezeRows?: number;
249
- /** Number of data columns to freeze (sticky left). */
250
- freezeCols?: number;
251
- layoutMode?: 'content' | 'fill';
252
- /** When true, horizontal scrolling is suppressed (overflow-x hidden). */
253
- suppressHorizontalScroll?: boolean;
254
- isLoading?: boolean;
255
- loadingMessage?: string;
256
- editable?: boolean;
257
- /** Enable spreadsheet-like cell selection. Default: true. */
258
- cellSelection?: boolean;
259
- onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
260
- onUndo?: () => void;
261
- onRedo?: () => void;
262
- canUndo?: boolean;
263
- canRedo?: boolean;
264
- rowSelection?: RowSelectionMode;
265
- selectedRows?: Set<RowId>;
266
- onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
267
- statusBar?: IStatusBarProps;
268
- /** Unified filter model (discriminated union values). */
269
- filters: IFilters;
270
- /** Single callback for all filter changes. Pass undefined to clear. */
271
- onFilterChange: (key: string, value: FilterValue | undefined) => void;
272
- filterOptions: Record<string, string[]>;
273
- loadingFilterOptions: Record<string, boolean>;
274
- peopleSearch?: (query: string) => Promise<UserLike[]>;
275
- getUserByEmail?: (email: string) => Promise<UserLike | undefined>;
276
- emptyState?: {
277
- onClearAll: () => void;
278
- hasActiveFilters: boolean;
279
- message?: ReactNode;
280
- render?: () => ReactNode;
281
- };
282
- 'aria-label'?: string;
283
- 'aria-labelledby'?: string;
159
+ /** Get the currently displayed (paginated) rows. */
160
+ getDisplayedRows: () => T[];
161
+ /** Re-trigger a data fetch (server-side only; no-op for client-side). */
162
+ refreshData: () => void;
284
163
  }
@@ -1,3 +1,3 @@
1
- export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, IDateFilterValue, HeaderCell, HeaderRow, } from './columnTypes';
2
- export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridDataGridProps, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, } from './dataGridTypes';
3
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
1
+ export type { ColumnFilterType, IDateFilterValue, IColumnFilterDef, IColumnMeta, IValueParserParams, IColumnDef, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IColumnGroupDef, HeaderCell, HeaderRow, IColumnDefinition, } from './columnTypes';
2
+ export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IOGridApi, } from './dataGridTypes';
3
+ export { toUserLike, isInSelectionRange, normalizeSelectionRange, } from './dataGridTypes';
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Checks whether a given row index falls within a selection range.
3
+ * O(1) — used by React.memo comparators to skip unchanged rows.
4
+ */
5
+ export declare function isRowInRange(range: {
6
+ startRow: number;
7
+ endRow: number;
8
+ } | null, rowIndex: number): boolean;
9
+ /**
10
+ * Props for GridRow comparator (generic to work with all 3 UI frameworks).
11
+ * Includes both render props and comparator-only props used to decide re-renders.
12
+ */
13
+ export interface GridRowComparatorProps {
14
+ item: unknown;
15
+ rowIndex: number;
16
+ rowId: string | number;
17
+ isSelected: boolean;
18
+ hasCheckboxCol: boolean;
19
+ selectionRange: {
20
+ startRow: number;
21
+ endRow: number;
22
+ startCol: number;
23
+ endCol: number;
24
+ } | null;
25
+ activeCell: {
26
+ rowIndex: number;
27
+ columnIndex: number;
28
+ } | null;
29
+ cutRange: {
30
+ startRow: number;
31
+ endRow: number;
32
+ startCol: number;
33
+ endCol: number;
34
+ } | null;
35
+ copyRange: {
36
+ startRow: number;
37
+ endRow: number;
38
+ startCol: number;
39
+ endCol: number;
40
+ } | null;
41
+ isDragging: boolean;
42
+ editingRowId: string | number | null;
43
+ visibleCols?: unknown;
44
+ columnMeta?: unknown;
45
+ cellClassMap?: unknown;
46
+ columnLayouts?: unknown;
47
+ }
48
+ /**
49
+ * Shared React.memo comparator for GridRow components across all 3 UI packages.
50
+ * Skips re-render for rows unaffected by selection/editing/interaction changes.
51
+ *
52
+ * Used by:
53
+ * - packages/radix/src/DataGridTable/DataGridTable.tsx
54
+ * - packages/fluent/src/DataGridTable/DataGridTable.tsx
55
+ * - packages/material/src/DataGridTable/DataGridTable.tsx
56
+ */
57
+ export declare function areGridRowPropsEqual(prev: GridRowComparatorProps, next: GridRowComparatorProps): boolean;