@alaarab/ogrid-core 1.9.0 → 2.0.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 (87) hide show
  1. package/README.md +6 -20
  2. package/dist/esm/index.js +5 -15
  3. package/dist/esm/types/index.js +2 -1
  4. package/dist/esm/utils/clientSideData.js +1 -1
  5. package/dist/esm/utils/gridRowComparator.js +11 -1
  6. package/dist/esm/utils/index.js +1 -2
  7. package/dist/types/index.d.ts +3 -23
  8. package/dist/types/types/columnTypes.d.ts +3 -6
  9. package/dist/types/types/dataGridTypes.d.ts +1 -150
  10. package/dist/types/types/index.d.ts +3 -3
  11. package/dist/types/utils/gridRowComparator.d.ts +8 -0
  12. package/dist/types/utils/index.d.ts +1 -3
  13. package/package.json +6 -24
  14. package/dist/esm/components/BaseInlineCellEditor.js +0 -112
  15. package/dist/esm/components/CellErrorBoundary.js +0 -43
  16. package/dist/esm/components/EmptyState.js +0 -19
  17. package/dist/esm/components/GridContextMenu.js +0 -35
  18. package/dist/esm/components/MarchingAntsOverlay.js +0 -110
  19. package/dist/esm/components/OGridLayout.js +0 -91
  20. package/dist/esm/components/SideBar.js +0 -122
  21. package/dist/esm/components/StatusBar.js +0 -6
  22. package/dist/esm/hooks/index.js +0 -25
  23. package/dist/esm/hooks/useActiveCell.js +0 -62
  24. package/dist/esm/hooks/useCellEditing.js +0 -15
  25. package/dist/esm/hooks/useCellSelection.js +0 -324
  26. package/dist/esm/hooks/useClipboard.js +0 -162
  27. package/dist/esm/hooks/useColumnChooserState.js +0 -62
  28. package/dist/esm/hooks/useColumnHeaderFilterState.js +0 -180
  29. package/dist/esm/hooks/useColumnResize.js +0 -92
  30. package/dist/esm/hooks/useContextMenu.js +0 -21
  31. package/dist/esm/hooks/useDataGridState.js +0 -314
  32. package/dist/esm/hooks/useDateFilterState.js +0 -34
  33. package/dist/esm/hooks/useDebounce.js +0 -35
  34. package/dist/esm/hooks/useFillHandle.js +0 -196
  35. package/dist/esm/hooks/useFilterOptions.js +0 -40
  36. package/dist/esm/hooks/useInlineCellEditorState.js +0 -44
  37. package/dist/esm/hooks/useKeyboardNavigation.js +0 -420
  38. package/dist/esm/hooks/useLatestRef.js +0 -11
  39. package/dist/esm/hooks/useMultiSelectFilterState.js +0 -59
  40. package/dist/esm/hooks/useOGrid.js +0 -467
  41. package/dist/esm/hooks/usePeopleFilterState.js +0 -68
  42. package/dist/esm/hooks/useRichSelectState.js +0 -58
  43. package/dist/esm/hooks/useRowSelection.js +0 -78
  44. package/dist/esm/hooks/useSideBarState.js +0 -39
  45. package/dist/esm/hooks/useTableLayout.js +0 -77
  46. package/dist/esm/hooks/useTextFilterState.js +0 -25
  47. package/dist/esm/hooks/useUndoRedo.js +0 -83
  48. package/dist/esm/storybook/index.js +0 -1
  49. package/dist/esm/storybook/mockData.js +0 -73
  50. package/dist/esm/utils/dataGridViewModel.js +0 -233
  51. package/dist/types/components/BaseInlineCellEditor.d.ts +0 -33
  52. package/dist/types/components/CellErrorBoundary.d.ts +0 -25
  53. package/dist/types/components/EmptyState.d.ts +0 -26
  54. package/dist/types/components/GridContextMenu.d.ts +0 -18
  55. package/dist/types/components/MarchingAntsOverlay.d.ts +0 -15
  56. package/dist/types/components/OGridLayout.d.ts +0 -37
  57. package/dist/types/components/SideBar.d.ts +0 -30
  58. package/dist/types/components/StatusBar.d.ts +0 -24
  59. package/dist/types/hooks/index.d.ts +0 -48
  60. package/dist/types/hooks/useActiveCell.d.ts +0 -13
  61. package/dist/types/hooks/useCellEditing.d.ts +0 -16
  62. package/dist/types/hooks/useCellSelection.d.ts +0 -22
  63. package/dist/types/hooks/useClipboard.d.ts +0 -30
  64. package/dist/types/hooks/useColumnChooserState.d.ts +0 -27
  65. package/dist/types/hooks/useColumnHeaderFilterState.d.ts +0 -73
  66. package/dist/types/hooks/useColumnResize.d.ts +0 -23
  67. package/dist/types/hooks/useContextMenu.d.ts +0 -19
  68. package/dist/types/hooks/useDataGridState.d.ts +0 -137
  69. package/dist/types/hooks/useDateFilterState.d.ts +0 -19
  70. package/dist/types/hooks/useDebounce.d.ts +0 -9
  71. package/dist/types/hooks/useFillHandle.d.ts +0 -33
  72. package/dist/types/hooks/useFilterOptions.d.ts +0 -16
  73. package/dist/types/hooks/useInlineCellEditorState.d.ts +0 -24
  74. package/dist/types/hooks/useKeyboardNavigation.d.ts +0 -47
  75. package/dist/types/hooks/useLatestRef.d.ts +0 -6
  76. package/dist/types/hooks/useMultiSelectFilterState.d.ts +0 -24
  77. package/dist/types/hooks/useOGrid.d.ts +0 -52
  78. package/dist/types/hooks/usePeopleFilterState.d.ts +0 -25
  79. package/dist/types/hooks/useRichSelectState.d.ts +0 -22
  80. package/dist/types/hooks/useRowSelection.d.ts +0 -22
  81. package/dist/types/hooks/useSideBarState.d.ts +0 -20
  82. package/dist/types/hooks/useTableLayout.d.ts +0 -27
  83. package/dist/types/hooks/useTextFilterState.d.ts +0 -16
  84. package/dist/types/hooks/useUndoRedo.d.ts +0 -23
  85. package/dist/types/storybook/index.d.ts +0 -2
  86. package/dist/types/storybook/mockData.d.ts +0 -37
  87. package/dist/types/utils/dataGridViewModel.d.ts +0 -169
