@opencorestack/opengridx 3.6.0 → 3.7.1

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/CHANGELOG.md +39 -0
  2. package/dist/ai.cjs +3 -3
  3. package/dist/ai.cjs.map +1 -1
  4. package/dist/ai.es.js +3 -3
  5. package/dist/ai.es.js.map +1 -1
  6. package/dist/index.d.ts +30 -0
  7. package/dist/opengridx.css +1 -1
  8. package/dist/opengridx.es.js +8756 -8244
  9. package/dist/opengridx.es.js.map +1 -1
  10. package/dist/opengridx.umd.js +15 -15
  11. package/dist/opengridx.umd.js.map +1 -1
  12. package/docs/API_REFERENCE.md +3 -2
  13. package/docs/architecture/datagrid-orchestration.md +46 -29
  14. package/docs/architecture/grid-row-meta.md +1 -1
  15. package/docs/customization/slots-api.md +1 -1
  16. package/docs/customization/theming.md +43 -0
  17. package/docs/features/virtualization.md +16 -2
  18. package/docs/performance.md +3 -1
  19. package/lib/components/AiAssistant/GridAiAssistantPanel.css +3 -1
  20. package/lib/components/Cell/Cell.css +4 -2
  21. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +2 -2
  22. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +3 -1
  23. package/lib/components/DataGrid/DataGrid.css +9 -12
  24. package/lib/components/DataGrid/DataGrid.tsx +163 -671
  25. package/lib/components/DataGrid/GridRoot.tsx +239 -0
  26. package/lib/components/DataGrid/GridViewport.tsx +314 -0
  27. package/lib/components/FilterPanel/FilterPanel.css +9 -8
  28. package/lib/components/Header/ColumnMenu.css +4 -3
  29. package/lib/components/Header/Header.css +5 -4
  30. package/lib/components/HeaderFilter/HeaderFilter.css +1 -1
  31. package/lib/components/ListView/ListView.css +4 -4
  32. package/lib/components/Pagination/Pagination.css +1 -1
  33. package/lib/components/Row/Row.css +2 -2
  34. package/lib/components/Toolbar/GridToolbar.css +8 -7
  35. package/lib/components/Toolbar/PivotPanel.css +3 -3
  36. package/lib/hooks/core/useGridDevWarnings.ts +3 -1
  37. package/lib/hooks/core/useGridEditPipeline.ts +55 -0
  38. package/lib/hooks/core/useGridRangeEditing.ts +129 -0
  39. package/lib/hooks/core/useGridRenderWindow.ts +66 -0
  40. package/lib/hooks/core/useGridScrollViewport.ts +42 -0
  41. package/lib/hooks/core/useGridSelectionAndStateApi.ts +61 -0
  42. package/lib/hooks/core/useGridToolbarStack.ts +49 -0
  43. package/lib/hooks/core/useGridViewApis.ts +46 -0
  44. package/lib/hooks/core/useLayout.ts +34 -24
  45. package/lib/styles/base/reset.css +5 -3
  46. package/lib/styles/base/variables.css +12 -0
  47. package/lib/theme/DataGridThemeProvider.tsx +15 -0
  48. package/lib/theme/__baseline__/default-theme.darwin.chromium.json +1503 -0
  49. package/lib/theme/__baseline__/default-theme.darwin.firefox.json +1503 -0
  50. package/lib/theme/__baseline__/default-theme.darwin.webkit.json +1503 -0
  51. package/lib/theme/types.ts +31 -0
  52. package/lib/types/ai.ts +109 -0
  53. package/lib/types/api.ts +169 -0
  54. package/lib/types/columns.ts +341 -0
  55. package/lib/types/dataSource.ts +40 -0
  56. package/lib/types/editing.ts +77 -0
  57. package/lib/types/export.ts +164 -0
  58. package/lib/types/filtering.ts +52 -0
  59. package/lib/types/grouping.ts +27 -0
  60. package/lib/types/index.ts +18 -1702
  61. package/lib/types/locale.ts +51 -0
  62. package/lib/types/props.ts +405 -0
  63. package/lib/types/rows.ts +132 -0
  64. package/lib/types/selection.ts +66 -0
  65. package/lib/types/slots.ts +56 -0
  66. package/lib/types/sorting.ts +25 -0
  67. package/lib/types/state.ts +35 -0
  68. package/lib/utils/export/exportShared.ts +22 -4
  69. package/lib/utils/export/index.ts +8 -4
  70. package/lib/utils/filtering/index.ts +8 -2
  71. package/lib/utils/parsing.ts +24 -1
  72. package/lib/utils/values.ts +31 -0
  73. package/llms.txt +1 -1
  74. package/package.json +4 -2
@@ -648,8 +648,9 @@ Context provider for overriding the grid's visual system.
648
648
  - **`colors`**: `primary`, `primaryDark`, `primaryLight`, `primaryFocus`, `secondary*`, `success`, `warning`, `error`, `info`, `white`, `black`, `gray` (`{ 50 … 900 }`). Toolbar, menu, selection and focus accents derive from `primary*` unless set directly.
649
649
  - **`typography`**: `fontFamily`, `fontFamilyMono`, `fontSizeXs` … `fontSizeXl`.
650
650
  - **`spacing`**: `xs` … `xxl`. **`borders`**: `widthThin/Medium/Thick`, `radiusSm` … `radiusXl`, `color`, `colorHover`. **`shadows`**: `sm` … `xl`.