package/README.md CHANGED
@@ -1,11 +1,10 @@
1
1
  <p align="center">
2
- <strong>OGrid Core</strong> — Headless types, hooks, and utilities for OGrid data grids.
2
+ <strong>OGrid Core</strong> — Pure TypeScript types, algorithms, and utilities for OGrid data grids.
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
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
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/React-17%20%7C%2018%20%7C%2019-blue" alt="React 17, 18, 19" />
9
8
  <img src="https://img.shields.io/badge/TypeScript-strict-blue" alt="TypeScript strict" />
10
9
  </p>
11
10
 
@@ -15,20 +14,11 @@
15
14
 
16
15
  ---
17
16
 
18
- Framework-agnostic foundation for [OGrid](https://github.com/alaarab/ogrid) data grids. You typically don't need to install this directly the UI packages ([`@alaarab/ogrid`](https://www.npmjs.com/package/@alaarab/ogrid), [`@alaarab/ogrid-fluent`](https://www.npmjs.com/package/@alaarab/ogrid-fluent), [`@alaarab/ogrid-material`](https://www.npmjs.com/package/@alaarab/ogrid-material)) re-export everything from core.
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).
19
18
 
20
- ## What's Inside
21
-
22
- ### Hooks
19
+ You typically don't need to install this directly — the UI packages re-export everything from core.
23
20
 
24
- - `useOGrid` — Orchestrator: pagination, sorting, filtering, visibility, editing, row selection, status bar, grid API
25
- - `useDataGridState` — All DataGridTable state: layout, selection, editing, interaction, context menu, view models
26
- - `useColumnHeaderFilterState` — Filter popover (open, temp values, apply/clear, people search debounce)
27
- - `useColumnChooserState` — Column visibility dropdown
28
- - `useInlineCellEditorState` — Inline cell editor
29
- - `useRichSelectState` — Searchable rich select dropdown
30
- - `useSideBarState` — Side bar panel management
31
- - `useActiveCell`, `useCellSelection`, `useCellEditing`, `useRowSelection`, `useKeyboardNavigation`, `useClipboard`, `useFillHandle`, `useUndoRedo`, `useContextMenu`, `useColumnResize`, `useFilterOptions`, `useDebounce`
21
+ ## What's Inside
32
22
 
33
23
  ### Types
34
24
 
@@ -36,11 +26,7 @@ Framework-agnostic foundation for [OGrid](https://github.com/alaarab/ogrid) data
36
26
 
37
27
  ### Utilities
38
28
 
39
- `processClientSideData` · `exportToCsv` · `getCellValue` · `flattenColumns` · `buildHeaderRows` · `getPaginationViewModel` · `getHeaderFilterConfig` · `getCellRenderDescriptor` · `computeAggregations` · `formatShortcut` · `GRID_CONTEXT_MENU_ITEMS`
40
-
41
- ### Headless Components
42
-
43
- `OGridLayout` · `StatusBar` · `GridContextMenu` · `SideBar` · `MarchingAntsOverlay`
29
+ `getCellValue` · `buildHeaderRows` · `parseValue` · `normalizeSelectionRange` · `isInSelectionRange` · `toUserLike`
44
30
 
45
31
  ## Install
46
32
 
@@ -48,7 +34,7 @@ Framework-agnostic foundation for [OGrid](https://github.com/alaarab/ogrid) data
48
34
  npm install @alaarab/ogrid-core
49
35
  ```
50
36
 
51
- Only peer dep is `react ^17 || ^18 || ^19`.
37
+ No peer dependencies. No framework dependencies.
52
38
 
53
39
  ## Documentation
54
40
 
package/dist/esm/index.js CHANGED
@@ -1,16 +1,6 @@
1
+ // Types
2
+ export * from './types';
3
+ // Utils
4
+ export * from './utils';
1
5
  // Constants
2
- export { CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from './constants';
3
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
4
- // Hooks
5
- export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useLatestRef, } from './hooks';
6
- // Components
7
- export { OGridLayout } from './components/OGridLayout';
8
- export { StatusBar } from './components/StatusBar';
9
- export { BaseInlineCellEditor, editorWrapperStyle, editorInputStyle, richSelectWrapperStyle, richSelectDropdownStyle, richSelectOptionStyle, richSelectOptionHighlightedStyle, richSelectNoMatchesStyle, selectEditorStyle, } from './components/BaseInlineCellEditor';
10
- export { GridContextMenu } from './components/GridContextMenu';
11
- export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
12
- export { SideBar } from './components/SideBar';
13
- export { CellErrorBoundary } from './components/CellErrorBoundary';
14
- export { EmptyState } from './components/EmptyState';
15
- // Utilities
16
- 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, areGridRowPropsEqual, } from './utils';
6
+ export * from './constants';
@@ -1 +1,2 @@
1
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
1
+ // Utility functions
2
+ export { toUserLike, isInSelectionRange, normalizeSelectionRange, } from './dataGridTypes';
@@ -60,7 +60,7 @@ export function processClientSideData(data, columns, filters, sortBy, sortDirect
60
60
  }
61
61
  }
62
62
  }
63
- let rows = predicates.length > 0
63
+ const rows = predicates.length > 0
64
64
  ? data.filter((row) => {
65
65
  for (let i = 0; i < predicates.length; i++) {
66
66
  if (!predicates[i](row))
@@ -1,4 +1,14 @@
1
- import { isRowInRange } from './dataGridViewModel';
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
+ }
2
12
  /**
3
13
  * Shared React.memo comparator for GridRow components across all 3 UI packages.
4
14
  * Skips re-render for rows unaffected by selection/editing/interaction changes.
@@ -6,8 +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';
13
- export { areGridRowPropsEqual } from './gridRowComparator';
12
+ export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
@@ -1,23 +1,3 @@
1
- export { CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from './constants';
2
- 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';
3
- export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
4
- export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useLatestRef, } from './hooks';
5
- export type { UseFilterOptionsResult, UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, 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, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, UseTableLayoutParams, UseTableLayoutResult, } from './hooks';
6
- export { OGridLayout } from './components/OGridLayout';
7
- export type { OGridLayoutProps } from './components/OGridLayout';
8
- export { StatusBar } from './components/StatusBar';
9
- export type { StatusBarProps, StatusBarClassNames } from './components/StatusBar';
10
- export { BaseInlineCellEditor, editorWrapperStyle, editorInputStyle, richSelectWrapperStyle, richSelectDropdownStyle, richSelectOptionStyle, richSelectOptionHighlightedStyle, richSelectNoMatchesStyle, selectEditorStyle, } from './components/BaseInlineCellEditor';
11
- export type { BaseInlineCellEditorProps } from './components/BaseInlineCellEditor';
12
- export { GridContextMenu } from './components/GridContextMenu';
13
- export type { GridContextMenuProps, GridContextMenuClassNames } from './components/GridContextMenu';
14
- export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
15
- export type { MarchingAntsOverlayProps } from './components/MarchingAntsOverlay';
16
- export { SideBar } from './components/SideBar';
17
- export type { SideBarProps, SideBarFilterColumn } from './components/SideBar';
18
- export { CellErrorBoundary } from './components/CellErrorBoundary';
19
- export type { CellErrorBoundaryProps } from './components/CellErrorBoundary';
20
- export { EmptyState } from './components/EmptyState';
21
- export type { EmptyStateProps } from './components/EmptyState';
22
- 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, areGridRowPropsEqual, } from './utils';
23
- export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, ParseValueResult, AggregationResult, GridRowComparatorProps, } 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). */
@@ -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 {
@@ -162,151 +161,3 @@ export interface IOGridApi<T> {
162
161
  /** Re-trigger a data fetch (server-side only; no-op for client-side). */
163
162
  refreshData: () => void;
164
163
  }
165
- /** Base props shared by both client-side and server-side OGrid modes. */
166
- interface IOGridBaseProps<T> {
167
- columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
168
- getRowId: (item: T) => RowId;
169
- page?: number;
170
- pageSize?: number;
171
- sort?: {
172
- field: string;
173
- direction: 'asc' | 'desc';
174
- };
175
- filters?: IFilters;
176
- visibleColumns?: Set<string>;
177
- isLoading?: boolean;
178
- onPageChange?: (page: number) => void;
179
- onPageSizeChange?: (size: number) => void;
180
- onSortChange?: (sort: {
181
- field: string;
182
- direction: 'asc' | 'desc';
183
- }) => void;
184
- onFiltersChange?: (filters: IFilters) => void;
185
- onVisibleColumnsChange?: (cols: Set<string>) => void;
186
- columnOrder?: string[];
187
- onColumnOrderChange?: (order: string[]) => void;
188
- /** Called when a column is resized by the user. */
189
- onColumnResized?: (columnId: string, width: number) => void;
190
- /** Called when a column is pinned or unpinned. */
191
- onColumnPinned?: (columnId: string, pinned: 'left' | 'right' | null) => void;
192
- freezeRows?: number;
193
- freezeCols?: number;
194
- editable?: boolean;
195
- /** Enable spreadsheet-like cell selection (active cell, range, fill handle, clipboard, context menu). Default: true. */
196
- cellSelection?: boolean;
197
- onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
198
- onUndo?: () => void;
199
- onRedo?: () => void;
200
- canUndo?: boolean;
201
- canRedo?: boolean;
202
- rowSelection?: RowSelectionMode;
203
- selectedRows?: Set<RowId>;
204
- onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
205
- statusBar?: boolean | IStatusBarProps;
206
- defaultPageSize?: number;
207
- defaultSortBy?: string;
208
- defaultSortDirection?: 'asc' | 'desc';
209
- toolbar?: ReactNode;
210
- /** Secondary toolbar row rendered below the primary toolbar (e.g. active filter chips). */
211
- toolbarBelow?: ReactNode;
212
- emptyState?: {
213
- message?: ReactNode;
214
- render?: () => ReactNode;
215
- };
216
- entityLabelPlural?: string;
217
- className?: string;
218
- /** Where the column chooser renders.
219
- * - `true` or `'toolbar'` (default): column chooser button in the toolbar strip.
220
- * - `'sidebar'`: column chooser only available via the sidebar columns panel.
221
- * - `false`: column chooser hidden entirely. */
222
- columnChooser?: boolean | 'toolbar' | 'sidebar';
223
- layoutMode?: 'content' | 'fill';
224
- /** When true, horizontal scrolling is suppressed (overflow-x hidden). */
225
- suppressHorizontalScroll?: boolean;
226
- /** Side bar configuration. `true` shows default panels (columns + filters). Pass ISideBarDef for options. */
227
- sideBar?: boolean | ISideBarDef;
228
- /** Page size options shown in the pagination dropdown. Default: [10, 20, 50, 100]. */
229
- pageSizeOptions?: number[];
230
- /** Fires once when the grid first renders with data (useful for restoring column state). */
231
- onFirstDataRendered?: () => void;
232
- /** Called when server-side fetchPage fails. */
233
- onError?: (error: unknown) => void;
234
- /** Called when a cell renderer or custom editor throws an error. */
235
- onCellError?: (error: Error, errorInfo: React.ErrorInfo) => void;
236
- 'aria-label'?: string;
237
- 'aria-labelledby'?: string;
238
- }
239
- /** Client-side mode: pass a data array. */
240
- export interface IOGridClientProps<T> extends IOGridBaseProps<T> {
241
- data: T[];
242
- dataSource?: never;
243
- }
244
- /** Server-side mode: pass a dataSource. */
245
- export interface IOGridServerProps<T> extends IOGridBaseProps<T> {
246
- data?: never;
247
- dataSource: IDataSource<T>;
248
- }
249
- /** Props for the OGrid wrapper component (shared across Fluent, Material, Radix).
250
- * Must provide either `data` (client-side) or `dataSource` (server-side), not both. */
251
- export type IOGridProps<T> = IOGridClientProps<T> | IOGridServerProps<T>;
252
- /** Props passed from useOGrid to the framework-specific DataGridTable. */
253
- export interface IOGridDataGridProps<T> {
254
- items: T[];
255
- columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
256
- getRowId: (item: T) => RowId;
257
- sortBy?: string;
258
- sortDirection: 'asc' | 'desc';
259
- onColumnSort: (columnKey: string) => void;
260
- visibleColumns: Set<string>;
261
- /** Optional column display order (column ids). When set, visible columns are ordered by this array. */
262
- columnOrder?: string[];
263
- onColumnOrderChange?: (order: string[]) => void;
264
- /** Called when a column is resized by the user. */
265
- onColumnResized?: (columnId: string, width: number) => void;
266
- /** Called when a column is pinned or unpinned. */
267
- onColumnPinned?: (columnId: string, pinned: 'left' | 'right' | null) => void;
268
- /** Runtime pin overrides (from restored state or programmatic changes). */
269
- pinnedColumns?: Record<string, 'left' | 'right'>;
270
- /** Initial column width overrides (from restored state). */
271
- initialColumnWidths?: Record<string, number>;
272
- /** Number of rows to freeze (sticky), e.g. 1 = header row. */
273
- freezeRows?: number;
274
- /** Number of data columns to freeze (sticky left). */
275
- freezeCols?: number;
276
- layoutMode?: 'content' | 'fill';
277
- /** When true, horizontal scrolling is suppressed (overflow-x hidden). */
278
- suppressHorizontalScroll?: boolean;
279
- isLoading?: boolean;
280
- loadingMessage?: string;
281
- editable?: boolean;
282
- /** Enable spreadsheet-like cell selection. Default: true. */
283
- cellSelection?: boolean;
284
- onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
285
- onUndo?: () => void;
286
- onRedo?: () => void;
287
- canUndo?: boolean;
288
- canRedo?: boolean;
289
- rowSelection?: RowSelectionMode;
290
- selectedRows?: Set<RowId>;
291
- onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
292
- statusBar?: IStatusBarProps;
293
- /** Unified filter model (discriminated union values). */
294
- filters: IFilters;
295
- /** Single callback for all filter changes. Pass undefined to clear. */
296
- onFilterChange: (key: string, value: FilterValue | undefined) => void;
297
- filterOptions: Record<string, string[]>;
298
- loadingFilterOptions: Record<string, boolean>;
299
- peopleSearch?: (query: string) => Promise<UserLike[]>;
300
- getUserByEmail?: (email: string) => Promise<UserLike | undefined>;
301
- emptyState?: {
302
- onClearAll: () => void;
303
- hasActiveFilters: boolean;
304
- message?: ReactNode;
305
- render?: () => ReactNode;
306
- };
307
- /** Called when a cell renderer or custom editor throws an error. */
308
- onCellError?: (error: Error, errorInfo: React.ErrorInfo) => void;
309
- 'aria-label'?: string;
310
- 'aria-labelledby'?: string;
311
- }
312
- export {};
@@ -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, IOGridClientProps, IOGridServerProps, 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';
@@ -1,3 +1,11 @@
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;
1
9
  /**
2
10
  * Props for GridRow comparator (generic to work with all 3 UI frameworks).
3
11
  * Includes both render props and comparator-only props used to decide re-renders.
@@ -7,8 +7,6 @@ export { getDataGridStatusBarConfig } from './dataGridStatusBar';
7
7
  export { getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, } from './paginationHelpers';
8
8
  export type { PaginationViewModel } from './paginationHelpers';
9
9
  export { GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from './gridContextMenuHelpers';
10
- export { getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './dataGridViewModel';
11
- export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, } from './dataGridViewModel';
12
10
  export type { CsvColumn } from './exportToCsv';
13
11
  export type { StatusBarPart, StatusBarPartsInput } from './statusBarHelpers';
14
12
  export type { GridContextMenuItem, GridContextMenuHandlerProps } from './gridContextMenuHelpers';
@@ -17,5 +15,5 @@ export type { ParseValueResult } from './valueParsers';
17
15
  export { computeAggregations } from './aggregationUtils';
18
16
  export type { AggregationResult } from './aggregationUtils';
19
17
  export { processClientSideData } from './clientSideData';
20
- export { areGridRowPropsEqual } from './gridRowComparator';
18
+ export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
21
19
  export type { GridRowComparatorProps } from './gridRowComparator';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-core",
3
- "version": "1.9.0",
4
- "description": "OGrid core – framework-agnostic types, hooks, and utilities for OGrid data tables.",
3
+ "version": "2.0.0",
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",
7
7
  "types": "dist/types/index.d.ts",
@@ -16,28 +16,10 @@
16
16
  "build": "rimraf dist && tsc -p tsconfig.build.json",
17
17
  "test": "jest"
18
18
  },
19
- "keywords": [
20
- "ogrid",
21
- "datatable",
22
- "react",
23
- "typescript",
24
- "grid",
25
- "core"
26
- ],
19
+ "keywords": ["ogrid", "datatable", "typescript", "grid", "core"],
27
20
  "author": "Ala Arab",
28
21
  "license": "MIT",
29
- "files": [
30
- "dist",
31
- "README.md",
32
- "LICENSE"
33
- ],
34
- "engines": {
35
- "node": ">=18"
36
- },
37
- "peerDependencies": {
38
- "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
39
- },
40
- "publishConfig": {
41
- "access": "public"
42
- }
22
+ "files": ["dist", "README.md", "LICENSE"],
23
+ "engines": { "node": ">=18" },
24
+ "publishConfig": { "access": "public" }
43
25
  }
@@ -1,112 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { useInlineCellEditorState, useRichSelectState } from '../hooks';
4
- // ── Shared editor style constants (used across all 3 UI packages) ──
5
- export const editorWrapperStyle = {
6
- width: '100%',
7
- height: '100%',
8
- display: 'flex',
9
- alignItems: 'center',
10
- padding: '6px 10px',
11
- boxSizing: 'border-box',
12
- overflow: 'hidden',
13
- minWidth: 0,
14
- };
15
- export const editorInputStyle = {
16
- width: '100%',
17
- padding: 0,
18
- border: 'none',
19
- background: 'transparent',
20
- color: 'inherit',
21
- font: 'inherit',
22
- fontSize: '13px',
23
- outline: 'none',
24
- minWidth: 0,
25
- };
26
- export const richSelectWrapperStyle = {
27
- ...editorWrapperStyle,
28
- position: 'relative',
29
- };
30
- export const richSelectDropdownStyle = {
31
- position: 'absolute',
32
- top: '100%',
33
- left: 0,
34
- right: 0,
35
- maxHeight: 200,
36
- overflowY: 'auto',
37
- background: 'var(--ogrid-bg, #fff)',
38
- border: '1px solid var(--ogrid-border, #ccc)',
39
- zIndex: 10,
40
- boxShadow: '0 4px 16px rgba(0,0,0,0.2)',
41
- };
42
- export const richSelectOptionStyle = {
43
- padding: '6px 8px',
44
- cursor: 'pointer',
45
- color: 'var(--ogrid-fg, #242424)',
46
- };
47
- export const richSelectOptionHighlightedStyle = {
48
- ...richSelectOptionStyle,
49
- background: 'var(--ogrid-bg-hover, #e8f0fe)',
50
- };
51
- export const richSelectNoMatchesStyle = {
52
- padding: '6px 8px',
53
- color: 'var(--ogrid-muted, #999)',
54
- };
55
- export const selectEditorStyle = {
56
- width: '100%',
57
- padding: 0,
58
- border: 'none',
59
- background: 'transparent',
60
- color: 'inherit',
61
- font: 'inherit',
62
- fontSize: '13px',
63
- cursor: 'pointer',
64
- outline: 'none',
65
- };
66
- /**
67
- * Base inline cell editor with shared logic for all editor types except checkbox and select
68
- * (which are framework-specific). Used by all 3 UI packages to avoid duplication.
69
- *
70
- * Usage:
71
- * - Radix: Pass Radix Checkbox/native select via render props
72
- * - Fluent: Pass Fluent Checkbox/Select via render props
73
- * - Material: Pass MUI Checkbox/Select via render props
74
- */
75
- export function BaseInlineCellEditor(props) {
76
- const { value, column, editorType, onCommit, onCancel, renderCheckbox, renderSelect } = props;
77
- const wrapperRef = React.useRef(null);
78
- const { localValue, setLocalValue, handleKeyDown, handleBlur, commit, cancel } = useInlineCellEditorState({ value, editorType, onCommit, onCancel });
79
- const richSelectValues = column.cellEditorParams?.values ?? [];
80
- const richSelectFormatValue = column.cellEditorParams?.formatValue;
81
- const richSelect = useRichSelectState({
82
- values: richSelectValues,
83
- formatValue: richSelectFormatValue,
84
- initialValue: value,
85
- onCommit,
86
- onCancel,
87
- });
88
- React.useEffect(() => {
89
- const input = wrapperRef.current?.querySelector('input');
90
- input?.focus();
91
- }, []);
92
- // Rich select (shared across all frameworks)
93
- if (editorType === 'richSelect') {
94
- return (_jsxs("div", { ref: wrapperRef, style: richSelectWrapperStyle, children: [_jsx("input", { type: "text", value: richSelect.searchText, onChange: (e) => richSelect.setSearchText(e.target.value), onKeyDown: richSelect.handleKeyDown, placeholder: "Search...", autoFocus: true, style: editorInputStyle }), _jsxs("div", { style: richSelectDropdownStyle, role: "listbox", children: [richSelect.filteredValues.map((v, i) => (_jsx("div", { role: "option", "aria-selected": i === richSelect.highlightedIndex, onClick: () => richSelect.selectValue(v), style: i === richSelect.highlightedIndex ? richSelectOptionHighlightedStyle : richSelectOptionStyle, children: richSelect.getDisplayText(v) }, String(v)))), richSelect.filteredValues.length === 0 && (_jsx("div", { style: richSelectNoMatchesStyle, children: "No matches" }))] })] }));
95
- }
96
- // Checkbox (framework-specific)
97
- if (editorType === 'checkbox') {
98
- const checked = value === true;
99
- return _jsx(_Fragment, { children: renderCheckbox(checked, (val) => commit(val), cancel) });
100
- }
101
- // Select (framework-specific)
102
- if (editorType === 'select') {
103
- const values = column.cellEditorParams?.values ?? [];
104
- return _jsx(_Fragment, { children: renderSelect(value, values, commit, cancel) });
105
- }
106
- // Date editor (shared across all frameworks)
107
- if (editorType === 'date') {
108
- return (_jsx("div", { ref: wrapperRef, style: editorWrapperStyle, children: _jsx("input", { type: "date", value: localValue, onChange: (e) => setLocalValue(e.target.value), onBlur: handleBlur, onKeyDown: handleKeyDown, style: editorInputStyle, autoFocus: true }) }));
109
- }
110
- // Text editor (default, shared across all frameworks)
111
- return (_jsx("div", { ref: wrapperRef, style: editorWrapperStyle, children: _jsx("input", { type: "text", value: localValue, onChange: (e) => setLocalValue(e.target.value), onBlur: handleBlur, onKeyDown: handleKeyDown, style: editorInputStyle, autoFocus: true }) }));
112
- }
@@ -1,43 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- const DEFAULT_FALLBACK_STYLE = {
4
- color: '#d32f2f',
5
- fontSize: '0.75rem',
6
- padding: '2px 4px',
7
- };
8
- /**
9
- * Error boundary for cell renderers and custom editors.
10
- * Prevents a runtime error in a cell from crashing the entire grid.
11
- */
12
- export class CellErrorBoundary extends React.Component {
13
- constructor(props) {
14
- super(props);
15
- this.state = { hasError: false };
16
- }
17
- static getDerivedStateFromError() {
18
- return { hasError: true };
19
- }
20
- componentDidCatch(error, errorInfo) {
21
- if (this.props.onError) {
22
- this.props.onError(error, errorInfo);
23
- }
24
- }
25
- componentDidUpdate(prevProps) {
26
- // Reset error state when children change (e.g., navigating to a different cell)
27
- if (prevProps.children !== this.props.children && this.state.hasError) {
28
- this.setState({ hasError: false });
29
- }
30
- }
31
- resetErrorBoundary() {
32
- this.setState({ hasError: false });
33
- }
34
- render() {
35
- if (this.state.hasError) {
36
- if (this.props.fallback !== undefined) {
37
- return this.props.fallback;
38
- }
39
- return _jsx("span", { style: DEFAULT_FALLBACK_STYLE, children: "\u26A0 Error" });
40
- }
41
- return this.props.children;
42
- }
43
- }
@@ -1,19 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- const clearButtonStyle = { background: 'none', border: 'none', color: 'inherit', textDecoration: 'underline', cursor: 'pointer', padding: 0, font: 'inherit' };
3
- /**
4
- * Headless empty state component with default rendering logic.
5
- * Framework-specific wrappers provide styling.
6
- *
7
- * Default behavior:
8
- * - Shows "No results found" title
9
- * - If hasActiveFilters=true: shows "clear all filters" link
10
- * - If message provided: shows custom message
11
- * - If render provided: uses custom renderer
12
- */
13
- export function EmptyState(props) {
14
- const { message, hasActiveFilters, onClearAll, render } = props;
15
- if (render) {
16
- return _jsx(_Fragment, { children: render() });
17
- }
18
- return (_jsx(_Fragment, { children: message != null ? (message) : hasActiveFilters ? (_jsxs(_Fragment, { children: ["No items match your current filters. Try adjusting your search or", ' ', _jsx("button", { type: "button", onClick: onClearAll, style: clearButtonStyle, children: "clear all filters" }), ' ', "to see all items."] })) : ('There are no items available at this time.') }));
19
- }
@@ -1,35 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from '../utils/gridContextMenuHelpers';
4
- const menuPositionStyle = (x, y) => ({ left: x, top: y });
5
- export function GridContextMenu(props) {
6
- const { x, y, hasSelection, canUndo, canRedo, onClose, onCopy, onCut, onPaste, onSelectAll, onUndo, onRedo, classNames } = props;
7
- const ref = React.useRef(null);
8
- const handlers = React.useMemo(() => getContextMenuHandlers({ onCopy, onCut, onPaste, onSelectAll, onUndo, onRedo, onClose }), [onCopy, onCut, onPaste, onSelectAll, onUndo, onRedo, onClose]);
9
- const isDisabled = React.useCallback((item) => {
10
- if (item.disabledWhenNoSelection && !hasSelection)
11
- return true;
12
- if (item.id === 'undo' && !canUndo)
13
- return true;
14
- if (item.id === 'redo' && !canRedo)
15
- return true;
16
- return false;
17
- }, [hasSelection, canUndo, canRedo]);
18
- React.useEffect(() => {
19
- const handleClickOutside = (e) => {
20
- if (ref.current && !ref.current.contains(e.target))
21
- onClose();
22
- };
23
- const handleKeyDown = (e) => {
24
- if (e.key === 'Escape')
25
- onClose();
26
- };
27
- document.addEventListener('mousedown', handleClickOutside, true);
28
- document.addEventListener('keydown', handleKeyDown, true);
29
- return () => {
30
- document.removeEventListener('mousedown', handleClickOutside, true);
31
- document.removeEventListener('keydown', handleKeyDown, true);
32
- };
33
- }, [onClose]);
34
- return (_jsx("div", { ref: ref, className: classNames?.contextMenu, role: "menu", style: menuPositionStyle(x, y), "aria-label": "Grid context menu", children: GRID_CONTEXT_MENU_ITEMS.map((item) => (_jsxs(React.Fragment, { children: [item.dividerBefore && _jsx("div", { className: classNames?.contextMenuDivider }), _jsxs("button", { type: "button", className: classNames?.contextMenuItem, onClick: handlers[item.id], disabled: isDisabled(item), children: [_jsx("span", { className: classNames?.contextMenuItemLabel, children: item.label }), item.shortcut && (_jsx("span", { className: classNames?.contextMenuItemShortcut, children: formatShortcut(item.shortcut) }))] })] }, item.id))) }));
35
- }