651
- - **`grid`**: surfaces (`background`, `borderColor`, `headerBackground`, `headerText`, `headerHoverBackground`, `headerSortedBackground`, `rowText`, `rowHoverBackground`, `rowAlternateBackground`, `rowSelectedBackground`, `rowSelectedHoverBackground`, `cellFocusBorder`, `rangeBackground`, `rangeBorder` (v3.3), `pinnedLeftShadow`, `pinnedRightShadow`, `checkboxBg`, `checkboxBorder`), sizing (`rowHeightCompact`, `rowHeightStandard`, `rowHeightComfortable`, `headerHeight` in px, read by the grid's layout; the `rowHeight` / `headerHeight` props win), `cellPaddingX`, `cellPaddingY`, `cellFontSize`, `headerFontSize`.
652
- - **`toolbar`**, **`overlays`**, **`scrollbar`** (`thumbColor`, `trackColor`, `size`), **`skeleton`** (`baseColor`, `highlightColor`), **`transitions`**.
651
+ - **`grid`**: surfaces (`background`, `borderColor`, `headerBackground`, `headerText`, `headerHoverBackground`, `headerSortedBackground`, `rowText`, `rowHoverBackground`, `rowAlternateBackground`, `rowSelectedBackground`, `rowSelectedHoverBackground`, `cellFocusBorder`, `rangeBackground`, `rangeBorder` (v3.3), `pinnedLeftShadow`, `pinnedRightShadow`, `checkboxBg`, `checkboxBorder`), sizing (`rowHeightCompact`, `rowHeightStandard`, `rowHeightComfortable`, `headerHeight` in px, read by the grid's layout; the `rowHeight` / `headerHeight` props win), `cellPaddingX`, `cellPaddingY`, `cellFontSize`, `headerFontSize`; v3.7: fonts (`headerFontFamily`, `headerFontWeight`, `cellFontFamily`, `cellFontWeight`), `headerPaddingX`, outer `borderWidth` and `borderRadius`, dividers (`columnDividerWidth`, `headerColumnDividerWidth`, `rowDividerWidth`, `columnDividerColor`, `rowDividerColor`; `0` hides a divider).
652
+ - **`overlays`**: colours (`background`, `text`, `border`, `shadow`, `item*`) and, v3.7, `fontFamily`, `fontWeight`, `fontSize` for menus and panels.
653
+ - **`toolbar`**, **`scrollbar`** (`thumbColor`, `trackColor`, `size`), **`skeleton`** (`baseColor`, `highlightColor`), **`transitions`**.
653
654
 
654
655
  Presets: `darkTheme`, `roseTheme`, `emeraldTheme`, `amberTheme`, `compactTheme`. See [Theming](customization/theming.md).
655
656
 
@@ -1,8 +1,23 @@
1
1
  # DataGrid orchestration
2
2
 
3
3
  `lib/components/DataGrid/DataGrid.tsx` is the orchestration layer: it destructures the props, calls
4
- hooks and pure utils in a fixed order, and renders JSX. Feature logic lives in the hooks and utils
5
- listed here; add new logic there, not in the component.
4
+ hooks and pure utils in a fixed order, and renders `GridRoot` with one props object. Feature logic lives
5
+ in the hooks and utils listed here; add new logic there, not in the component.
6
+
7
+ Some contiguous runs of hooks are wrapped in a **composite hook** (`hooks/core/`) that calls its members in
8
+ the same order, so `DataGrid.tsx` calls one hook where the table below lists several (marked with the
9
+ composite's name). A composite only ever wraps consecutive steps: a hook that has to run between two of its
10
+ members goes inside it at that position, never before or after the composite.
11
+
12
+ | Composite | Steps |
13
+ | :--- | :--- |
14
+ | `useGridEditPipeline` | 12a–13 |
15
+ | `useGridSelectionAndStateApi` | 23–24 |
16
+ | `useGridViewApis` | 30–31a |
17
+ | `useGridScrollViewport` | 32–33 |
18
+ | `useGridRangeEditing` | 36a–36d |
19
+ | `useGridRenderWindow` | 38–41 |
20
+ | `useGridToolbarStack` | 44–46 |
6
21
 
7
22
  ## Why the order matters
8
23
 
@@ -47,9 +62,9 @@ in an effect, so **keep the call order when you move or add a hook**. In particu
47
62
  | 10 | `useGridRowIdOf` | `hooks/core/useGridRowIdOf.ts` | `getRowIdOf` and its stable event-handler twin |
48
63
  | 11 | `useGridApiRefBinding` | `hooks/core/useGridApiRefBinding.ts` | Binds the `apiRef` prop (layout effect) |
49
64
  | 12 | `useGridHierarchy` | `hooks/core/useGridHierarchy.ts` | `useTreeData` + `useRowGrouping`, the active handlers and `rowMetaMap` |
50
- | 12a | `useGridEditCommitChannel` | `hooks/core/useGridUndoRedo.ts` | Stable `emit` / `setListener` pair: the commit paths below report stored changes through `emit`, and `useGridUndoRedo` (36c) registers the listener (v3.4) |
51
- | 12b | `useGridBatchEdit` | `hooks/features/useGridBatchEdit.ts` | Batch edits (v3.4): `applyEdits` runs `valueSetter` → `processRowUpdate` once per row (rows in parallel, failures isolated and sent to `onProcessRowUpdateError`) and stores every successful row in one `replaceRows` update; `getRow` sees rows stored since the last render; `storeRow` stores a single edit |
52
- | 13 | `useGridEditing` | `hooks/features/useGridEditing.ts` | Cell editing state and handlers; stores through `useGridBatchEdit.storeRow` and reports the committed change to the channel |
65
+ | 12a | `useGridEditCommitChannel` (in `useGridEditPipeline`) | `hooks/core/useGridUndoRedo.ts` | Stable `emit` / `setListener` pair: the commit paths below report stored changes through `emit`, and `useGridUndoRedo` (36c) registers the listener (v3.4) |
66
+ | 12b | `useGridBatchEdit` (in `useGridEditPipeline`) | `hooks/features/useGridBatchEdit.ts` | Batch edits (v3.4): `applyEdits` runs `valueSetter` → `processRowUpdate` once per row (rows in parallel, failures isolated and sent to `onProcessRowUpdateError`) and stores every successful row in one `replaceRows` update; `getRow` sees rows stored since the last render; `storeRow` stores a single edit |
67
+ | 13 | `useGridEditing` (in `useGridEditPipeline`) | `hooks/features/useGridEditing.ts` | Cell editing state and handlers; stores through `useGridBatchEdit.storeRow` and reports the committed change to the channel |
53
68
  | 14 | `useGridLiveRowSelection` | `hooks/core/useGridLiveRowSelection.ts` | Selection without removed rows |
54
69
  | 15 | `useGridDataSource` | `hooks/features/useGridDataSource.ts` | Server fetching |
55
70
  | 16 | `useServerTreeChildren` | `hooks/features/useServerTreeChildren.ts` | Lazy server tree children |
@@ -59,46 +74,48 @@ in an effect, so **keep the call order when you move or add a hook**. In particu
59
74
  | 20 | `useGridRowPipeline` | `hooks/core/useGridRowPipeline.ts` | Filter → pin → sort → paginate |
60
75
  | 21 | `useGridPageCorrection` | `hooks/core/useGridPageCorrection.ts` | Moves a page left past the end back to the last page |
61
76
  | 22 | `getPaginationRowCount` (pure) | `utils/pagination/index.ts` | What the pager pages through |
62
- | 23 | `useGridRowInteractions` | `hooks/core/useGridRowInteractions.ts` | Row selection, `handleRowClick`, `handleSelectionChange` |
63
- | 24 | `useGridApiMethods` | `hooks/core/useGridApiMethods.ts` | State API methods (`getAllFilteredRows` via `utils/gridApiRows.ts`); since v3.5 also `setSortModel`, `setRowGroupingModel`, `setAggregationModel`, `setColumnVisibilityModel`, `setPivotModel` and `getGridAiState` |
77
+ | 23 | `useGridRowInteractions` (in `useGridSelectionAndStateApi`) | `hooks/core/useGridRowInteractions.ts` | Row selection, `handleRowClick`, `handleSelectionChange` |
78
+ | 24 | `useGridApiMethods` (in `useGridSelectionAndStateApi`) | `hooks/core/useGridApiMethods.ts` | State API methods (`getAllFilteredRows` via `utils/gridApiRows.ts`); since v3.5 also `setSortModel`, `setRowGroupingModel`, `setAggregationModel`, `setColumnVisibilityModel`, `setPivotModel` and `getGridAiState` |
64
79
  | 25 | `useRowReorder` | `hooks/useRowReorder.ts` | Row drag-reorder |
65
80
  | 26 | `useAggregation` | `hooks/features/useAggregation.ts` | Footer aggregation result |
66
81
  | 27 | `useGridAggregationApi` | `hooks/core/useGridAggregationApi.ts` | `getAggregationResult`, `getAggregationModel`, `getGroupedExportRows` |
67
82
  | 28 | `useLayout` | `hooks/core/useLayout.ts` | Row heights, pinned column split, widths |
68
83
  | 29 | `useGridSpanning` | `hooks/features/useGridSpanning.ts` | Row/column spans |
69
84
  | 29a | `useGridCellSelection` | `hooks/core/useGridCellSelection.ts` | Cell range (v3.3): the model's range resolved against `allRenderableRows` and the data columns of `navigationColumns` (grown over spans), the render rectangle, pointer drag with auto-scroll and Shift+click, the Shift-key handlers for the keyboard hook, copy text, status-bar totals and the debounced live-region text. Inert while `cellSelection` is off |
70
- | 30 | `useGridClipboardApi` | `hooks/core/useGridClipboardApi.ts` | Ctrl/Cmd+C and `copySelectedRows`; with `cellSelection` the shortcut copies the range (`getCellRangeCopyText`) |
71
- | 31 | `useGridScrollToIndexesApi` | `hooks/core/useGridScrollToIndexesApi.ts` | `scrollToIndexes` |
72
- | 31a | `useGridColumnAutosize` | `hooks/core/useGridColumnAutosize.ts` | `autosizeColumn` / `autosizeColumns` on the API, and the resize handle's auto-size handler (v3.1) |
73
- | 32 | `useGridViewport` | `hooks/core/useGridViewport.ts` | Scroll sync, viewport measurement, viewport callback ref |
74
- | 32a | `useGridMaxContentHeight` | `hooks/core/useGridMaxContentHeight.ts` | The tallest element this browser lays out (`utils/maxElementHeight.ts`, measured once per document after mount; `undefined` until then, `Infinity` without a layout engine) |
75
- | 33 | `useGridVirtualization` | `hooks/core/useGridVirtualization.ts` | Render window; `virtualContainerHeight` caps the rows' container to `maxContentHeight` (Firefox collapses taller elements) |
85
+ | 30 | `useGridClipboardApi` (in `useGridViewApis`) | `hooks/core/useGridClipboardApi.ts` | Ctrl/Cmd+C and `copySelectedRows`; with `cellSelection` the shortcut copies the range (`getCellRangeCopyText`) |
86
+ | 31 | `useGridScrollToIndexesApi` (in `useGridViewApis`) | `hooks/core/useGridScrollToIndexesApi.ts` | `scrollToIndexes` |
87
+ | 31a | `useGridColumnAutosize` (in `useGridViewApis`) | `hooks/core/useGridColumnAutosize.ts` | `autosizeColumn` / `autosizeColumns` on the API, and the resize handle's auto-size handler (v3.1) |
88
+ | 32 | `useGridViewport` (in `useGridScrollViewport`) | `hooks/core/useGridViewport.ts` | Scroll sync, viewport measurement, viewport callback ref |
89
+ | 32a | `useGridMaxContentHeight` (in `useGridScrollViewport`) | `hooks/core/useGridMaxContentHeight.ts` | The tallest element this browser lays out (`utils/maxElementHeight.ts`, measured once per document after mount; `undefined` until then, `Infinity` without a layout engine) |
90
+ | 33 | `useGridVirtualization` (in `useGridScrollViewport`) | `hooks/core/useGridVirtualization.ts` | Render window; `virtualContainerHeight` caps the rows' container to `maxContentHeight` (Firefox collapses taller elements) |
76
91
  | 34 | `useGridSortHandlers` | `hooks/core/useGridHeaderHandlers.ts` | Header sort / shift-sort |
77
92
  | 35 | `useGridKeyboardNavigation` | `hooks/core/useGridKeyboardNavigation.ts` | Focus and keyboard handling; takes `cellSelection.keyboard` (Shift+navigation, Escape, Ctrl/Cmd+A) |
78
93
  | 36 | `useGridPointerFocusHandlers` | `hooks/core/useGridFocusHandlers.ts` | Cell and header click focus |
79
- | 36a | `useGridCellSelectionApi` | `hooks/core/useGridCellSelectionApi.ts` | Keeps the range anchor and the focused cell together (layout effect: a new anchor moves focus; focus moved to another data cell collapses the range), clears a range whose corner is gone (`'dataChange'`), and installs `getCellSelectionModel`, `setCellSelectionModel`, `selectCellRange`, `clearCellSelection`, `getSelectedCells`, `copySelectedCells` (once per `apiRef`, reading the latest values from a ref) |
80
- | 36b | `useGridClipboardPaste` | `hooks/core/useGridClipboardPaste.ts` | Paste and range clear (v3.4): `paste` and Delete/Backspace listeners on the grid root (only for cell targets, not editors or nested grids), TSV parsing (`utils/parsing.ts`), placement against the renderable rows and range columns (`utils/editing/targets.ts`), the batch edit, the new selection, `onBeforeClipboardPaste` / `onClipboardPaste`, and `apiRef.pasteText`. Inert without `cellSelection` except for `pasteText` |
81
- | 36c | `useGridUndoRedo` | `hooks/core/useGridUndoRedo.ts` | History (v3.4, `undoRedo`): records the channel's changes, replays them as batch edits with an `expect` value per cell (stale cells skipped), queues undo/redo, selects and scrolls to the affected cells, Ctrl/Cmd+Z / Shift+Z / Ctrl+Y on the grid root, `onHistoryChange`, and `apiRef.undo` / `redo` / `canUndo` / `canRedo` / `clearHistory` |
82
- | 36d | `useGridFillHandle` | `hooks/core/useGridFillHandle.ts` | Fill handle (v3.5, `cellSelection` without `disableFillHandle`): where the handle is drawn (`rowRenderProps.fillHandle`, the range's bottom-right cell or its span origin), a `pointerdown` listener on the viewport for `.ogx__cell-fill-handle` that runs `cellSelection.startTrackedDrag` (the range drag's hit testing, auto-scroll and slow-machine fallback, on pointer events so touch works), the fill itself (`utils/editing/fill.ts`: series detection, lines, `GridColDef.fillValue`) as one batch edit with source `'fill'`, the new selection, `onFill`, and Ctrl/Cmd+D / Ctrl/Cmd+R on the grid root |
94
+ | 36a | `useGridCellSelectionApi` (in `useGridRangeEditing`) | `hooks/core/useGridCellSelectionApi.ts` | Keeps the range anchor and the focused cell together (layout effect: a new anchor moves focus; focus moved to another data cell collapses the range), clears a range whose corner is gone (`'dataChange'`), and installs `getCellSelectionModel`, `setCellSelectionModel`, `selectCellRange`, `clearCellSelection`, `getSelectedCells`, `copySelectedCells` (once per `apiRef`, reading the latest values from a ref) |
95
+ | 36b | `useGridClipboardPaste` (in `useGridRangeEditing`) | `hooks/core/useGridClipboardPaste.ts` | Paste and range clear (v3.4): `paste` and Delete/Backspace listeners on the grid root (only for cell targets, not editors or nested grids), TSV parsing (`utils/parsing.ts`), placement against the renderable rows and range columns (`utils/editing/targets.ts`), the batch edit, the new selection, `onBeforeClipboardPaste` / `onClipboardPaste`, and `apiRef.pasteText`. Inert without `cellSelection` except for `pasteText` |
96
+ | 36c | `useGridUndoRedo` (in `useGridRangeEditing`) | `hooks/core/useGridUndoRedo.ts` | History (v3.4, `undoRedo`): records the channel's changes, replays them as batch edits with an `expect` value per cell (stale cells skipped), queues undo/redo, selects and scrolls to the affected cells, Ctrl/Cmd+Z / Shift+Z / Ctrl+Y on the grid root, `onHistoryChange`, and `apiRef.undo` / `redo` / `canUndo` / `canRedo` / `clearHistory` |
97
+ | 36d | `useGridFillHandle` (in `useGridRangeEditing`) | `hooks/core/useGridFillHandle.ts` | Fill handle (v3.5, `cellSelection` without `disableFillHandle`): where the handle is drawn (`rowRenderProps.fillHandle`, the range's bottom-right cell or its span origin), a `pointerdown` listener on the viewport for `.ogx__cell-fill-handle` that runs `cellSelection.startTrackedDrag` (the range drag's hit testing, auto-scroll and slow-machine fallback, on pointer events so touch works), the fill itself (`utils/editing/fill.ts`: series detection, lines, `GridColDef.fillValue`) as one batch edit with source `'fill'`, the new selection, `onFill`, and Ctrl/Cmd+D / Ctrl/Cmd+R on the grid root |
83
98
  | 37 | `useGridColumnMenuHandlers` | `hooks/core/useGridHeaderHandlers.ts` | Column menu Hide / Pin (`pinColumnTo` in `utils/pinning`) |
84
- | 38 | `useGridAriaRows` | `hooks/core/useGridAriaRows.ts` | `aria-rowcount` and `aria-rowindex` bases |
85
- | 39 | `useGridSpanRowWindow` | `hooks/features/useGridSpanRenderWindow.ts` | Render window widened to whole row spans |
86
- | 40 | `useGridVisibleRows` | `hooks/core/useGridVisibleRows.ts` | Pinned + virtual centre rows to render |
87
- | 41 | `useGridSpanColumnWindow` | `hooks/features/useGridSpanRenderWindow.ts` | Columns to render, widened to whole column spans |
99
+ | 38 | `useGridAriaRows` (in `useGridRenderWindow`) | `hooks/core/useGridAriaRows.ts` | `aria-rowcount` and `aria-rowindex` bases |
100
+ | 39 | `useGridSpanRowWindow` (in `useGridRenderWindow`) | `hooks/features/useGridSpanRenderWindow.ts` | Render window widened to whole row spans |
101
+ | 40 | `useGridVisibleRows` (in `useGridRenderWindow`) | `hooks/core/useGridVisibleRows.ts` | Pinned + virtual centre rows to render |
102
+ | 41 | `useGridSpanColumnWindow` (in `useGridRenderWindow`) | `hooks/features/useGridSpanRenderWindow.ts` | Columns to render, widened to whole column spans |
88
103
  | 42 | `useGridDevWarnings` | `hooks/core/useGridDevWarnings.ts` | Dev-only warnings |
89
104
  | 43 | `useColumnGroupReorderGuard` | `hooks/features/useColumnGroupReorderGuard.ts` | Header drag-reorder kept inside column groups |
90
- | 44 | `useGridColumnsPanel` | `hooks/core/useGridColumnsPanel.ts` | Columns panel (toolbar or standalone) |
91
- | 45 | `useGridStylesheetWarning` | `hooks/core/useGridDevWarnings.ts` | Warns when the stylesheet is missing |
92
- | 45a | `useGridAiAssistant` | `hooks/features/useGridAiAssistant.ts` | The `aiAssistant` panel (v3.5): open state, the request and its `AbortController` (Stop), applying a branded reply through the `apiRef` setters of step 24 (`utils/aiAssistant.ts`: allowed parts, the Undo snapshot, chips, chip removal), the live-region status, and `apiRef.openAiAssistant` / `closeAiAssistant`. Inert without `aiAssistant`; it runs before step 46 because the toolbar gets its Ask AI button from it |
93
- | 46 | `useGridToolbarProps` | `hooks/core/useGridToolbarProps.ts` | Props for `slots.toolbar` |
105
+ | 44 | `useGridColumnsPanel` (in `useGridToolbarStack`) | `hooks/core/useGridColumnsPanel.ts` | Columns panel (toolbar or standalone) |
106
+ | 45 | `useGridStylesheetWarning` (in `useGridToolbarStack`) | `hooks/core/useGridDevWarnings.ts` | Warns when the stylesheet is missing |
107
+ | 45a | `useGridAiAssistant` (in `useGridToolbarStack`) | `hooks/features/useGridAiAssistant.ts` | The `aiAssistant` panel (v3.5): open state, the request and its `AbortController` (Stop), applying a branded reply through the `apiRef` setters of step 24 (`utils/aiAssistant.ts`: allowed parts, the Undo snapshot, chips, chip removal), the live-region status, and `apiRef.openAiAssistant` / `closeAiAssistant`. Inert without `aiAssistant`; it runs before step 46 because the toolbar gets its Ask AI button from it |
108
+ | 46 | `useGridToolbarProps` (in `useGridToolbarStack`) | `hooks/core/useGridToolbarProps.ts` | Props for `slots.toolbar` |
94
109
 
95
110
  ## Rendered components
96
111
 
97
- `GridToolbarSlot`, `GridAiAssistantArea` (the `aiAssistant` panel or `slots.aiAssistantPanel` while open, anchored under the toolbar), `GridStandaloneColumnPanel`, `GridListView` (list view) or the viewport
98
- (`Header`, `GridPinnedRows` top, `GridEmptyState`, `GridVirtualRows`, `GridPinnedRows` bottom,
112
+ `DataGrid.tsx` renders `GridRoot` (`components/DataGrid/GridRoot.tsx`): the root element with, in this order,
113
+ `GridToolbarSlot`, `GridAiAssistantArea` (the `aiAssistant` panel or `slots.aiAssistantPanel` while open, anchored under the toolbar), `GridStandaloneColumnPanel`, `GridListView` (list view) or `GridViewport` (`components/DataGrid/GridViewport.tsx`, the `role="grid"` viewport:
114
+ `Header`, `GridPinnedRows` top, `GridEmptyState`, `GridVirtualRows`, `GridPinnedRows` bottom,
99
115
  `GridAggregationFooter`), `GridCellSelectionStatsArea` (`showCellSelectionStats` with `cellSelection`), then `slots.footer` or `GridPaginationArea`, `GridLiveRegion`,
100
- `GridLoadingOverlay` and `GridErrorOverlay`. The props all three row renderers take alike are built
101
- once in `DataGrid.tsx` (`rowRenderProps`) and spread into each. `rowRenderProps.cellRange` is the cell range's
116
+ `GridLoadingOverlay` and `GridErrorOverlay`. `GridRoot` builds the overlay slot elements and passes the
117
+ rest of its props on to `GridViewport`. The props all three row renderers take alike are built once in
118
+ `GridViewport` (`rowRenderProps`) and spread into each. `rowRenderProps.cellRange` is the cell range's
102
119
  render rectangle; each row renderer turns it into a per-row `cellRange` (`getRowCellRange`), `null` for rows
103
120
  outside it, and `Row` is memoised with a value comparison of that prop, so a drag only re-renders the rows the
104
121
  range enters or leaves. `rowRenderProps.fillHandle` (v3.5) adds `fillHandleCol` to the one row that draws the fill
@@ -2,7 +2,7 @@
2
2
 
3
3
  Hierarchy metadata for a grid row, available via `params.rowMeta` in `renderCell` since v1.1.
4
4
 
5
- **File:** `lib/types/index.ts`
5
+ **File:** `lib/types/rows.ts` (re-exported from `lib/types/index.ts`)
6
6
 
7
7
  ---
8
8
 
@@ -288,4 +288,4 @@ function CustomPagination(props: Record<string, unknown>) {
288
288
  ## See Also
289
289
 
290
290
  - [Custom Pagination Guide](../features/custom-pagination.md) - Detailed examples with Ant Design and MUI
291
- - [DataGrid Props](../../lib/types/index.ts) - Full TypeScript definitions
291
+ - [DataGrid Props](../../lib/types/props.ts) - Full TypeScript definitions
@@ -5,6 +5,8 @@ OpenGridX ships with a complete design-token system built on CSS custom properti
5
5
  1. **`DataGridThemeProvider`** — the recommended React API (maps a typed `GridTheme` object → CSS variables)
6
6
  2. **Raw CSS override** — set any `--ogx-*` variable yourself on a wrapper element or `:root`
7
7
 
8
+ To design a theme without writing code, use the **[Theme Builder](https://opencorestack.github.io/OpenGridX/theme-builder)** demo page: controls for colours, fonts, sizes, borders and dividers, buttons and menus, edited separately for light and dark mode on a live grid, with WCAG AA contrast hints. It copies or downloads the result as a `GridTheme` (only the values that differ from the defaults) and can share it as a link (`#theme=…`).
9
+
8
10
  ---
9
11
 
10
12
  ## Quick Start — `DataGridThemeProvider`
@@ -198,6 +200,47 @@ All sub-theme types (`GridThemeColors`, `GridThemeGrayScale`, `GridThemeTypograp
198
200
  | `--ogx-grid-header-font-size` | `13px` | Header cell font size (`grid.headerFontSize`) |
199
201
  | `--ogx-grid-line-height` | `var(--ogx-line-height-normal)` | Row line height |
200
202
 
203
+ ### Fonts, dividers and corners (v3.7)
204
+
205
+ These keys were added for the [Theme Builder](https://opencorestack.github.io/OpenGridX/theme-builder). Each defaults to exactly what the grid drew before, so a theme that leaves them out looks unchanged. The variables without a stylesheet value are read with a fallback, so they follow the token in the Default column when only that one is set.
206
+
207
+ | Theme key | CSS variable | Default | Styles |
208
+ |---|---|---|---|
209
+ | `grid.headerFontFamily` | `--ogx-grid-header-font-family` | the grid font | Header cells |
210
+ | `grid.headerFontWeight` | `--ogx-grid-header-font-weight` | `600` | Header cells |
211
+ | `grid.cellFontFamily` | `--ogx-grid-cell-font-family` | the grid font | Body cells |
212
+ | `grid.cellFontWeight` | `--ogx-grid-cell-font-weight` | inherited (`400`) | Body cells |
213
+ | `grid.headerPaddingX` | `--ogx-grid-header-padding-x` | `--ogx-grid-cell-padding-x` | Horizontal padding of header cells |
214
+ | `grid.borderWidth` | `--ogx-grid-border-width` | `1px` | The grid's outer border |
215
+ | `grid.borderRadius` | `--ogx-grid-border-radius` | `--ogx-border-radius-lg` (`12px`) | The grid's corners (inner corners follow: radius minus border width) |
216
+ | `grid.columnDividerWidth` | `--ogx-grid-column-divider-width` | `1px` | Lines between body cells and aggregation footer cells, and the edge of the right-pinned section |
217
+ | `grid.headerColumnDividerWidth` | `--ogx-grid-header-column-divider-width` | `1px` | The short separators between header cells and column-group cells |
218
+ | `grid.rowDividerWidth` | `--ogx-grid-row-divider-width` | `1px` | Lines under rows, the header row, column-group rows and the header filter row |
219
+ | `grid.columnDividerColor` | `--ogx-grid-column-divider-color` | `--ogx-grid-border-color` | Colour of the column dividers |
220
+ | `grid.rowDividerColor` | `--ogx-grid-row-divider-color` | `--ogx-grid-border-color` | Colour of the row dividers |
221
+ | `overlays.fontFamily` | `--ogx-overlay-font-family` | the grid font | Column menu, header filter menu, toolbar panels (columns, filters, summaries, pivot), AI panel |
222
+ | `overlays.fontWeight` | `--ogx-overlay-font-weight` | each item's own (`400`, labels `500`) | Menu items and panel labels; section titles and active items stay bold |
223
+ | `overlays.fontSize` | `--ogx-overlay-font-size` | `--ogx-font-size-xs` (AI panel `--ogx-font-size-sm`) | Menu items and panel labels |
224
+
225
+ The header-cell separators keep their own colour (`--ogx-color-gray-300` at 50% opacity); only their width is themable.
226
+
227
+ #### Recipe: hide the dividers
228
+
229
+ A width of `0` removes a divider without changing any cell's size or position:
230
+
231
+ ```tsx
232
+ const borderless: GridTheme = {
233
+ grid: {
234
+ columnDividerWidth: '0', // body and footer cells
235
+ headerColumnDividerWidth: '0', // header and column-group cells
236
+ rowDividerWidth: '0', // under rows and header rows
237
+ rowAlternateBackground: '#f8fafc', // optional: zebra rows instead of lines
238
+ },
239
+ };
240
+ ```
241
+
242
+ For visible gaps between cells instead of lines, set the divider colour to the grid background (`columnDividerColor: '#ffffff'`) and widen it. Cells have no margin setting: they are positioned to the pixel for virtualization, pinning and spanning, so use padding for room inside them.
243
+
201
244
  ### Grid Colors
202
245
 
203
246
  | Token | Default (light) | Dark override | Description |
@@ -33,7 +33,7 @@ The grid calculates which rows are visible based on:
33
33
  - ✅ About 19 row elements at rest in an 800 px page at 52 px rows, at most about 86 while scrolling fast, whatever the row count (10k to 1M)
34
34
  - ✅ Scrolling keeps up with a 60 Hz display at 1M rows in Chromium (no missed frames) and Firefox; WebKit misses a frame or two on each long jump of the scroll position
35
35
  - ✅ The grid itself adds about 10 MB of JS heap at 100k rows and 79 MB at 1M (Chromium), on top of your row data
36
- - ⚠️ Not millions of rows at the default height. Browsers cap an element's height and the grid does no scroll scaling: it lays every row out at its real height. Chromium and WebKit stop at 33,554,428 px, so rows past about `33,554,428 / rowHeight` cannot be scrolled to (645,277 at the default 52 px; 1M rows fit at 32 px). Firefox's limit is about 17.9M px (about 344,000 rows at 52 px); since 3.5.1 the grid caps its content to the limit it measures, so Firefox scrolls normally up to there (on 3.5.0 and earlier, content past the limit collapsed and the grid did not scroll at all). Beyond that, use pagination, or server-side paging / infinite scroll. In development the grid warns when rows are cut off and names how many can be scrolled to. Measured reach per browser: [Performance → Reach](../performance.md#reach-the-browser-height-ceiling).
36
+ - ⚠️ Not millions of rows at the default height: browsers cap an element's height. See [The browser height limit](#the-browser-height-limit) for how many rows fit and what to use beyond that.
37
37
 
38
38
  ### Column Virtualization
39
39
 
@@ -89,6 +89,20 @@ In development builds the grid logs a `console.warn` when it is rendering every
89
89
 
90
90
  Pagination can hide this problem, because a paginated grid only has one page of rows to render. Row grouping switches pagination off (see below), so an unbounded container that looked fine with pagination can freeze the tab once grouping is enabled.
91
91
 
92
+ ### The browser height limit
93
+
94
+ **How many rows can one scrolling grid hold?**
95
+
96
+ | Rows (at the default 52 px) | Use |
97
+ | :--- | :--- |
98
+ | up to about 300,000 | client-side rows: they fit in every browser |
99
+ | up to about 600,000 | client-side rows in Chromium and WebKit only; Firefox users cannot scroll past about 344,000 |
100
+ | more | a [`dataSource`](data-source.md) (server-side pages or infinite scroll), or `pagination` |
101
+
102
+ `density="compact"` (32 px rows) stretches each limit by about 1.6×: about 559,000 rows in Firefox and 1,048,000 in Chromium and WebKit.
103
+
104
+ Why: the grid lays every row out at its real height (`rowCount × rowHeight` pixels) and does no scroll scaling, and browsers cap an element's height. Chromium and WebKit stop at 33,554,428 px, so rows past about `33,554,428 / rowHeight` cannot be scrolled to (645,277 at 52 px). Firefox's limit is about 17.9M px (about 344,000 rows at 52 px); since 3.5.1 the grid caps its content to the limit it measures, so Firefox scrolls normally up to there (on 3.5.0 and earlier, content past the limit collapsed and the grid did not scroll at all). In development the grid warns when rows are cut off, names how many can be scrolled to and points to `dataSource`. Measured reach per browser: [Performance → Reach](../performance.md#reach-the-browser-height-ceiling).
105
+
92
106
  ## Configuration
93
107
 
94
108
  ### Row Height
@@ -243,7 +257,7 @@ You should see console logs only for visible rows + overscan buffer.
243
257
  ## Known Limitations
244
258
 
245
259
  1. **Unbounded containers** - Virtualization needs a bounded viewport height (see above)
246
- 2. **Browser height cap** - Rows past roughly `33,554,432 / rowHeight` (Chromium; less in Firefox) cannot be scrolled to (see above)
260
+ 2. **Browser height cap** - Rows past roughly `33,554,428 / rowHeight` (Chromium and WebKit; less in Firefox) cannot be scrolled to ([see above](#the-browser-height-limit))
247
261
  3. **Uniform row heights** - Only detail panels vary the height; cumulative heights are recomputed over all rows when panels change
248
262
  4. **Horizontal scrolling** - May show brief flicker with very wide grids
249
263
 
@@ -87,12 +87,14 @@ import '@opencorestack/opengridx/styles';
87
87
 
88
88
  ## Know the browser's height limit
89
89
 
90
+ In short: up to about 300,000 rows at 52 px fit in every browser, up to about 600,000 in Chromium and WebKit; beyond that load rows with a [`dataSource`](features/data-source.md) or use pagination. The decision guide is in [Virtualization → The browser height limit](features/virtualization.md#the-browser-height-limit).
91
+
90
92
  The grid sizes its scroll content to `rowCount × rowHeight` pixels and does not scale scroll positions. Browsers cap an element's height, and the [benchmark](#reach-the-browser-height-ceiling) measures what happens past the cap:
91
93
 
92
94
  - **Chromium and WebKit** clamp the content at 33,554,428 px. Rows below that cannot be scrolled to: at the default 52 px row height that is every row past 645,277.
93
95
  - **Firefox** has a lower limit, about 17.9M px, and collapses a taller element instead of clamping it. Up to 3.5.0 that made a grid with more content unscrollable in Firefox (only the first screen of rows was reachable). **Since 3.5.1 the grid measures the browser's limit once and caps its scroll content to it**, so Firefox scrolls normally and reaches the rows above the cap: about 344,000 rows at 52 px, 559,000 at 32 px.
94
96
 
95
- In development the grid warns when rows are cut off, naming how many can be scrolled to in that browser. For more rows, use pagination, `paginationMode="server"` or infinite scroll with a `dataSource`, or a smaller `rowHeight` (`density="compact"`, 32 px, keeps 1M rows under the Chromium and WebKit cap).
97
+ In development the grid warns when rows are cut off, naming how many can be scrolled to in that browser and pointing to `dataSource`. For more rows, use a [`dataSource`](features/data-source.md) (`paginationMode="server"` or infinite scroll), `pagination`, or a smaller `rowHeight` (`density="compact"`, 32 px, keeps 1M rows under the Chromium and WebKit cap).
96
98
 
97
99
  ## Use server-side operations for very large datasets
98
100
 
@@ -21,7 +21,9 @@
21
21
  border: 1px solid var(--ogx-overlay-border, var(--ogx-grid-border-color));
22
22
  border-radius: var(--ogx-border-radius-lg, 8px);
23
23
  box-shadow: var(--ogx-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.15));
24
- font-size: var(--ogx-font-size-sm);
24
+ font-family: var(--ogx-overlay-font-family, inherit);
25
+ font-size: var(--ogx-overlay-font-size, var(--ogx-font-size-sm));
26
+ font-weight: var(--ogx-overlay-font-weight, inherit);
25
27
  }
26
28
  .ogx-ai-panel__header {
27
29
  display: flex;
@@ -3,8 +3,10 @@
3
3
  display: flex;
4
4
  align-items: center;
5
5
  padding: var(--ogx-grid-cell-padding-y) var(--ogx-grid-cell-padding-x);
6
- border-inline-end: 1px solid var(--ogx-grid-border-color);
6
+ border-inline-end: var(--ogx-grid-column-divider-width) solid var(--ogx-grid-column-divider-color, var(--ogx-grid-border-color));
7
+ font-family: var(--ogx-grid-cell-font-family, inherit);
7
8
  font-size: var(--ogx-grid-cell-font-size);
9
+ font-weight: var(--ogx-grid-cell-font-weight, inherit);
8
10
  color: var(--ogx-grid-row-text);
9
11
  line-height: var(--ogx-grid-line-height);
10
12
  white-space: nowrap;
@@ -132,7 +134,7 @@
132
134
  background-color: var(--ogx-grid-background);
133
135
  }
134
136
  .ogx__cell--pinned-right {
135
- border-inline-start: 1px solid var(--ogx-grid-border-color);
137
+ border-inline-start: var(--ogx-grid-column-divider-width) solid var(--ogx-grid-column-divider-color, var(--ogx-grid-border-color));
136
138
  }
137
139
  /* When the columns are narrower than the grid, the free space goes before the right-pinned section. */
138
140
  .ogx__cell--pinned-right-first {
@@ -8,7 +8,7 @@
8
8
  display: flex;
9
9
  align-items: stretch;
10
10
  background: var(--ogx-grid-header-background);
11
- border-bottom: 1px solid var(--ogx-grid-border-color);
11
+ border-bottom: var(--ogx-grid-row-divider-width) solid var(--ogx-grid-row-divider-color, var(--ogx-grid-border-color));
12
12
  /* No overflow clipping: it would make the row the scroll container of its sticky (pinned) cells. */
13
13
  flex-shrink: 0;
14
14
  min-height: var(--ogx-header-height, 48px);
@@ -31,7 +31,7 @@
31
31
  inset-inline-end: 0;
32
32
  top: 25%;
33
33
  height: 50%;
34
- width: 1px;
34
+ width: var(--ogx-grid-header-column-divider-width);
35
35
  background-color: var(--ogx-color-gray-300, #cbd5e1);
36
36
  opacity: 0.5;
37
37
  }
@@ -2,6 +2,7 @@
2
2
  background: var(--ogx-overlay-background, var(--ogx-color-white));
3
3
  border: var(--ogx-border-width-thin) solid var(--ogx-overlay-border, var(--ogx-border-color));
4
4
  border-radius: var(--ogx-border-radius-sm);
5
+ font-family: var(--ogx-overlay-font-family, inherit);
5
6
  min-width: 250px;
6
7
  max-width: 350px;
7
8
  box-shadow: var(--ogx-shadow-lg);
@@ -120,7 +121,8 @@
120
121
  }
121
122
 
122
123
  .ogx-column-visibility-panel__label {
123
- font-size: var(--ogx-font-size-xs);
124
+ font-size: var(--ogx-overlay-font-size, var(--ogx-font-size-xs));
125
+ font-weight: var(--ogx-overlay-font-weight, inherit);
124
126
  color: var(--ogx-overlay-text, var(--ogx-color-gray-900));
125
127
  user-select: none;
126
128
  cursor: inherit;
@@ -3,8 +3,8 @@
3
3
  flex-direction: column;
4
4
  width: 100%;
5
5
  background: var(--ogx-grid-background);
6
- border: 1px solid var(--ogx-grid-border-color);
7
- border-radius: var(--ogx-border-radius-lg);
6
+ border: var(--ogx-grid-border-width) solid var(--ogx-grid-border-color);
7
+ border-radius: var(--ogx-grid-border-radius, var(--ogx-border-radius-lg));
8
8
  font-family: var(--ogx-font-family);
9
9
  position: relative;
10
10
  box-shadow: var(--ogx-shadow-sm);
@@ -12,15 +12,12 @@
12
12
  overflow: hidden;
13
13
  }
14
14
  .ogx > :first-child {
15
- border-top-left-radius: calc(var(--ogx-border-radius-lg) - 1px);
16
- border-top-right-radius: calc(var(--ogx-border-radius-lg) - 1px);
15
+ border-top-left-radius: max(0px, calc(var(--ogx-grid-border-radius, var(--ogx-border-radius-lg)) - var(--ogx-grid-border-width)));
16
+ border-top-right-radius: max(0px, calc(var(--ogx-grid-border-radius, var(--ogx-border-radius-lg)) - var(--ogx-grid-border-width)));
17
17
  }
18
18
  .ogx > :last-child {
19
- border-bottom-left-radius: calc(var(--ogx-border-radius-lg) - 1px);
20
- border-bottom-right-radius: calc(var(--ogx-border-radius-lg) - 1px);
21
- }
22
- .ogx:hover {
23
- box-shadow: var(--ogx-shadow-md);
19
+ border-bottom-left-radius: max(0px, calc(var(--ogx-grid-border-radius, var(--ogx-border-radius-lg)) - var(--ogx-grid-border-width)));
20
+ border-bottom-right-radius: max(0px, calc(var(--ogx-grid-border-radius, var(--ogx-border-radius-lg)) - var(--ogx-grid-border-width)));
24
21
  }
25
22
  .ogx__viewport {
26
23
  flex: 1;
@@ -150,7 +147,7 @@
150
147
  min-height: 0;
151
148
  pointer-events: none;
152
149
  z-index: var(--ogx-z-index-modal);
153
- border-radius: var(--ogx-border-radius-lg);
150
+ border-radius: var(--ogx-grid-border-radius, var(--ogx-border-radius-lg));
154
151
  overflow: hidden;
155
152
  }
156
153
  .ogx__loading-overlay--custom {
@@ -243,7 +240,7 @@
243
240
  justify-content: center;
244
241
  overflow: hidden;
245
242
  box-sizing: border-box;
246
- border-inline-end: 1px solid var(--ogx-grid-border-color);
243
+ border-inline-end: var(--ogx-grid-column-divider-width) solid var(--ogx-grid-column-divider-color, var(--ogx-grid-border-color));
247
244
  position: relative;
248
245
  }
249
246
  .ogx__aggregation-cell--pinned-left,
@@ -265,7 +262,7 @@
265
262
  background: var(--ogx-grid-header-background);
266
263
  }
267
264
  .ogx__aggregation-cell--pinned-right {
268
- border-inline-start: 1px solid var(--ogx-grid-border-color);
265
+ border-inline-start: var(--ogx-grid-column-divider-width) solid var(--ogx-grid-column-divider-color, var(--ogx-grid-border-color));
269
266
  }
270
267
  .ogx__aggregation-cell--pinned-left-last {
271
268
  overflow: visible;