@opencorestack/opengridx 2.1.0 → 3.0.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 (225) hide show
  1. package/CHANGELOG.md +399 -0
  2. package/README.md +95 -72
  3. package/dist/index.d.ts +590 -131
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +7891 -5631
  6. package/dist/opengridx.es.js.map +1 -1
  7. package/dist/opengridx.umd.js +17 -23
  8. package/dist/opengridx.umd.js.map +1 -1
  9. package/docs/API_REFERENCE.md +348 -166
  10. package/docs/README.md +6 -1
  11. package/docs/architecture/datagrid-orchestration.md +81 -0
  12. package/docs/architecture/grid-row-meta.md +102 -0
  13. package/docs/architecture/use-grid-columns.md +128 -0
  14. package/docs/architecture/use-grid-controlled-state.md +157 -0
  15. package/docs/architecture/use-grid-row-pipeline.md +120 -0
  16. package/docs/architecture/use-grid-scroll-sync.md +111 -0
  17. package/docs/architecture/use-grid-state-snapshot.md +90 -0
  18. package/docs/architecture/use-grid-virtualization.md +115 -0
  19. package/docs/architecture/use-grid-visible-rows.md +85 -0
  20. package/docs/assets/banner-alt.png +0 -0
  21. package/docs/assets/banner.png +0 -0
  22. package/docs/assets/hero.png +0 -0
  23. package/docs/assets/logo.png +0 -0
  24. package/docs/components/aggregation-footer.md +15 -10
  25. package/docs/components/cell.md +16 -6
  26. package/docs/components/column-group-header.md +55 -28
  27. package/docs/components/column-resize.md +17 -13
  28. package/docs/components/column-visibility.md +6 -7
  29. package/docs/components/datagrid.md +15 -13
  30. package/docs/components/empty-state.md +2 -1
  31. package/docs/components/error-overlay.md +12 -7
  32. package/docs/components/filter-panel.md +28 -8
  33. package/docs/components/header.md +28 -9
  34. package/docs/components/pagination.md +14 -8
  35. package/docs/components/row.md +21 -5
  36. package/docs/components/toolbar.md +27 -9
  37. package/docs/components/tooltip.md +16 -7
  38. package/docs/customization/slots-api.md +90 -38
  39. package/docs/customization/theming.md +66 -18
  40. package/docs/features/aggregation-pivot.md +75 -9
  41. package/docs/features/cell-spanning.md +80 -125
  42. package/docs/features/clipboard.md +41 -16
  43. package/docs/features/custom-pagination.md +38 -18
  44. package/docs/features/data-source.md +36 -13
  45. package/docs/features/editing-reordering.md +125 -16
  46. package/docs/features/export-guide.md +126 -39
  47. package/docs/features/filtering.md +44 -6
  48. package/docs/features/infinite-scroll.md +54 -46
  49. package/docs/features/keyboard-navigation.md +83 -0
  50. package/docs/features/list-view.md +58 -5
  51. package/docs/features/loading-states.md +18 -20
  52. package/docs/features/master-detail.md +18 -5
  53. package/docs/features/pdf-export.md +72 -19
  54. package/docs/features/pinning.md +24 -5
  55. package/docs/features/selection.md +50 -10
  56. package/docs/features/sorting-pagination.md +64 -14
  57. package/docs/features/state-persistence.md +78 -10
  58. package/docs/features/toolbar-customization.md +26 -17
  59. package/docs/features/tree-data-grouping.md +36 -8
  60. package/docs/features/virtualization.md +37 -14
  61. package/docs/migration/v1-to-v2.md +115 -0
  62. package/docs/migration/v2-to-v3.md +1233 -0
  63. package/docs/performance.md +124 -0
  64. package/docs/roadmap.md +5 -5
  65. package/lib/components/Cell/Cell.css +17 -1
  66. package/lib/components/Cell/Cell.tsx +129 -57
  67. package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
  68. package/lib/components/Cell/GridEditInputCell.tsx +114 -41
  69. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
  70. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +15 -2
  71. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
  72. package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
  73. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
  74. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
  75. package/lib/components/DataGrid/DataGrid.css +85 -12
  76. package/lib/components/DataGrid/DataGrid.tsx +582 -876
  77. package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
  78. package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
  79. package/lib/components/DataGrid/GridListView.tsx +165 -24
  80. package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
  81. package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
  82. package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
  83. package/lib/components/DataGrid/GridPinnedRows.tsx +62 -11
  84. package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
  85. package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
  86. package/lib/components/DataGrid/GridVirtualRows.tsx +40 -24
  87. package/lib/components/DetailPanel/DetailPanel.css +0 -6
  88. package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
  89. package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
  90. package/lib/components/FilterPanel/FilterPanel.css +13 -2
  91. package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
  92. package/lib/components/Header/ColumnMenu.tsx +91 -39
  93. package/lib/components/Header/Header.css +6 -3
  94. package/lib/components/Header/Header.tsx +181 -160
  95. package/lib/components/ListView/ListView.css +25 -0
  96. package/lib/components/ListView/ListViewRow.tsx +76 -10
  97. package/lib/components/Pagination/Pagination.tsx +15 -7
  98. package/lib/components/Row/Row.tsx +177 -77
  99. package/lib/components/Toolbar/GlobalSearch.css +6 -3
  100. package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
  101. package/lib/components/Toolbar/GridToolbar.css +12 -11
  102. package/lib/components/Toolbar/GridToolbar.tsx +133 -89
  103. package/lib/components/Toolbar/PivotPanel.tsx +89 -49
  104. package/lib/components/Tooltip/Tooltip.css +20 -0
  105. package/lib/components/Tooltip/Tooltip.tsx +68 -35
  106. package/lib/components/ui/Button.tsx +3 -0
  107. package/lib/components/ui/Checkbox.tsx +2 -1
  108. package/lib/components/ui/ExpandIcon.css +0 -9
  109. package/lib/components/ui/ExpandIcon.tsx +4 -1
  110. package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
  111. package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
  112. package/lib/hooks/core/useDataGrid.ts +115 -173
  113. package/lib/hooks/core/useGridAggregationApi.ts +72 -0
  114. package/lib/hooks/core/useGridApiMethods.ts +115 -0
  115. package/lib/hooks/core/useGridApiRef.ts +19 -4
  116. package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
  117. package/lib/hooks/core/useGridAriaRows.ts +38 -0
  118. package/lib/hooks/core/useGridClipboardApi.ts +38 -0
  119. package/lib/hooks/core/useGridColumnLookup.ts +30 -0
  120. package/lib/hooks/core/useGridColumns.tsx +279 -76
  121. package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
  122. package/lib/hooks/core/useGridControlledState.ts +99 -6
  123. package/lib/hooks/core/useGridDetailPanel.ts +51 -0
  124. package/lib/hooks/core/useGridDevWarnings.ts +104 -5
  125. package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
  126. package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
  127. package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
  128. package/lib/hooks/core/useGridHierarchy.ts +81 -0
  129. package/lib/hooks/core/useGridKeyboardNavigation.ts +447 -243
  130. package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
  131. package/lib/hooks/core/useGridPageCorrection.ts +55 -0
  132. package/lib/hooks/core/useGridRowIdOf.ts +32 -0
  133. package/lib/hooks/core/useGridRowInteractions.ts +81 -0
  134. package/lib/hooks/core/useGridRowPipeline.ts +69 -39
  135. package/lib/hooks/core/useGridRowSelection.ts +91 -0
  136. package/lib/hooks/core/useGridScrollSync.ts +80 -32
  137. package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
  138. package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
  139. package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
  140. package/lib/hooks/core/useGridToolbarProps.ts +97 -0
  141. package/lib/hooks/core/useGridViewport.ts +38 -0
  142. package/lib/hooks/core/useGridViewportSize.ts +26 -0
  143. package/lib/hooks/core/useGridVisibleRows.ts +17 -5
  144. package/lib/hooks/core/useLayout.ts +215 -195
  145. package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
  146. package/lib/hooks/core/useStableColumns.ts +34 -0
  147. package/lib/hooks/features/useAggregation.ts +82 -51
  148. package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
  149. package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
  150. package/lib/hooks/features/useGridClipboard.ts +133 -73
  151. package/lib/hooks/features/useGridDataSource.ts +354 -128
  152. package/lib/hooks/features/useGridEditing.ts +211 -51
  153. package/lib/hooks/features/useGridPivot.ts +92 -0
  154. package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
  155. package/lib/hooks/features/useGridSpanning.ts +410 -306
  156. package/lib/hooks/features/usePivot.ts +0 -0
  157. package/lib/hooks/features/useServerTreeChildren.ts +50 -0
  158. package/lib/hooks/useColumnReorder.ts +38 -44
  159. package/lib/hooks/useRowGrouping.ts +298 -160
  160. package/lib/hooks/useRowReorder.ts +62 -31
  161. package/lib/hooks/useTreeData.ts +287 -220
  162. package/lib/index.ts +28 -2
  163. package/lib/state/index.ts +1 -0
  164. package/lib/state/types.ts +7 -1
  165. package/lib/state/useGridStateStorage.ts +88 -29
  166. package/lib/theme/DataGridThemeProvider.tsx +143 -38
  167. package/lib/theme/gridThemeContext.ts +5 -0
  168. package/lib/theme/index.ts +5 -0
  169. package/lib/theme/presets.ts +5 -4
  170. package/lib/theme/types.ts +40 -2
  171. package/lib/types/index.ts +313 -116
  172. package/lib/utils/aggregation/index.ts +119 -24
  173. package/lib/utils/aria/index.ts +40 -0
  174. package/lib/utils/columnGroups.ts +110 -0
  175. package/lib/utils/columnLookup.ts +33 -0
  176. package/lib/utils/columnWidth.ts +22 -0
  177. package/lib/utils/dataSource/index.ts +46 -0
  178. package/lib/utils/detailPanel.ts +66 -0
  179. package/lib/utils/dragData.ts +44 -0
  180. package/lib/utils/editing/index.ts +30 -0
  181. package/lib/utils/export/exportShared.ts +197 -0
  182. package/lib/utils/export/exportToExcelAdvanced.ts +278 -139
  183. package/lib/utils/export/exportToPdf.ts +143 -92
  184. package/lib/utils/export/groupLabel.ts +14 -0
  185. package/lib/utils/export/index.ts +278 -272
  186. package/lib/utils/export/pdfText.ts +50 -0
  187. package/lib/utils/filtering/index.ts +277 -148
  188. package/lib/utils/focus/index.ts +115 -0
  189. package/lib/utils/gridApiRows.ts +35 -0
  190. package/lib/utils/gridModes.ts +77 -0
  191. package/lib/utils/grouping/groupedExportRows.ts +91 -0
  192. package/lib/utils/pagination/index.ts +56 -0
  193. package/lib/utils/pinning/index.ts +77 -33
  194. package/lib/utils/pivot/index.ts +368 -0
  195. package/lib/utils/scroll/index.ts +55 -5
  196. package/lib/utils/scroll/scrollEnd.ts +18 -0
  197. package/lib/utils/sorting/index.ts +154 -30
  198. package/lib/utils/syntheticRows.ts +12 -0
  199. package/lib/utils/values.ts +206 -0
  200. package/lib/utils/viewport.ts +14 -0
  201. package/llms.txt +261 -164
  202. package/package.json +13 -5
  203. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
  204. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
  205. package/lib/components/DataGrid/DataGrid.defects.test.tsx +0 -103
  206. package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
  207. package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
  208. package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
  209. package/lib/components/DataGrid/DataGrid.virtualization.browser.test.tsx +0 -183
  210. package/lib/components/Header/Header.test.tsx +0 -135
  211. package/lib/components/Pagination/Pagination.test.tsx +0 -64
  212. package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
  213. package/lib/hooks/core/useGridControlledState.test.ts +0 -86
  214. package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
  215. package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
  216. package/lib/hooks/features/useAggregation.test.ts +0 -175
  217. package/lib/hooks/features/usePivot.test.ts +0 -202
  218. package/lib/hooks/useRowGrouping.test.ts +0 -298
  219. package/lib/hooks/useTreeData.test.ts +0 -83
  220. package/lib/utils/aggregation/aggregation.test.ts +0 -16
  221. package/lib/utils/export/exportToExcelAdvanced.test.ts +0 -109
  222. package/lib/utils/export/exportToPdf.test.ts +0 -120
  223. package/lib/utils/export/groupedExport.test.ts +0 -144
  224. package/lib/utils/filtering/filtering.test.ts +0 -178
  225. package/lib/utils/sorting/sorting.test.ts +0 -88
@@ -12,41 +12,41 @@ The main component for displaying and interacting with data.
12
12
  #### Props
13
13
  | Prop | Type | Default | Description |
14
14
  | :--- | :--- | :--- | :--- |
15
- | `rows` | `GridRowModel[]` | `[]` | Array of data objects. |
16
- | `columns` | `GridColDef[]` | `[]` | Definitions for the columns. |
17
- | `getRowId` | `(row: GridRowModel) => GridRowId` | `row.id` | Returns a unique identifier for each row. |
18
- | `rowHeight` | `number` | `52` | Height of each row in pixels. |
19
- | `headerHeight` | `number` | `56` | Height of the header row. |
15
+ | `rows` | `R[]` | — | **Required.** Array of data objects. `R` is your row type: any object type, interfaces included (see [Typing your rows](#typing-your-rows)). Pass `[]` when a `dataSource` supplies the rows. |
16
+ | `columns` | `GridColDef<R>[]` or `GridColDef[]` | — | **Required.** Definitions for the columns: typed for your rows, or untyped. An inline array is fine (v3.0+): while every column is shallowly equal to the previous render's (same properties, callbacks compared by identity) the grid keeps its filter, sort and grouping results. A new inline callback (`valueGetter: (p) => …` written in the render) counts as a change. |
17
+ | `getRowId` | `(row: R) => GridRowId` | `row.id` | Returns a unique identifier for each row. It only keys the grid's internal store: rows reach `renderCell`, events, `apiRef` and `processRowUpdate` unchanged, and the id is never written to `row.id` (v3.0+). Pass it to export functions as `getRowId` when you export `selectedRows`. It must not throw: an id is a programming contract, so a throw is not contained (for a `dataSource` row it fails that request with the error overlay). |
18
+ | `rowHeight` | `number` | `52` | Height of each row in pixels. Without it, an enclosing `DataGridThemeProvider`'s `grid.rowHeightStandard` applies (v3.0+). |
19
+ | `headerHeight` | `number` | `56` | Height of the header row. Without it, the theme's `grid.headerHeight` applies (v3.0+). |
20
20
  | `autoHeight` | `boolean` | `false` | Adjust grid height to match row total. |
21
21
  | `overscanRowCount` | `number` | `3` | Minimum rows rendered outside the visible viewport. The grid adapts this upward automatically based on scroll velocity — this prop sets the floor. |
22
- | `loading` | `boolean` | `false` | Shows a loading skeleton overlay. |
22
+ | `loading` | `boolean` | `false` | With no rows, the body shows skeleton rows (or `slots.loadingOverlay`). With rows already shown, they stay and a progress bar runs along the top of the grid (or `slots.loadingOverlay` is shown over them). Works in list view too. |
23
23
  | `checkboxSelection` | `boolean` | `false` | Enable row selection via checkboxes. |
24
- | `pagination` | `boolean` | `false` | Enable the bottom pagination bar. Ignored while `rowGroupingModel` is active (dev-mode warning). |
25
- | `paginationMode` | `'client' \| 'server' \| 'infinite'` | `'client'` | How to handle paging. |
24
+ | `pagination` | `boolean` | `false` | Enable the bottom pagination bar. Ignored while `rowGroupingModel` is active (dev-mode warning) and with `paginationMode="infinite"`. |
25
+ | `paginationMode` | `'client' \| 'server' \| 'infinite'` | `'client'` | How to handle paging. `'infinite'` appends rows from the `dataSource` as `paginationModel.page` grows and never shows a pager or slices rows (see [Infinite Scroll](features/infinite-scroll.md)). |
26
26
  | `paginationModel` | `GridPaginationModel` | — | Controlled pagination state (`{ page, pageSize }`). |
27
27
  | `onPaginationModelChange` | `(model: GridPaginationModel) => void` | — | Fired when page or page size changes. |
28
- | `pageSizeOptions` | `number[]` | `[10, 25, 50, 100]` | Available page size options. |
29
- | `rowCount` | `number` | — | Total rows (required for server-side paging). |
30
- | `height` | `number \| string` | `undefined` | Total height of the grid container. |
31
- | `density` | `'compact' \| 'standard' \| 'comfortable'` | `'standard'` | Visual row density. |
32
- | `initialState` | `GridInitialState` | `undefined` | Starting state for sorting, filters, etc. |
33
- | `slots` | `GridSlots` | `{}` | Custom component overrides: `toolbar`, `pagination`, `noRowsOverlay`, `loadingOverlay`, `footer`. See [Slots API](customization/slots-api.md). |
34
- | `slotProps` | `Record<string, unknown>` | `{}` | Props passed to custom slots. |
35
- | `filterModel` | `GridFilterModel` | `undefined` | Active filters. |
28
+ | `pageSizeOptions` | `number[]` | `[10, 25, 50, 100]` | Available page size options. The uncontrolled default page size is 100 when offered, else the first option. |
29
+ | `rowCount` | `number` | — | Server total for `paginationMode="server"` when you fetch pages yourself (read on every render). With a `dataSource`, the response's `rowCount` is used and this is only the fallback before the first response. |
30
+ | `height` | `number \| string` | `undefined` | Total height of the grid. Without it the grid fills its container (`height: 100%`, or `flex: 1 1 auto; min-height: 0` as a flex item; v3.0.1+), and grows to fit every row in an auto-height container. |
31
+ | `density` | `'compact' \| 'standard' \| 'comfortable'` | `'standard'` | Visual row density: compact = the theme's `grid.rowHeightCompact` or 32 px, standard = `rowHeight`, comfortable = the theme's `grid.rowHeightComfortable` or 72 px. |
32
+ | `initialState` | `GridInitialState` | `undefined` | Starting state (sort, filter, pagination, columns, density), read on mount. Seeds the grid's uncontrolled state; see [State Persistence](features/state-persistence.md). |
33
+ | `slots` | `GridSlots` | — | Custom component overrides: `toolbar`, `pagination`, `noRowsOverlay`, `loadingOverlay`, `footer`. See [`GridSlots`](#gridslots-and-gridslotprops) and the [Slots API](customization/slots-api.md). |
34
+ | `slotProps` | `GridSlotProps` | — | Props passed to the slots, keyed like `slots`. `slotProps.toolbar` is typed as `Partial<GridToolbarProps>` (plus any extra keys your own toolbar reads), so `GridToolbar` render props are type-checked (v3.0+). |
35
+ | `filterModel` | `GridFilterModel` | `undefined` | Active filters (controlled). Omit it to let the grid keep its own filter state, seeded from `initialState.filter` and changed by the toolbar or `apiRef.setFilterModel` (v3.0+). |
36
36
  | `sortModel` | `GridSortItem[]` | `undefined` | Active sorting. |
37
- | `onRowClick` | `(params: GridRowParams) => void` | — | Fired when a row is clicked. |
38
- | `onRowDoubleClick` | `(params: GridRowParams) => void` | — | Fired when a row is double-clicked (v2.1+). Also fires for group rows; not fired on the checkbox, expand icon, drag handle or an open editor. |
39
- | `onCellClick` | `(params: GridCellParams) => void` | — | Fired when a cell is clicked. |
40
- | `onStateChange` | `(state: GridState) => void` | — | Fired on any internal state update. |
41
- | `processRowUpdate` | `(new, old) => R \| Promise<R>` | — | Fired after a cell edit is committed. |
37
+ | `onRowClick` | `(params: GridRowParams) => void` | — | Fired when a row is clicked. Tree-data parent rows are real rows: a click fires this and selects them (their chevron expands them). Not fired for synthetic rows (row-grouping group rows, subtotal rows, auto-created tree parents): clicking a group row toggles it. |
38
+ | `onRowDoubleClick` | `(params: GridRowParams) => void` | — | Fired when a row is double-clicked (v2.1+). Also fires for group rows, and (v3.0+) for the double-click that opens an editor on an editable cell; not fired on the checkbox, expand icon, drag handle or inside an open editor. |
39
+ | `onCellClick` | `(params: GridCellParams) => void` | — | Fired when a cell is clicked. Not fired for clicks inside an open editor (v3.0+). |
40
+ | `onStateChange` | `(state: GridState) => void` | — | Fired on mount and whenever the value of the sort, filter, pagination, column or density state changes (not on re-renders with equal props). |
41
+ | `processRowUpdate` | `(new, old) => R \| Promise<R>` | — | Called once per committed cell edit (Enter, Tab, blur, or the editor's cell leaving the grid). Return the row to store, or a Promise of it; the editor stays open until the Promise settles. Returning nothing is reported through `onProcessRowUpdateError`. See [Editing](features/editing-reordering.md#commit-and-cancel). |
42
42
  | `dataSource` | `GridDataSource` | — | Remote data provider interface. |
43
43
 
44
44
  #### Sorting, Filtering & Pagination
45
45
 
46
46
  | Prop | Type | Default | Description |
47
47
  | :--- | :--- | :--- | :--- |
48
- | `sortingMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles sorting. The grid calls `onSortModelChange` but does not re-sort rows locally. |
49
- | `filterMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles filtering. The grid calls `onFilterModelChange` but does not re-filter rows locally. |
48
+ | `sortingMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles sorting. The grid calls `onSortModelChange` but does not re-sort rows locally, with or without a `dataSource` (v3.0+). |
49
+ | `filterMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles filtering. The grid calls `onFilterModelChange` but does not re-filter rows locally, with or without a `dataSource` (v3.0+). |
50
50
  | `multiSort` | `boolean` | `false` | When `true`, clicking any sortable column header appends/cycles it in the sort model instead of replacing it — no Shift key required. Shift+click always appends regardless of this prop. |
51
51
  | `onSortModelChange` | `(model: GridSortItem[]) => void` | — | Fired when the active sort model changes. |
52
52
  | `onFilterModelChange` | `(model: GridFilterModel) => void` | — | Fired when the active filter model changes. |
@@ -58,7 +58,8 @@ The main component for displaying and interacting with data.
58
58
  | `rowSelectionModel` | `GridRowSelectionModel` | `[]` | Controlled selection state — array of selected row IDs. |
59
59
  | `onRowSelectionModelChange` | `(model: GridRowSelectionModel) => void` | — | Fired when the selection changes. |
60
60
  | `disableRowSelectionOnClick` | `boolean` | `false` | When `true`, clicking a row does not toggle its selection. |
61
- | `disableMultipleRowSelection` | `boolean` | `false` | When `true`, at most one row can be selected at a time. |
61
+ | `disableMultipleRowSelection` | `boolean` | `false` | When `true`, at most one row can be selected at a time — by click, checkbox, Space or `apiRef` — and the header select-all checkbox is not shown. |
62
+ | `disableClipboardCopy` | `boolean` | `false` | When `true`, Ctrl+C / Cmd+C does not copy the selected rows, so the page or your own handler owns the shortcut. `apiRef.current.copySelectedRows()` still works. See [Clipboard](features/clipboard.md). |
62
63
 
63
64
  #### Column Visibility
64
65
 
@@ -71,11 +72,12 @@ The main component for displaying and interacting with data.
71
72
 
72
73
  | Prop | Type | Default | Description |
73
74
  | :--- | :--- | :--- | :--- |
74
- | `disableColumnReorder` | `boolean` | `false` | Disables drag-and-drop column reordering. |
75
+ | `disableColumnReorder` | `boolean` | `false` | Disables drag-and-drop and Columns-panel reordering. A controlled `columnOrder` or `initialState.columns.columnOrder` still applies. |
75
76
  | `columnOrder` | `GridColumnOrder` | — | Controlled ordered array of column field names. |
76
- | `onColumnOrderChange` | `(params: GridColumnOrderChangeParams) => void` | — | Fired after a column is dragged to a new position. |
77
+ | `onColumnOrderChange` | `(params: GridColumnOrderChangeParams) => void` | — | Fired after the user moves one column (header drag or Columns panel drag). Indices are positions in the grid's full column order. |
78
+ | `onColumnOrderModelChange` | `(columnOrder: GridColumnOrder) => void` | — | Fired with the whole new column order after every change, including the Columns panel's **Reset**. Use it to keep a controlled `columnOrder` in sync (v3.0+). Not fired for generated pivot columns. |
77
79
  | `rowReordering` | `boolean` | `false` | Enables drag-and-drop row reordering. |
78
- | `onRowOrderChange` | `(params: GridRowOrderChangeParams) => void` | — | Fired after a row is dragged to a new position. |
80
+ | `onRowOrderChange` | `(params: GridRowOrderChangeParams) => void` | — | Fired after a row is dragged onto another row. Indices are positions in your `rows` prop. |
79
81
 
80
82
  #### Pinning
81
83
 
@@ -83,7 +85,7 @@ The main component for displaying and interacting with data.
83
85
  | :--- | :--- | :--- | :--- |
84
86
  | `pinnedColumns` | `GridColumnPinning` | — | Columns pinned to the left or right viewport edges. See [Column & Row Pinning](#-column--row-pinning). |
85
87
  | `onPinnedColumnsChange` | `(model: GridColumnPinning) => void` | — | Fired when column pinning changes. |
86
- | `pinnedRows` | `GridRowPinning` | — | Row IDs pinned to the top or bottom of the viewport. |
88
+ | `pinnedRows` | `GridRowPinning` | — | Row IDs pinned to the top or bottom of the viewport. Pinned rows still have to pass the filter. Ignored (with a development warning) under tree data and row grouping, where the rows stay in the hierarchy. |
87
89
  | `pinCheckboxColumn` | `boolean` | `true` | Keeps the checkbox column visible during horizontal scrolling. |
88
90
  | `pinExpandColumn` | `boolean` | `true` | Keeps the Master-Detail expansion column visible during horizontal scrolling. |
89
91
 
@@ -91,15 +93,15 @@ The main component for displaying and interacting with data.
91
93
 
92
94
  | Prop | Type | Default | Description |
93
95
  | :--- | :--- | :--- | :--- |
94
- | `isCellEditable` | `(params: GridCellParams) => boolean` | — | Per-cell predicate. Return `false` to make a specific cell read-only even when the column has `editable: true`. |
95
- | `onProcessRowUpdateError` | `(error: unknown) => void` | — | Fired if `processRowUpdate` throws or returns a rejected Promise. Use to display validation errors or restore the previous row value. |
96
+ | `isCellEditable` | `(params: GridCellParams) => boolean` | — | Per-cell predicate. Return `false` to make a specific cell read-only even when the column has `editable: true`. Applies to double-click, Enter, Tab stops and `aria-readonly` (v3.0+). It can only restrict: a column without `editable: true` is never editable. A predicate that throws counts as `false`. |
97
+ | `onProcessRowUpdateError` | `(error: unknown) => void` | — | Fired if `processRowUpdate` (or `valueSetter`) throws, returns a rejected Promise, or returns something that is not a row. The editor stays open with the pending value. Use to display validation errors. |
96
98
 
97
99
  #### Master-Detail
98
100
 
99
101
  | Prop | Type | Default | Description |
100
102
  | :--- | :--- | :--- | :--- |
101
103
  | `getDetailPanelContent` | `(params: GridDetailPanelParams) => ReactNode` | — | Returns the JSX content rendered inside the expandable detail panel. |
102
- | `getDetailPanelHeight` | `(params: GridDetailPanelParams) => number \| 'auto'` | `'auto'` | Controls the panel height in pixels, or `'auto'` to fit content. |
104
+ | `getDetailPanelHeight` | `(params: GridDetailPanelParams) => GridDetailPanelHeight` | `200` | Panel height in pixels (`0` is a 0px panel), or `'auto'` to fit the content. An `'auto'` panel is measured when it renders and whenever its size changes, and the rows below it are laid out at that height. Without this prop every panel is a fixed 200px scroll box. A callback that throws gets the 200px default (v3.0+, with a one-time development warning). |
103
105
  | `detailPanelExpandedRowIds` | `Set<GridRowId>` | — | Controlled set of currently-expanded detail panel row IDs. |
104
106
  | `onDetailPanelExpandedRowIdsChange` | `(ids: Set<GridRowId>) => void` | — | Fired when detail panels expand or collapse. |
105
107
 
@@ -108,8 +110,8 @@ The main component for displaying and interacting with data.
108
110
  | Prop | Type | Default | Description |
109
111
  | :--- | :--- | :--- | :--- |
110
112
  | `treeData` | `boolean` | `false` | Enables hierarchical tree data display. |
111
- | `getTreeDataPath` | `(row: R) => string[]` | — | Returns the hierarchy path for a row (e.g. `['Engineering', 'Frontend']`). |
112
- | `groupingColDef` | `Partial<GridColDef>` | — | Configures the dedicated `__group__` column prepended at position 0 (auto-pinned left) when row grouping is active. Accepts `headerName`, `width`, `renderCell`, and any non-system `GridColDef` field. The column's `field` is always `'__group__'`; it is never sorted, exported, hidden, or re-ordered. |
113
+ | `getTreeDataPath` | `(row: R) => string[]` | — | Returns the hierarchy path for a row (e.g. `['Engineering', 'Frontend']`). Required with `treeData`: without it the rows are shown flat. Keep its identity stable (module scope or `useCallback`); a new function rebuilds the tree. It must not throw (a throw is not contained). |
114
+ | `groupingColDef` | `Partial<GridColDef<R>>` | — | Configures the dedicated `__group__` column prepended at position 0 (auto-pinned left) when row grouping or tree data is active. Under tree data its cells show the last segment of each row's `getTreeDataPath` (override with `groupingColDef.valueGetter`). Accepts `headerName`, `width`, `renderCell`, and any non-system `GridColDef` field. Its `renderCell` is called for group rows too (check `params.rowMeta?.isGroupRow`); its output replaces the default group label next to the toggle, and returning `undefined` for a group row keeps the default label. The column's `field` is always `'__group__'` (no `field` is needed; one you pass is ignored); it is never sorted, filtered, pinned by the user, exported, hidden, or re-ordered. Defaults: `headerName: 'Group'`, `width: 220`. |
113
115
  | `defaultGroupingExpansionDepth` | `number` | `0` | Number of tree levels expanded on initial render (`-1` = all). |
114
116
 
115
117
  #### Row Grouping & Aggregation
@@ -119,13 +121,13 @@ The main component for displaying and interacting with data.
119
121
  | `rowGroupingModel` | `GridRowGroupingModel` | `[]` | Array of field names to group rows by (e.g. `['department', 'team']`). See [Row Grouping](#️-row-grouping). |
120
122
  | `aggregationModel` | `GridAggregationModel` | — | Map of `field → aggFn` (e.g. `{ salary: 'sum', age: 'avg' }`). See [Aggregation Reference](#-aggregation-reference). |
121
123
  | `onAggregationModelChange` | `(model: GridAggregationModel) => void` | — | Fired when the aggregation model changes. |
122
- | `getAggregationPosition` | `(groupNode: GridTreeNode \| null) => 'inline' \| 'footer' \| null` | — | Controls where aggregation results appear. `'inline'` = inside the group row, `'footer'` = a dedicated row below the group, `null` = hidden. Pass `null` groupNode = grand-total (root) position. |
124
+ | `getAggregationPosition` | `(groupNode: GridTreeNode \| null) => 'inline' \| 'footer' \| null` | — | Controls where aggregation results appear. Called for every group node (with its current `isExpanded`) and once with `null` for the grand total. `'inline'` (group default) = on the group row, `'footer'` = on a subtotal row after the group's children while it is expanded (on the group row while collapsed), `null` = hidden. For the grand total, `null` hides the footer row. A callback that throws gets the default (`'inline'` for a group, `'footer'` for the grand total; v3.0+). See [the callback](#getaggregationposition-callback). |
123
125
 
124
126
  #### Pivot
125
127
 
126
128
  | Prop | Type | Default | Description |
127
129
  | :--- | :--- | :--- | :--- |
128
- | `pivotMode` | `boolean` | `false` | Switches the grid to multidimensional Pivot Mode. |
130
+ | `pivotMode` | `boolean` | `false` | Switches the grid to multidimensional Pivot Mode (client-side: ignored when a `dataSource` is set; tree data and row grouping are turned off while pivoting). See [Aggregation & Pivot](features/aggregation-pivot.md#-pivot-mode). |
129
131
  | `pivotModel` | `GridPivotModel` | — | Controlled pivot configuration (row fields, column fields, value fields). |
130
132
  | `onPivotModelChange` | `(model: GridPivotModel) => void` | — | Fired when the pivot model changes. |
131
133
 
@@ -133,7 +135,7 @@ The main component for displaying and interacting with data.
133
135
 
134
136
  | Prop | Type | Default | Description |
135
137
  | :--- | :--- | :--- | :--- |
136
- | `onRowsScrollEnd` | `(params: GridRowScrollEndParams) => void` | — | Fired when the user scrolls to the bottom of the grid viewport. Use this to trigger the next page in infinite-scroll mode. |
138
+ | `onRowsScrollEnd` | `(params: GridRowScrollEndParams) => void` | — | Fired once each time the viewport comes within 100px of the bottom of the rows: after a vertical scroll, and also when the rows change while the end is in view (including rows that do not fill the viewport). It fires again only after the viewport leaves that zone or the row count changes; horizontal scrolling never fires it. `listView` follows the same once-per-arrival rule for its list (it re-arms when the list leaves the zone or the row count changes). Use it to load the next page in infinite-scroll mode. |
137
139
 
138
140
  #### Accessibility & Appearance
139
141
 
@@ -148,19 +150,19 @@ The main component for displaying and interacting with data.
148
150
 
149
151
  | Prop | Type | Default | Description |
150
152
  | :--- | :--- | :--- | :--- |
151
- | `localeText` | `GridLocaleText` | — | Override user-visible strings in the Pagination component. See `GridLocaleText` for the full interface. |
153
+ | `localeText` | `GridLocaleText` | — | Override user-visible strings: the Pagination labels and the empty-state label (`noRowsLabel`, which wins over the `noRowsLabel` prop). See [`GridLocaleText`](#gridlocaletext). |
152
154
 
153
155
  #### Imperative Ref
154
156
 
155
157
  | Prop | Type | Default | Description |
156
158
  | :--- | :--- | :--- | :--- |
157
- | `apiRef` | `React.MutableRefObject<GridApi>` | — | Reactive ref to the imperative API. Create with `useGridApiRef()`. |
159
+ | `apiRef` | `React.MutableRefObject<GridApi<R>> \| React.MutableRefObject<GridApi>` | — | Reactive ref to the imperative API. Create with `useGridApiRef()`, or `useGridApiRef<Row>()` for typed row getters (v3.0.1+). |
158
160
 
159
161
  #### List View
160
162
 
161
163
  | Prop | Type | Default | Description |
162
164
  | :--- | :--- | :--- | :--- |
163
- | `listView` | `boolean` | `false` | Renders the grid as a single-column list of cards. Designed for mobile and responsive layouts. |
165
+ | `listView` | `boolean` | `false` | Renders the grid as a single-column list of cards. Designed for mobile and responsive layouts. Requires `listViewColumn`; without it the grid view is shown and a dev warning is logged. |
164
166
  | `listViewColumn` | `GridListViewColDef` | — | Column definition for list view mode. Must provide a `renderCell` function. The `field` value is used as a key. |
165
167
 
166
168
  #### Column Group Headers
@@ -169,11 +171,57 @@ The main component for displaying and interacting with data.
169
171
  | :--- | :--- | :--- | :--- |
170
172
  | `columnGroupingModel` | `GridColumnGroupingModel` | — | Defines spanning header rows above the regular column headers. See [Column Group Headers](#️-column-group-headers). |
171
173
 
174
+ ### Typing your rows
175
+
176
+ `DataGrid` and every generic type (`GridColDef<R>`, `GridRenderCellParams<R>`, `GridRowParams<R>` …) take your row type `R`. Since v3.0 `R` can be any object type (`GridValidRowModel`), so your own interfaces work directly: they need no index signature and do not have to extend `GridRowModel`. `R` is inferred from `rows`.
177
+
178
+ ```tsx
179
+ import { DataGrid } from '@opencorestack/opengridx';
180
+ import type { GridColDef } from '@opencorestack/opengridx';
181
+
182
+ interface Employee { id: number; name: string; salary: number }
183
+
184
+ // Typed columns: callbacks see `params.row` as Employee.
185
+ const columns: GridColDef<Employee>[] = [
186
+ { field: 'name', renderCell: ({ row }) => row.name.toUpperCase() },
187
+ { field: 'salary', valueGetter: ({ row }) => row.salary * 12,
188
+ valueSetter: ({ row, value }) => ({ ...row, salary: Number(value) / 12 }) },
189
+ ];
190
+
191
+ <DataGrid rows={employees} columns={columns} onRowClick={({ row }) => console.log(row.name)} />;
192
+ ```
193
+
194
+ - **Untyped columns** (`GridColDef[]`, as in the README) work with rows of any type. Their callbacks see `params.row` as `GridRowModel` (any key, read as `unknown`). The `DataGridUntypedColumnsProps<R>` type describes these props.
195
+ - **Inline column arrays** (`columns={[{ field: 'name', renderCell: ({ row }) => … }]}`) get `R` from `rows`.
196
+ - **Rows without an `id`** need `getRowId`, e.g. `getRowId={(row) => row.code}`.
197
+ - A `GridColDef<Row>` for a type-alias row (or an interface that extends `GridRowModel`) can be used where a `GridColDef` is expected: the row callbacks are declared as methods, so they are checked bivariantly.
198
+ - `params.value` stays `unknown`; read typed values from `params.row`, or render `params.formattedValue`.
199
+ - The export functions, `usePivot` and `useAggregation` accept the same typed or untyped columns, and their options' `getRowId` takes your row type.
200
+ - `apiRef` row getters (`getRow`, `getAllRows`, `getVisibleRows`, `getAllFilteredRows`) return your row type when the ref is created with `useGridApiRef<Row>()` (v3.0.1+); a plain `useGridApiRef()` returns `GridRowModel`.
201
+ - For the component's props type, use `DataGridProps<R>`. `React.ComponentProps<typeof DataGrid>` resolves to the untyped-columns overload.
202
+
203
+ ### `GridSlots` and `GridSlotProps`
204
+
205
+ The types of the `slots` and `slotProps` props (v3.0+ exports). Each slot is typed with the props the grid passes it, so a slot component with its own typed props (e.g. `function Footer({ rowCount }: { rowCount: number })`) is checked, and an inline slot (`footer: (props) => …`) gets its parameter typed. A function component typed `(props: Record<string, unknown>)` is still accepted; a class, `memo` or `forwardRef` component needs the slot's props type (e.g. `GridToolbarSlotProps`). A prop the grid does not pass (one you supply through `slotProps`) must be optional in your component, or close over it in an inline slot.
206
+
207
+ | Slot | Replaces | Receives |
208
+ | :--- | :--- | :--- |
209
+ | `toolbar` | Nothing by default: renders a toolbar above the grid. Use `GridToolbar` or your own component. | `GridToolbarSlotProps`: the toolbar props the grid owns (see [`GridToolbarProps`](#gridtoolbarprops)) plus `apiRef`, then `slotProps.toolbar`. |
210
+ | `pagination` | The built-in `Pagination` bar (shown with `pagination`). | `GridPaginationSlotProps` (= `PaginationProps`: `page`, `pageSize`, `rowCount`, `pageSizeOptions`, `onPageChange`, `onPageSizeChange`, `localeText`), then `slotProps.pagination`. |
211
+ | `noRowsOverlay` | The empty-state icon and label (shown when there are no rows and the grid is not loading). | `slotProps.noRowsOverlay` only (`GridOverlaySlotProps`). |
212
+ | `loadingOverlay` | The loading indicator: the skeleton rows while loading with no rows, the progress bar while loading with rows shown (the slot is then shown over them). | `slotProps.loadingOverlay` only (`GridOverlaySlotProps`). |
213
+ | `footer` | The pagination area. | `GridFooterSlotProps`: `apiRef`, `aggregationModel`, `aggregationResult`, `rowCount`, `pagination`, `paginationModel`, `pageSizeOptions`, `onPaginationModelChange`, then `slotProps.footer`. `rowCount` is the built-in pager's count (pinned rows excluded, v3.0+); under tree data or row grouping, the filtered data rows. |
214
+
215
+ ```ts
216
+ type GridSlots = NonNullable<DataGridProps['slots']>;
217
+ type GridSlotProps = NonNullable<DataGridProps['slotProps']>;
218
+ ```
219
+
172
220
  ---
173
221
 
174
222
  ## 🛠️ `GridToolbar`
175
223
 
176
- The built-in toolbar component. Mount it via `slots={{ toolbar: GridToolbar }}`. All props below are passed through `slotProps.toolbar` (or directly when building a custom wrapper).
224
+ The built-in toolbar component. Mount it via `slots={{ toolbar: GridToolbar }}`. The grid passes the models and change handlers it owns (plus `apiRef`), then spreads `slotProps.toolbar` over them, so any prop below can be set or overridden there (or directly when rendering `GridToolbar` yourself).
177
225
 
178
226
  ### `GridToolbarProps`
179
227
 
@@ -182,17 +230,18 @@ The built-in toolbar component. Mount it via `slots={{ toolbar: GridToolbar }}`.
182
230
  | `columns` | `GridColDef[]` | `[]` | Column definitions — injected automatically when used via `slots`. |
183
231
  | `baseColumns` | `GridColDef[]` | — | Pre-pivot columns shown in the Pivot panel instead of synthetic pivot columns. |
184
232
  | `aggregationModel` | `GridAggregationModel` | `{}` | Current aggregation configuration. |
185
- | `onAggregationModelChange` | `(model: GridAggregationModel) => void` | — | Called when the user changes aggregation settings. |
186
- | `pivotModel` | `GridPivotModel` | — | Current pivot configuration. Presence of this prop renders the Pivot button. |
187
- | `onPivotModelChange` | `(model: GridPivotModel) => void` | — | Called when the user changes pivot settings. |
188
- | `filterModel` | `GridFilterModel` | — | Current filter model. Presence of this prop renders the search bar and Filter button. |
189
- | `onFilterModelChange` | `(model: GridFilterModel) => void` | — | Called when the user changes filters or the quick-search value. |
233
+ | `onAggregationModelChange` | `(model: GridAggregationModel) => void` | — | Called when the user changes aggregation settings. Presence of this prop renders the Summaries button; the grid always injects it when the toolbar is mounted via `slots`. |
234
+ | `pivotModel` | `GridPivotModel` | — | Current pivot configuration. |
235
+ | `onPivotModelChange` | `(model: GridPivotModel) => void` | — | Called when the user changes pivot settings. Presence of this prop renders the Pivot button; the grid injects it when `pivotMode`, `pivotModel` or `onPivotModelChange` is set. |
236
+ | `filterModel` | `GridFilterModel` | — | Current filter model. |
237
+ | `onFilterModelChange` | `(model: GridFilterModel) => void` | — | Called when the user changes filters or the quick-search value. Its presence renders the search bar and Filter button; the grid always injects it when the toolbar is mounted via `slots` (v3.0+). |
190
238
  | `columnVisibilityModel` | `Record<string, boolean>` | `{}` | Current column visibility map. |
191
- | `onColumnVisibilityModelChange` | `(model: Record<string, boolean>) => void` | — | Called when the user shows or hides a column. Presence renders the Columns button. |
239
+ | `onColumnVisibilityModelChange` | `(model: Record<string, boolean>) => void` | — | Called when the user shows or hides a column. Presence renders the Columns button; the grid always injects it when the toolbar is mounted via `slots`. |
240
+ | `showNonHideableColumns` | `boolean` | `false` | Show `hideable: false` columns in the Columns panel as disabled rows. |
192
241
  | `onColumnReorder` | `(from: string, to: string) => void` | — | Called when the user drags a column in the Columns panel. |
193
242
  | `onColumnOrderReset` | `() => void` | — | Called when the user clicks "Reset order" in the Columns panel. |
194
- | `forceColumnsOpen` | `boolean` | — | Forces the Columns panel open (used internally by the column header context menu). |
195
- | `onColumnsPanelClose` | `() => void` | — | Called when the Columns panel closes after a `forceColumnsOpen`. |
243
+ | `forceColumnsOpen` | `boolean` | — | Opens the Columns panel when it becomes `true` (set by `DataGrid` for the column menu's **Manage columns**). If the toolbar slot does not render a `GridToolbar` that receives it, the grid opens its standalone Columns panel instead. |
244
+ | `onColumnsPanelClose` | `() => void` | — | Called whenever the Columns panel closes (its button, a custom button, another panel opening, click-outside or Escape). |
196
245
  | `children` | `ReactNode` | — | Content rendered on the **left** side of the toolbar, before the spacer. |
197
246
  | `rightContent` | `ReactNode` | — | Content rendered on the **right** side, after all built-in buttons. |
198
247
  | `className` | `string` | — | Additional CSS class on the toolbar root `<div>`. Use for visual theme overrides. |
@@ -219,8 +268,8 @@ Passed to `renderQuickFilter`.
219
268
 
220
269
  | Property | Type | Description |
221
270
  | :--- | :--- | :--- |
222
- | `value` | `string` | Current search string. |
223
- | `onChange` | `(value: string) => void` | Call with the updated string when the input changes. |
271
+ | `value` | `string` | Current search string: `filterModel.quickFilterValues` joined with spaces. |
272
+ | `onChange` | `(value: string) => void` | Call with the updated string when the input changes. The toolbar splits it on whitespace into `quickFilterValues` terms. |
224
273
 
225
274
  ---
226
275
 
@@ -230,44 +279,50 @@ Access these methods via the `apiRef` prop.
230
279
 
231
280
  | Method | Return | Description |
232
281
  | :--- | :--- | :--- |
233
- | `getRow(id)` | `GridRowModel \| null` | Get row data by ID. |
234
- | `getAllRows()` | `GridRowModel[]` | Get all loaded rows. |
235
- | `getVisibleRows()` | `GridRowModel[]` | Get rows after filtering/sorting. |
282
+ | `getRow(id)` | `R \| null` | Get row data by ID (your row object, unchanged). `R` is the type passed to `useGridApiRef<R>()`, `GridRowModel` by default. |
283
+ | `getAllRows()` | `R[]` | Get all loaded rows. |
284
+ | `getVisibleRows()` | `R[]` | Get the rows on screen after filtering/sorting: pinned-top rows, the current page (or all rows without pagination), pinned-bottom rows. Under row grouping and tree data this is the visible hierarchy, group rows included. |
236
285
  | `getColumn(field)` | `GridColDef \| null` | Get column definition by field. |
237
- | `getVisibleColumns()` | `GridColDef[]` | Get currently visible columns. |
238
- | `selectRow(id, isSelected)` | `void` | Set selection for a single row. |
239
- | `selectRows(ids, isSelected)`| `void` | Set selection for multiple rows. |
286
+ | `getVisibleColumns()` | `GridColDef[]` | Get the columns on screen, in render order (hidden columns excluded): left-pinned, unpinned, right-pinned, as the header shows them (v3.0+; before, pinned columns kept their unpinned position). |
287
+ | `selectRow(id, isSelected = true)` | `void` | Set selection for a single row. Fires `onRowSelectionModelChange`, unless nothing changes (selecting a selected row, deselecting an unselected one; v3.0+). Ids of rows the grid made (group headers, subtotals, auto-created tree parents, the pivot Grand Total) are ignored, as they are for the checkbox, click and keyboard (v3.0+). |
288
+ | `selectRows(ids, isSelected = true)`| `void` | Set selection for multiple rows. With `disableMultipleRowSelection`, selecting keeps only the last id. Same no-op and synthetic-id rules as `selectRow`. |
240
289
  | `getSelectedRows()` | `GridRowId[]` | Get IDs of all selected rows. |
241
- | `sortColumn(field, dir)` | `void` | Programmatically sort a column. |
242
- | `getSortModel()` | `GridSortItem[]` | Get active sorting state. |
243
- | `setFilterModel(model)` | `void` | Programmatically set filters. |
244
- | `getFilterModel()` | `GridFilterModel` | Get the current filter model. |
245
- | `setPage(page)` | `void` | Change current page (0-indexed). |
246
- | `setPageSize(pageSize)` | `void` | Change the current page size. |
247
- | `scrollToIndexes(params)` | `void` | Scroll to specific row/column index. |
290
+ | `sortColumn(field, dir)` | `void` | Sort like a header click: replaces the sort model (or, with `multiSort`, updates/appends that field); `null` removes the field. Fires `onSortModelChange`. |
291
+ | `getSortModel()` | `GridSortItem[]` | Get the sort model the grid is using (controlled or not). |
292
+ | `setFilterModel(model)` | `void` | Set filters. Fires `onFilterModelChange`; with a controlled `filterModel`, update the prop from it. |
293
+ | `getFilterModel()` | `GridFilterModel` | Get the filter model the grid is using (controlled or not). |
294
+ | `setPage(page)` | `void` | Change current page (0-indexed). Fires `onPaginationModelChange`. |
295
+ | `setPageSize(pageSize)` | `void` | Change the page size and go to page 0. Fires `onPaginationModelChange`. |
296
+ | `scrollToIndexes(params)` | `void` | Scroll to specific row/column index. Uses the current layout, also when called from a layout effect right after an update (v3.0+). |
248
297
  | `getAllColumns()` | `GridColDef[]` | Get all defined columns. |
249
- | `getAggregationResult()` | `Record<string, unknown> \| null` | Get current aggregation results. |
298
+ | `getAggregationResult()` | `Record<string, unknown> \| null` | Get current aggregation results (`null` without an `aggregationModel`, and in pivot mode). Live in a parent's `useLayoutEffect` on mount and current after every update (v3.0+; before, it was installed in a passive effect). The same holds for `getAggregationModel()` and `getGroupedExportRows()`. |
250
299
  | `getAggregationModel()` | `GridAggregationModel \| null` | Get the active aggregation configuration. |
251
- | `getAllFilteredRows()` | `GridRowModel[]` | Get all filtered+sorted rows regardless of the current pagination window. Use for full-dataset exports. |
252
- | `getGroupedExportRows()` | `GridGroupedExportRow[] \| null` | Get a flat ordered list reflecting the active row-grouping tree (group-header, leaf, subtotal, grand-total). Returns `null` when row grouping is not active. |
253
- | `copySelectedRows()` | `Promise<void>` | Copy selected rows to clipboard as TSV. |
300
+ | `getAllFilteredRows()` | `R[]` | Get every row that passes the filter, sorted, regardless of pagination, including pinned rows. Under row grouping and tree data it returns the data rows (no group rows) in hierarchy order with every group expanded, ordered like the screen (also when sorting by the hierarchy column; v3.0+). With tree data and a filter, ancestors shown only to give a match its context are left out: the result is the set select-all and the aggregation footer act on (v3.0+). Use for full-dataset exports. |
301
+ | `getGroupedExportRows()` | `GridGroupedExportRow[] \| null` | Get a flat ordered list reflecting the active row-grouping tree (group-header, leaf, subtotal, grand-total), sorted and filtered like the screen. Collapsed groups are included with their rows, and subtotals are computed over each group's exported rows. Group-header and subtotal entries carry `groupLabel`, the label the grid shows (v3.0+). Returns `null` when row grouping is not active. |
302
+ | `copySelectedRows()` | `Promise<void>` | Copy every selected row that passes the filter (other pages, collapsed groups and pinned rows included) as TSV, with the visible columns in screen order. Resolves without writing when no selected row is found; rejects when the clipboard write fails. |
254
303
 
255
304
  ---
256
305
 
257
306
  ## 🌳 Internal Data Structures
258
307
 
259
308
  ### `GridTreeNode`
260
- Used in Tree Data and Row Grouping hierarchies.
309
+ Used in Tree Data and Row Grouping hierarchies. This is what `getAggregationPosition` receives.
261
310
 
262
311
  | Property | Type | Description |
263
312
  | :--- | :--- | :--- |
264
313
  | `id` | `GridRowId` | Unique ID of the row. |
265
- | `parentId` | `GridRowId \| null` | ID of the parent node. |
314
+ | `parentId` | `GridRowId \| null` | ID of the parent node (`null` at the root). |
266
315
  | `depth` | `number` | Nesting level (0 for root). |
316
+ | `groupingKey` | `string` | The key identifying this node within its parent. |
317
+ | `groupingField` | `string` (optional) | Row grouping: the field this node groups by. |
318
+ | `groupingValue` | `unknown` (optional) | The value this node is grouping by. |
267
319
  | `isExpanded` | `boolean` | Current expansion state. |
268
- | `children` | `GridRowId[]` | IDs of child nodes. |
269
- | `groupingValue` | `unknown` | The value this node is grouping by. |
270
- | `aggregatedValues` | `object` | Sums/Avgs computed for this group. |
320
+ | `children` | `GridRowId[]` (optional) | IDs of the direct child nodes. |
321
+ | `label` | `string` (optional) | Display label of the group. |
322
+ | `aggregatedValues` | `Record<string, unknown>` (optional) | Aggregates computed for this group, keyed by field. |
323
+ | `aggregationPosition` | `'inline' \| 'footer' \| null` (optional) | Where this group's aggregates are shown. |
324
+ | `descendantCount` | `number` (optional) | Row grouping: leaf rows in the group that pass the filter (recursive). |
325
+ | `serverChildrenCount` | `number` (optional) | Server-side tree data: the child count the server reported. |
271
326
 
272
327
  ---
273
328
 
@@ -283,21 +338,23 @@ Defines the behavior and appearance of a single column.
283
338
  | `field` | `string` | — | **Required.** Unique identifier matching the row object key. |
284
339
  | `headerName` | `string` | — | Text shown in the column header cell. |
285
340
  | `description` | `string` | — | Tooltip shown on column header hover (rendered as the HTML `title` attribute — improves accessibility). |
286
- | `width` | `number \| string` | `100` | Fixed width in pixels or a percentage string. |
287
- | `minWidth` | `number` | — | Minimum width in pixels (enforced during resize). |
288
- | `maxWidth` | `number` | — | Maximum width in pixels (enforced during resize). |
289
- | `flex` | `number` | — | Flex grow factor — distributes remaining space proportionally. Mutually exclusive with a fixed `width`. |
341
+ | `width` | `number \| string` | unset: flexes | Fixed width in pixels, or a percentage string (`'30%'`): a share of the width left after the fixed-width columns, the same base for every percentage column. Unset (or `'auto'`), the column behaves like `flex: 1` and shares the free space with the flex columns (at least `minWidth`, 50px by default). Pinned and unpinned columns are sized the same way. Every width, including a manual resize, is clamped to `minWidth` / `maxWidth`. |
342
+ | `minWidth` | `number` | — | Minimum width in pixels (enforced during resize; 50 when omitted, or the column's own width if that is smaller). |
343
+ | `maxWidth` | `number` | — | Maximum width in pixels (enforced during resize; no limit when omitted). |
344
+ | `flex` | `number` | — | Flex grow factor — distributes remaining space proportionally. When both are set, `flex` wins over `width` until the user resizes the column. |
290
345
  | `align` | `'left' \| 'center' \| 'right'` | `'left'` | Horizontal alignment of cell content. |
291
- | `headerAlign` | `'left' \| 'center' \| 'right'` | `'left'` | Horizontal alignment of the header cell content. |
346
+ | `headerAlign` | `'left' \| 'center' \| 'right'` | `align` | Horizontal alignment of the header cell content. Falls back to the column's `align`, then `'left'`. |
347
+ | `zIndex` | `number` | — | CSS `z-index` for the column's cells and header cell (pinned cells default to 3). |
292
348
 
293
349
  #### Data & Type
294
350
 
295
351
  | Property | Type | Default | Description |
296
352
  | :--- | :--- | :--- | :--- |
297
- | `type` | `'string' \| 'number' \| 'date' \| 'boolean' \| 'singleSelect' \| 'image'` | `'string'` | Data type — determines default filter operators and cell formatting. |
298
- | `valueOptions` | `Array<string \| number \| { value: unknown; label: string }>` | — | Allowed values for `type: 'singleSelect'` — used in the filter dropdown and edit cell. |
299
- | `valueGetter` | `(params: GridValueGetterParams) => unknown` | — | Derive a computed value from the row object. Runs before `valueFormatter` and `renderCell`. |
300
- | `valueFormatter` | `(params: GridValueFormatterParams) => string` | — | Format the value into a display string (e.g. currency, dates). Does not affect editing or sorting. |
353
+ | `type` | `'string' \| 'number' \| 'date' \| 'boolean' \| 'singleSelect' \| 'image'` | `'string'` | Data type: sets the default filter operators and, when the column has no `valueFormatter`, the default cell text: `date` shows the local calendar date (`toLocaleDateString()`), `boolean` shows Yes / No, `singleSelect` shows the `valueOptions` label, `image` renders an `<img>` of the URL. `number` and `string` show `String(value)`. The text exporters (CSV, Excel HTML, print, PDF) and list view use the same text. |
354
+ | `valueOptions` | `Array<string \| number \| { value: unknown; label: string }>` | — | Allowed values for `type: 'singleSelect'` — the filter panel offers them as a (multi-)select, and the edit cell uses them; cells and exports show the option's label. |
355
+ | `valueGetter` | `(params: GridValueGetterParams) => unknown` | — | Derive a computed value from the row object. Runs before `valueFormatter` and `renderCell`. Client-side sorting, column filters and the quick filter use this value, and editors start from it (double-click and Enter alike). Keep it pure: the quick filter caches each row's search text per row object and column set. A getter that throws for a row shows an error in that cell and reads as `undefined` for sorting, filtering, aggregation, grouping, pivot and list view (v3.0+, with a one-time development warning per column); before, it crashed the grid once the column was sorted, filtered or aggregated. |
356
+ | `valueSetter` | `(params: GridValueSetterParams) => R` | — | v3.0+. Maps an edited value back onto the row (`{ value, row, field }` → updated row) when an edit is committed. Needed for editable `valueGetter` columns; without it the commit writes `row[field]` and a development warning is logged. |
357
+ | `valueFormatter` | `(params: GridValueFormatterParams) => string` | — | Format the value into a display string (e.g. currency, dates). Does not affect editing, sorting or column filters; the quick filter also searches the formatted text. A formatter that throws leaves the value unformatted (the quick filter searches the raw value; v3.0+). |
301
358
 
302
359
  #### Rendering
303
360
 
@@ -305,20 +362,22 @@ Defines the behavior and appearance of a single column.
305
362
  | :--- | :--- | :--- |
306
363
  | `renderCell` | `(params: GridRenderCellParams) => ReactNode` | Fully custom cell renderer. Receives `value`, `formattedValue` (v2.1+, the `valueFormatter` output), `row`, `field`, `colDef`, `rowIndex`, `colIndex`, `rowMeta`. |
307
364
  | `renderHeader` | `(params: GridRenderHeaderParams) => ReactNode` | Custom header cell renderer. Use for icons, sort indicators, or rich headers. |
308
- | `renderEditCell` | `(params: GridRenderCellParams) => ReactNode` | Custom editor rendered when the cell enters edit mode. Requires `editable: true`. |
365
+ | `renderEditCell` | `(params: GridRenderEditCellParams) => ReactNode` | Custom editor rendered when the cell enters edit mode. Requires `editable: true`. Receives the `renderCell` params (with `value` = the pending value) plus `onValueChange(value)`, `onCommit()` and `onCancel()` (v3.0+). Errors it throws are contained to the cell. |
309
366
 
310
367
  #### Editing
311
368
 
312
369
  | Property | Type | Default | Description |
313
370
  | :--- | :--- | :--- | :--- |
314
- | `editable` | `boolean` | `false` | Enables inline cell editing. Commit is handled by `DataGrid.processRowUpdate`. |
371
+ | `editable` | `boolean` | `false` | Enables inline cell editing (double-click or Enter). Commit is handled by `DataGrid.processRowUpdate`. Row-grouping group rows and auto-generated tree ancestors are never editable; tree-data parent rows are. |
315
372
 
316
373
  #### Spanning
317
374
 
318
375
  | Property | Type | Description |
319
376
  | :--- | :--- | :--- |
320
- | `colSpan` | `number \| ((params: GridRenderCellParams) => number)` | Number of columns this cell merges horizontally. |
321
- | `rowSpan` | `number \| ((params: GridRenderCellParams) => number)` | Number of rows this cell merges vertically. |
377
+ | `colSpan` | `number \| ((params: GridRenderCellParams) => number)` | Number of columns this cell merges horizontally: the origin plus the next visible columns in render order, clamped to the end of its pinned section. `params.value` is the `valueGetter` result. |
378
+ | `rowSpan` | `number \| ((params: GridRenderCellParams) => number)` | Number of rows this cell merges vertically, clamped to the end of its row section (top-pinned, scrolling or bottom-pinned rows) and to the first row with an expanded detail panel. |
379
+
380
+ Span values are floored; `Infinity` means "to the end"; `NaN`, `0` and negative values mean no span. A span function that throws is treated as `1` (with a development warning). With both set, the origin covers the whole `colSpan × rowSpan` rectangle. See [Cell Spanning](features/cell-spanning.md).
322
381
 
323
382
  #### Styling
324
383
 
@@ -332,16 +391,16 @@ Defines the behavior and appearance of a single column.
332
391
  | Property | Type | Default | Description |
333
392
  | :--- | :--- | :--- | :--- |
334
393
  | `sortable` | `boolean` | `true` | Enable/disable sorting for this column. |
335
- | `filterable` | `boolean` | `true` | Enable/disable filtering for this column. |
394
+ | `filterable` | `boolean` | `true` | Enable/disable filtering for this column. `false` also removes it from the quick-filter search. |
336
395
  | `resizable` | `boolean` | `true` | Allow the user to drag-resize this column. |
337
- | `hideable` | `boolean` | `true` | Allow the user to hide this column via the panel. |
338
- | `pinnable` | `boolean` | `true` | Allow this column to be pinned via the UI. |
396
+ | `hideable` | `boolean` | `true` | Allow the user to hide this column from the UI. `false` removes it from the Columns panel (unless `showNonHideableColumns`) and removes **Hide Column** from its column menu. |
397
+ | `pinnable` | `boolean` | `true` | Allow this column to be pinned via the UI. `false` removes the pin actions from its column menu. Does not affect drag-reordering (v3.0+). |
339
398
  | `disableColumnMenu` | `boolean` | `false` | Hide the column header kebab/context menu. |
340
- | `exportable` | `boolean` | `true` | Set to `false` to exclude from CSV, Excel, JSON, and Print exports. |
341
- | `groupable` | `boolean` | `true` | Allow this column to be used as a row grouping dimension. Set to `false` to prevent this field from being grouped, even when it appears in `rowGroupingModel`. |
342
- | `groupingValueFormatter` | `(params: { field: string; value: unknown }) => string` | — | Custom formatter for group-header labels when this column is the active grouping field. Falls back to `"field: value"` when omitted. |
343
- | `aggregable` | `boolean` | `true` | Allow this column to be aggregated. |
344
- | `availableAggregationFunctions` | `string[]` | all built-ins | Restrict which aggregation functions are computed and available for this column (e.g. `['sum', 'avg']`). Functions outside this list are skipped even if set in `aggregationModel`. |
399
+ | `exportable` | `boolean` | `true` | Set to `false` to exclude from CSV, Excel, JSON, PDF and Print exports (including their subtotals and totals). |
400
+ | `groupable` | `boolean` | `true` | Allow this column to be used as a row grouping dimension. Set to `false` to prevent this field from being grouped, even when it appears in `rowGroupingModel`, or used as a pivot row or column field (the pivot panel does not offer it). |
401
+ | `groupingValueFormatter` | `(params: { field: string; value: unknown }) => string` | — | Custom formatter for group-header labels when this column is the active grouping field. Falls back to `"field: value"` when omitted or when it throws (v3.0+). |
402
+ | `aggregable` | `boolean` | — | Whether this column can be aggregated. Unset, the toolbar's Summaries panel and the pivot panel's value fields offer it only for `type: 'number'` columns; `true` offers it for any type. `false` keeps it out of both panels, and an `aggregationModel` or `pivotModel` entry naming it is ignored (no footer total, group aggregate, pivot value or export total). |
403
+ | `availableAggregationFunctions` | `string[]` | all built-ins | Restrict which aggregation functions are computed and available for this column (e.g. `['sum', 'avg']`). Functions outside this list are skipped even if set in `aggregationModel` or a pivot value field, the toolbar's Summaries panel and the pivot panel offer only these. |
345
404
 
346
405
  ---
347
406
 
@@ -349,19 +408,35 @@ Defines the behavior and appearance of a single column.
349
408
 
350
409
  | Function | Return | Description |
351
410
  | :--- | :--- | :--- |
352
- | `exportToCsv(rows, cols, options?)` | `void` | Triggers download of CSV file. |
353
- | `exportToJson(rows, cols, options?)` | `void` | Triggers download of JSON file. |
354
- | `exportToExcel(rows, cols, options?)` | `void` | Basic `.xls` export (zero-dep). |
411
+ | `exportToCsv(rows, cols, options?)` | `void` | Triggers download of a UTF-8 CSV file (with a byte-order mark and formula escaping by default, v3.0+). |
412
+ | `exportToJson(rows, cols, options?)` | `void` | Triggers download of JSON file. Values, including aggregation values, are raw. |
413
+ | `exportToExcel(rows, cols, options?)` | `void` | Basic `.xls` export (zero-dep HTML table). A `.xlsx` file name is renamed to `.xls` (v3.0+). |
355
414
  | `exportToExcelAdvanced(rows, cols, options?)` | `Promise<void>` | Real `.xlsx` export (styled, multi-sheet, lazy-loaded). Accepts `groupedRows` for outlined grouped reports (v2.1+). |
356
- | `printGrid(rows, cols, title \| options)` | `void` | Opens browser print dialog. |
415
+ | `exportToPdf(rows, cols, options?)` | `Promise<void>` | PDF report via the optional `jspdf` + `jspdf-autotable` peers. `font` registers a Unicode font (v3.0+). See [PDF Export](features/pdf-export.md). |
416
+ | `printGrid(rows, cols, titleOrOptions?)` | `Promise<void>` | Opens the browser print dialog. The third argument is a title string or `PrintOptions`. |
417
+
418
+ In every exporter, a non-empty `selectedRows` takes precedence over `groupedRows` (the selected rows are exported flat) and the totals are recomputed over the selected rows (v3.0+). See the [Export Guide](features/export-guide.md#selection-grouping-and-totals).
419
+
420
+ Every exporter takes `columns` typed for your rows (`GridColDef<R>[]`) or untyped (`GridColDef[]`), and its options also accept `getRowId?: (row: R) => GridRowId`. Pass the grid's `getRowId` when you use one: `selectedRows` holds those ids, and since v3.0 the grid no longer copies them onto `row.id`.
357
421
 
358
422
  ---
359
423
 
360
424
  ## 🧩 Standalone Components
361
425
 
362
- | Component | Description |
363
- | :--- | :--- |
364
- | `ColumnVisibilityPanel` | Controlled column show/hide (and optional reorder) list, for use outside the grid (v2.1+). Props: `ColumnVisibilityPanelProps`. See [Column Visibility](components/column-visibility.md). |
426
+ The grid's building blocks are exported with their props types (the props types since v3.0). `DataGrid` renders all of them itself; import them to build your own layout or slot.
427
+
428
+ | Component | Props type | Description |
429
+ | :--- | :--- | :--- |
430
+ | `ColumnVisibilityPanel` | `ColumnVisibilityPanelProps` | Controlled column show/hide (and optional reorder) list, for use outside the grid (v2.1+). See [Column Visibility](components/column-visibility.md). |
431
+ | `GridToolbar` | `GridToolbarProps` | The built-in toolbar. See [`GridToolbar`](#️-gridtoolbar). |
432
+ | `Pagination` | `PaginationProps` | The built-in pagination bar (v3.0+ export), e.g. to wrap in a `pagination` slot. See [Pagination](components/pagination.md). |
433
+ | `FilterPanel` | `FilterPanelProps` | The filter rule editor (`filterModel`, `columns`, `onFilterModelChange`). See [Filter Panel](components/filter-panel.md). |
434
+ | `GridTooltip` | `GridTooltipProps` | Tooltip wrapper. See [Tooltip](components/tooltip.md). |
435
+ | `Header` | `HeaderProps` | The column header rows. See [Header](components/header.md). |
436
+ | `Row` | `RowProps` | One body row. See [Row](components/row.md). |
437
+ | `Cell` | `CellProps` | One body cell. See [Cell](components/cell.md). |
438
+ | `Skeleton` | `SkeletonProps` | Loading placeholder (`rows`, `columns`, both default 5). |
439
+ | `Button`, `Input`, `Checkbox` | `ButtonProps`, `InputProps`, `CheckboxProps` | The grid's form controls. `Button` defaults to `type="button"`. |
365
440
 
366
441
  ---
367
442
 
@@ -371,9 +446,14 @@ Defines the behavior and appearance of a single column.
371
446
  Context provider for overriding the grid's visual system.
372
447
 
373
448
  #### `GridTheme` Object
374
- - **Colors**: `primary`, `secondary`, `header`, `border`, `rowHover`, `rowSelected`.
375
- - **Typography**: `fontFamily`, `fontSize`, `fontWeight`.
376
- - **Spacing**: `cellPadding`, `headerHeight`, `rowHeight`.
449
+ - **`mode`**: `'light'` (default) or `'dark'`: the complete base palette the provider pins before applying the other keys (v3.0+).
450
+ - **`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.
451
+ - **`typography`**: `fontFamily`, `fontFamilyMono`, `fontSizeXs` … `fontSizeXl`.
452
+ - **`spacing`**: `xs` … `xxl`. **`borders`**: `widthThin/Medium/Thick`, `radiusSm` … `radiusXl`, `color`, `colorHover`. **`shadows`**: `sm` … `xl`.
453
+ - **`grid`**: surfaces (`background`, `borderColor`, `headerBackground`, `headerText`, `headerHoverBackground`, `headerSortedBackground`, `rowText`, `rowHoverBackground`, `rowAlternateBackground`, `rowSelectedBackground`, `rowSelectedHoverBackground`, `cellFocusBorder`, `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`.
454
+ - **`toolbar`**, **`overlays`**, **`scrollbar`** (`thumbColor`, `trackColor`, `size`), **`skeleton`** (`baseColor`, `highlightColor`), **`transitions`**.
455
+
456
+ Presets: `darkTheme`, `roseTheme`, `emeraldTheme`, `amberTheme`, `compactTheme`. See [Theming](customization/theming.md).
377
457
 
378
458
  ---
379
459
 
@@ -383,6 +463,8 @@ Context provider for overriding the grid's visual system.
383
463
 
384
464
  Creates a typed ref to pass to the `apiRef` prop. Gives you imperative access to the grid after mount.
385
465
 
466
+ `apiRef.current` is never `null`: until the grid mounts it holds an API whose methods do nothing and whose getters return empty values. The grid installs the live API in a layout effect, so it is ready in your own `useLayoutEffect` / `useEffect` and in event handlers (v3.0+).
467
+
386
468
  ```tsx
387
469
  import { useGridApiRef } from '@opencorestack/opengridx';
388
470
 
@@ -394,6 +476,10 @@ const apiRef = useGridApiRef();
394
476
  // Then call methods imperiously
395
477
  apiRef.current.scrollToIndexes({ rowIndex: 0 });
396
478
  apiRef.current.setFilterModel({ items: [] });
479
+
480
+ // Typed row getters (v3.0.1+)
481
+ const invoiceApi = useGridApiRef<Invoice>();
482
+ const invoice = invoiceApi.current.getRow(42); // Invoice | null
397
483
  ```
398
484
 
399
485
  See the [Imperative API (`GridApi`)](#%EF%B8%8F-imperative-api-gridapi) table above for the full method list.
@@ -418,10 +504,11 @@ const { aggregationResult, isLoading } = useAggregation({
418
504
 
419
505
  | Param | Type | Description |
420
506
  | :--- | :--- | :--- |
421
- | `rows` | `GridRowModel[]` | The rows to aggregate. |
507
+ | `rows` | `R[]` | The rows to aggregate (already filtered: the hook aggregates every row it is given). Any object row type (v3.0+). |
422
508
  | `aggregationModel` | `GridAggregationModel` | Map of `field → aggFn` (e.g. `{ salary: 'sum' }`). |
423
- | `isServerSide` | `boolean` | If `true`, skips client computation and uses `serverAggregationResults`. |
424
- | `filterModel` | `GridFilterModel` | Optional — restricts aggregation to filtered rows. |
509
+ | `isServerSide` | `boolean` | If `true`, skips client computation and uses `serverAggregationResults`, else the result of `dataSource.getAggregations`. The grid sets it whenever a `dataSource` drives the rows (server or infinite pagination, server sorting or server filtering). |
510
+ | `columns` | `GridColDef[]` | Optional — column definitions, so values are read through each column's `valueGetter` and `availableAggregationFunctions` is honoured. |
511
+ | `filterModel` | `GridFilterModel` | Optional — sent to `dataSource.getAggregations`. It does not filter `rows`. |
425
512
  | `sortModel` | `GridSortItem[]` | Optional — used when `dataSource` is provided. |
426
513
  | `dataSource` | `GridDataSource` | Optional — server-side data adapter for async aggregation. |
427
514
  | `serverAggregationResults` | `GridAggregationResult \| null` | Pre-fetched results when `isServerSide: true`. |
@@ -431,8 +518,8 @@ const { aggregationResult, isLoading } = useAggregation({
431
518
  | Field | Type | Description |
432
519
  | :--- | :--- | :--- |
433
520
  | `aggregationResult` | `GridAggregationResult` | Map of `field → computed value`. |
434
- | `isLoading` | `boolean` | `true` while a server-side fetch is in progress. |
435
- | `error` | `unknown` | Set if the server-side fetch threw. |
521
+ | `isLoading` | `boolean` | `true` while the `getAggregations` request for the current model, filter and sort is pending. |
522
+ | `error` | `unknown` | Set if the request for the current model, filter and sort failed. |
436
523
 
437
524
  **Built-in aggregation functions:** `sum`, `avg`, `min`, `max`, `count`, `unique`
438
525
 
@@ -442,7 +529,7 @@ You can also use `formatAggregationValue(value, fnName)` to produce a display st
442
529
 
443
530
  ### `usePivot(rawRows, rawCols, model, enabled)`
444
531
 
445
- Headless hook that transforms a flat dataset into pivot rows and pivot column definitions. Pass the output directly to `<DataGrid rows={} columns={} />` when `pivotMode` is active.
532
+ Headless hook that transforms a flat dataset into pivot rows and pivot column definitions, with the same engine `<DataGrid pivotMode />` uses. Use it to pivot outside the grid (charts, custom exports). To show a pivot in the grid, prefer `pivotMode`: it also filters the source rows, keeps the Grand Total row last when sorting and leaves it out of select-all, which a grid given `pivotRows` as plain rows does not do.
446
533
 
447
534
  ```tsx
448
535
  import { usePivot } from '@opencorestack/opengridx';
@@ -468,7 +555,7 @@ const { pivotRows, pivotColumns, isValid } = usePivot(
468
555
 
469
556
  | Param | Type | Description |
470
557
  | :--- | :--- | :--- |
471
- | `rawRows` | `GridRowModel[]` | Original flat dataset. |
558
+ | `rawRows` | `R[]` | Original flat dataset. Any object row type (v3.0+); `rawCols` may be `GridColDef<R>[]` or `GridColDef[]`. |
472
559
  | `rawCols` | `GridColDef[]` | Original column definitions. |
473
560
  | `model` | `GridPivotModel` | Pivot configuration — `rowFields`, `columnFields`, `valueFields`. |
474
561
  | `enabled` | `boolean` | When `false`, returns empty arrays immediately (no computation). |
@@ -477,10 +564,10 @@ const { pivotRows, pivotColumns, isValid } = usePivot(
477
564
 
478
565
  | Field | Type | Description |
479
566
  | :--- | :--- | :--- |
480
- | `pivotRows` | `GridRowModel[]` | Transformed rows ready for the grid. |
481
- | `pivotColumns` | `GridColDef[]` | Generated column definitions for each pivot key. |
482
- | `colKeys` | `string[]` | The distinct column pivot values used. |
483
- | `isValid` | `boolean` | `false` if the model is incomplete (e.g. no `rowFields` or `valueFields`). |
567
+ | `pivotRows` | `GridRowModel[]` | One row per row-field combination (ids `'__pivot_row__:["value", …]'`, from its row-field values), then the Grand Total row (id `'__pivot_grand_total__'`). There is no Grand Total row when `rawRows` is empty. |
568
+ | `pivotColumns` | `GridColDef[]` | Row-label columns (keeping the source column's formatter, renderer, type and alignment; `hideable: false`), then one value column per column key and value field. Value columns format aggregates like the footer. |
569
+ | `colKeys` | `string[]` | The distinct column-field value combinations, ordered by value (numbers numerically, strings naturally, blanks last). `['']` when there are no column fields. |
570
+ | `isValid` | `boolean` | `false` if the model has no usable row field or value field (fields on `groupable: false` columns and value fields whose function `availableAggregationFunctions` does not allow are skipped). |
484
571
 
485
572
  #### `GridPivotModel`
486
573
 
@@ -542,7 +629,9 @@ const { initialState, onStateChange, clearState } = useGridStateStorage({
542
629
  | :--- | :--- | :--- |
543
630
  | `initialState` | `GridState \| undefined` | Restored state from storage — pass to `DataGrid.initialState`. |
544
631
  | `onStateChange` | `(state: GridState) => void` | Callback to pass to `DataGrid.onStateChange`. |
545
- | `clearState` | `() => void` | Removes the saved state from storage. |
632
+ | `clearState` | `() => void` | Removes the saved state from storage and cancels a pending write. |
633
+
634
+ If the key can change while the grid stays mounted, remount the grid with it (`<DataGrid key={storageKey} … />`): the grid reads `initialState` only on mount. When the browser blocks storage, the hook falls back to no persistence.
546
635
 
547
636
  ---
548
637
 
@@ -578,13 +667,19 @@ interface GridPaginationModel {
578
667
  interface GridPivotModel {
579
668
  rowFields: string[];
580
669
  columnFields: string[];
581
- valueFields: { field: string; aggFn: string; headerName?: string }[];
670
+ valueFields: GridPivotValueField[];
671
+ }
672
+
673
+ interface GridPivotValueField {
674
+ field: string;
675
+ aggFn: GridPivotAggFn; // 'sum' | 'avg' | 'count' | 'min' | 'max'
676
+ headerName?: string;
582
677
  }
583
678
  ```
584
679
 
585
680
  ### `GridLocaleText`
586
681
 
587
- Override user-visible strings in the Pagination component. All fields are optional.
682
+ Override user-visible strings in the Pagination component and the empty state. All fields are optional.
588
683
 
589
684
  ```typescript
590
685
  interface GridLocaleText {
@@ -606,26 +701,53 @@ interface GridRowMeta {
606
701
  groupingField?: string;
607
702
  groupingValue?: unknown;
608
703
  groupLabel?: string;
609
- descendantCount?: number;
704
+ descendantCount?: number; // data rows below, at any depth, that pass the filter
610
705
  isExpanded?: boolean;
611
- isGroupRow?: boolean;
706
+ isGroupRow?: boolean; // synthetic row: group, subtotal or auto-created tree parent
707
+ isGroupFooter?: boolean; // v3.0: the subtotal row of a group ('footer' aggregation position)
612
708
  }
613
709
  ```
614
710
 
711
+ ### `GridGroupedExportRow`
712
+
713
+ One entry of the list returned by `apiRef.current.getGroupedExportRows()`, and the element type of every exporter's `groupedRows` option. Traverse in array order to reproduce the grouping.
714
+
715
+ ```typescript
716
+ interface GridGroupedExportRow {
717
+ type: 'group-header' | 'leaf' | 'group-subtotal' | 'grand-total';
718
+ depth: number; // 0 = top-level group
719
+ groupField?: string; // absent on 'leaf' and 'grand-total'
720
+ groupValue?: unknown; // absent on 'leaf' and 'grand-total'
721
+ groupLabel?: string; // v3.0: label as the grid shows it ('group-header', 'group-subtotal')
722
+ aggregatedValues?: Record<string, unknown>; // 'group-subtotal' and 'grand-total'
723
+ row?: GridRowModel; // 'leaf' only: your row object
724
+ }
725
+ ```
726
+
727
+ ### `GridDetailPanelHeight`
728
+
729
+ ```typescript
730
+ type GridDetailPanelHeight = number | 'auto';
731
+ ```
732
+
733
+ Returned by `getDetailPanelHeight`.
734
+
615
735
  ---
616
736
 
617
737
  ## 📡 Event Callback Types
618
738
 
619
739
  These interfaces describe the parameter objects passed to every event callback prop.
620
740
 
741
+ Keyboard activation uses the same callbacks: **Enter** on a cell that is not editable runs the row-click handling (`onRowClick`, click-to-select, group expansion). See [Keyboard & Accessibility](features/keyboard-navigation.md) for every key and the ARIA structure.
742
+
621
743
  ### `GridRowParams<R>`
622
- Passed to `onRowClick`.
744
+ Passed to `onRowClick` and `onRowDoubleClick`.
623
745
 
624
746
  | Property | Type | Description |
625
747
  | :--- | :--- | :--- |
626
748
  | `row` | `R` | The full row data object. |
627
749
  | `id` | `GridRowId` | Unique identifier of the row. |
628
- | `rowIndex` | `number` | Zero-based index of the row in the visible dataset. |
750
+ | `rowIndex` | `number` | Zero-based index of the row among the rendered rows: top-pinned, then the current page, then bottom-pinned (the row's `data-rowindex`). |
629
751
 
630
752
  ### `GridCellParams<R>`
631
753
  Passed to `onCellClick` and `isCellEditable`.
@@ -636,8 +758,26 @@ Passed to `onCellClick` and `isCellEditable`.
636
758
  | `field` | `string` | The column field name. |
637
759
  | `value` | `unknown` | The cell value (after `valueGetter`, before `valueFormatter`). |
638
760
  | `colDef` | `GridColDef<R>` | The column definition. |
639
- | `rowIndex` | `number` | Zero-based row index in the visible dataset. |
640
- | `colIndex` | `number` | Zero-based column index. |
761
+ | `rowIndex` | `number` | Zero-based index of the row among the rendered rows: top-pinned, then the current page, then bottom-pinned. |
762
+ | `colIndex` | `number` | Zero-based position of the column among the visible data columns in render order (left-pinned, unpinned, right-pinned); system columns are not counted. It does not change with horizontal scrolling (v3.0). |
763
+
764
+ ### `GridRenderEditCellParams<R>`
765
+ Passed to `renderEditCell` (v3.0+). Everything in `GridRenderCellParams` (`value` is the pending, uncommitted value), plus:
766
+
767
+ | Property | Type | Description |
768
+ | :--- | :--- | :--- |
769
+ | `onValueChange` | `(value: unknown) => void` | Updates the pending value. Nothing is saved until `onCommit`. |
770
+ | `onCommit` | `() => void` | Commits the pending value through `processRowUpdate` and leaves edit mode. |
771
+ | `onCancel` | `() => void` | Discards the pending value and leaves edit mode. |
772
+
773
+ ### `GridValueSetterParams<R>`
774
+ Passed to `valueSetter` (v3.0+).
775
+
776
+ | Property | Type | Description |
777
+ | :--- | :--- | :--- |
778
+ | `value` | `unknown` | The committed value from the editor. |
779
+ | `row` | `R` | The row as it was before the edit. |
780
+ | `field` | `string` | The column field name. |
641
781
 
642
782
  ### `GridColumnOrderChangeParams`
643
783
  Passed to `onColumnOrderChange`.
@@ -645,25 +785,27 @@ Passed to `onColumnOrderChange`.
645
785
  | Property | Type | Description |
646
786
  | :--- | :--- | :--- |
647
787
  | `column` | `GridColDef` | The column definition that was moved. |
648
- | `oldIndex` | `number` | Previous column index. |
649
- | `targetIndex` | `number` | New column index after the move. |
788
+ | `oldIndex` | `number` | The column's position before the move, in the grid's full column order: every current column (hidden ones and the row-grouping `__group__` column included), in its current order. |
789
+ | `targetIndex` | `number` | Its position after the move, in the same order. |
790
+
791
+ Splicing the full order with these indices gives the new order. A controlled `columnOrder` that lists only some columns, or no `__group__`, is not that order: use `onColumnOrderModelChange`, which hands you the whole new order.
650
792
 
651
793
  ### `GridRowOrderChangeParams<R>`
652
794
  Passed to `onRowOrderChange`.
653
795
 
654
796
  | Property | Type | Description |
655
797
  | :--- | :--- | :--- |
656
- | `row` | `R` | The row data object that was moved. |
657
- | `oldIndex` | `number` | Previous row index. |
658
- | `targetIndex` | `number` | New row index after the move. |
798
+ | `row` | `R` | The row that was dragged: your own object from `rows`. |
799
+ | `oldIndex` | `number` | Its position in your `rows` prop (`rows[oldIndex] === row`), whatever the sort, filter, page or pinned rows on screen. |
800
+ | `targetIndex` | `number` | The position in `rows` of the row it was dropped on. Remove the row at `oldIndex` and insert it at `targetIndex`. |
659
801
 
660
802
  ### `GridRowScrollEndParams`
661
803
  Passed to `onRowsScrollEnd`.
662
804
 
663
805
  | Property | Type | Description |
664
806
  | :--- | :--- | :--- |
665
- | `visibleTop` | `number` | Pixel offset of the topmost visible row. |
666
- | `visibleBottom` | `number` | Pixel offset of the bottommost visible row. |
807
+ | `visibleTop` | `number` | Scroll offset (`scrollTop`) of the viewport's top edge, in pixels. |
808
+ | `visibleBottom` | `number` | Offset of the viewport's bottom edge (`visibleTop + viewportHeight`). |
667
809
  | `viewportHeight` | `number` | Current height of the scroll viewport in pixels. |
668
810
 
669
811
  ### `GridDetailPanelParams<R>`
@@ -673,7 +815,7 @@ Passed to `getDetailPanelContent` and `getDetailPanelHeight`.
673
815
  | :--- | :--- | :--- |
674
816
  | `row` | `R` | The full row data object. |
675
817
  | `id` | `GridRowId` | Unique identifier of the row. |
676
- | `rowIndex` | `number` | Zero-based index of the row in the visible dataset. |
818
+ | `rowIndex` | `number` | Zero-based index of the row among the rendered rows: top-pinned, then the current page, then bottom-pinned (the row's `data-rowindex`). |
677
819
 
678
820
  ---
679
821
 
@@ -693,8 +835,8 @@ The object passed to `getRows` by the grid on every data fetch.
693
835
 
694
836
  | Property | Type | Description |
695
837
  | :--- | :--- | :--- |
696
- | `startRow` | `number` | Zero-based index of the first row to fetch (based on `page * pageSize`). |
697
- | `endRow` | `number` | Zero-based index of the last row to fetch (exclusive). |
838
+ | `startRow` | `number` | Zero-based index of the first row to fetch. Server pagination: `page * pageSize`. Infinite scroll: the number of rows already loaded. Client pagination and tree children: `0`. |
839
+ | `endRow` | `number` | Zero-based index just past the last row to fetch (exclusive), so `rows.slice(startRow, endRow)` is the requested range. Server pagination: `startRow + pageSize`. Infinite scroll: `(page + 1) * pageSize`. When the grid wants every row (client pagination, the children of a tree node) it is `Number.MAX_SAFE_INTEGER`. |
698
840
  | `sortModel` | `GridSortItem[]` | Active sort configuration. Empty array when no sort is applied. |
699
841
  | `filterModel` | `GridFilterModel` | Active filter state. Has empty `items` when no filters are applied. |
700
842
  | `groupKeys` | `string[]` | Path of grouping key values for the current group level (used in lazy-loaded tree/grouping). Empty array for the root level. |
@@ -721,7 +863,7 @@ import {
721
863
  GridColDef,
722
864
  } from '@opencorestack/opengridx';
723
865
 
724
- interface Employee extends GridRowModel {
866
+ interface Employee {
725
867
  id: number;
726
868
  name: string;
727
869
  department: string;
@@ -773,7 +915,15 @@ export default function EmployeeGrid() {
773
915
  }
774
916
  ```
775
917
 
776
- **How the grid decides when to call `getRows`:** The hook fires (with a 300 ms debounce) whenever `paginationMode`, `sortingMode`, or `filterMode` is set to `'server'` AND a `dataSource` is provided. Any change to `sortModel`, `filterModel`, or `paginationModel` re-triggers the fetch. Stale responses from superseded requests are silently discarded — only the latest request updates the grid.
918
+ **How the grid decides when to call `getRows`:** whenever a `dataSource` is provided, with a 300 ms debounce that collapses rapid changes into one request. The grid shows its loading state from the moment a request is scheduled, so the empty state never flashes up first.
919
+
920
+ | `paginationMode` | Request | Refetches when |
921
+ | :--- | :--- | :--- |
922
+ | `'client'` (default) | Every row once (`startRow: 0`, `endRow: Number.MAX_SAFE_INTEGER`). The grid pages, and sorts/filters unless those modes are `'server'`. | A `'server'` sort or filter changes, or `getRows` changes. Page changes never refetch. |
923
+ | `'server'` | One page. The response replaces the rows. | The page, page size, sort, filter or aggregation model changes, or `getRows` changes. |
924
+ | `'infinite'` | The rows from the end of what is loaded up to the current page. Responses are appended; rows whose id is already loaded are skipped. | The page grows. A sort, filter, page-size or `getRows` change restarts the list at row 0 and reports page 0 through `onPaginationModelChange`; a smaller page reloads the list up to that page. |
925
+
926
+ Models are compared by content, so inline `sortModel`/`filterModel`/`aggregationModel` objects and a `dataSource` object recreated with the same `getRows` function do not refetch. A response for parameters that have changed since (another page, sort, filter or `dataSource`, or a removed `dataSource`) is discarded. Fetched rows are keyed with `getRowId`. When `getRows` rejects, the error overlay shows the rejection's `message` (an `Error`, a string, or any object with a string `message`) and a **Retry** button that requests the rows again.
777
927
 
778
928
  ---
779
929
 
@@ -844,7 +994,7 @@ interface GridFilterModel {
844
994
  | :--- | :--- | :--- | :--- |
845
995
  | `items` | `(GridFilterItem \| GridFilterGroup)[]` | `[]` | Root-level filter conditions or nested groups. |
846
996
  | `logicOperator` | `'and' \| 'or'` | `'and'` | How to combine the root `items`. |
847
- | `quickFilterValues` | `string[]` | `[]` | Each string must match at least one field in the row (all strings must match). Applied before `items`. |
997
+ | `quickFilterValues` | `string[]` | `[]` | Search terms. Every term must be found (case-insensitive substring) in at least one visible, filterable column, read through `valueGetter` and, if set, `valueFormatter`. The row `id` and non-column fields are not searched. Blank terms are ignored. Applied together with `items`. The toolbar search box splits the typed text on whitespace. |
848
998
 
849
999
  ### `GridFilterItem`
850
1000
 
@@ -855,7 +1005,7 @@ A single column filter condition.
855
1005
  | `id` | `string \| number` | No | Optional stable identifier for this item (useful for controlled updates). |
856
1006
  | `field` | `string` | Yes | The column field to filter on. |
857
1007
  | `operator` | `GridFilterOperator` | Yes | The comparison operator to apply. |
858
- | `value` | `unknown` | No | The value to compare against. Not required for `isEmpty` / `isNotEmpty`. |
1008
+ | `value` | `unknown` | No | The value to compare against. Not required for `isEmpty` / `isNotEmpty`. For every other operator, an empty value (`undefined`, `null`, a blank string or an empty array) means the item does not filter. |
859
1009
 
860
1010
  ### `GridFilterGroup`
861
1011
 
@@ -873,13 +1023,25 @@ A nested group of filter conditions combined with a logical operator. Groups can
873
1023
  type GridFilterOperator =
874
1024
  | 'contains' | 'equals' | 'startsWith' | 'endsWith'
875
1025
  | 'isEmpty' | 'isNotEmpty' | 'isAnyOf'
876
- | '>' | '>=' | '<' | '<=' | '!='
877
- | 'is' | 'not';
1026
+ | '=' | '>' | '>=' | '<' | '<=' | '!='
1027
+ | 'is' | 'not'
1028
+ | 'after' | 'onOrAfter' | 'before' | 'onOrBefore';
878
1029
  ```
879
1030
 
1031
+ | Operator | Matches when the cell… |
1032
+ | :--- | :--- |
1033
+ | `contains` / `startsWith` / `endsWith` / `equals` | contains / starts with / ends with / equals the value (case-insensitive text). |
1034
+ | `=` `!=` `>` `>=` `<` `<=` | compares numerically with the value (numeric strings accepted). A blank cell never matches, except for `!=`, which it always matches. |
1035
+ | `is` / `not` | equals / does not equal the value (case-insensitive). On `type: 'date'` columns and `Date` cells: the same / a different local calendar day. |
1036
+ | `after` / `onOrAfter` / `before` / `onOrBefore` | is a later / same-or-later / earlier / same-or-earlier local calendar day. |
1037
+ | `isAnyOf` | equals any value in the array (a single value counts as a one-element array). |
1038
+ | `isEmpty` / `isNotEmpty` | is / is not `null`, `undefined` or blank. No value needed. |
1039
+
1040
+ Dates can be `Date` objects, epoch milliseconds or date strings; a `'YYYY-MM-DD'` string is read as a local date. Cells are read through the column's `valueGetter`.
1041
+
880
1042
  ### Default Operators per Column Type
881
1043
 
882
- The filter panel shows only the operators relevant to each column's `type`. These are also the operators used when the grid applies client-side filtering.
1044
+ The filter panel shows only the operators relevant to each column's `type`; an operator set programmatically that the type does not list is still shown and applied. Every operator works in a programmatic `filterModel` on any column.
883
1045
 
884
1046
  | Column `type` | Default operator | Available operators |
885
1047
  | :--- | :--- | :--- |
@@ -918,10 +1080,12 @@ const filterModel: GridFilterModel = {
918
1080
 
919
1081
  ## 💾 `GridInitialState` and `GridState`
920
1082
 
921
- `GridInitialState` is a type alias for `GridState`. Pass it to `initialState` to restore persisted grid state on mount (e.g. from `localStorage`). Each key corresponds to a feature slice — all are optional.
1083
+ `GridInitialState` is `GridState` with every column field optional as well. Pass it to `initialState` to restore persisted grid state on mount (e.g. from `localStorage`). Each key corresponds to a feature slice — all are optional. A `GridState` from `onStateChange` is a valid `GridInitialState`.
922
1084
 
923
1085
  ```typescript
924
- type GridInitialState = GridState;
1086
+ interface GridInitialState extends Omit<GridState, 'columns'> {
1087
+ columns?: Partial<GridColumnsState>; // e.g. { columnVisibilityModel: { age: false } }
1088
+ }
925
1089
 
926
1090
  interface GridState {
927
1091
  sorting?: GridSortingState;
@@ -1008,16 +1172,18 @@ type GridAggregationResult = Record<string, unknown>;
1008
1172
 
1009
1173
  | Function | Input | Output | Description |
1010
1174
  | :--- | :--- | :--- | :--- |
1011
- | `sum` | numeric values | `number` | Sum of all non-null numeric values in the group. |
1012
- | `avg` | numeric values | `number \| null` | Arithmetic mean. Returns `null` if there are no numeric values. |
1013
- | `min` | numeric values | `number \| null` | Smallest numeric value. Returns `null` if there are no numeric values. |
1014
- | `max` | numeric values | `number \| null` | Largest numeric value. Returns `null` if there are no numeric values. |
1015
- | `count` | any values | `number` | Count of non-null values. |
1016
- | `unique` | any values | `number` | Count of distinct non-null values (uses a `Set`). |
1175
+ | `sum` | numeric values | `number` | Sum of the numeric values (numbers, numeric strings, `Date`s as timestamps). Blank cells, booleans and arrays are skipped. `0` when there are none. |
1176
+ | `avg` | numeric values | `number \| null` | Arithmetic mean of the same values. Returns `null` if there are none. |
1177
+ | `min` | numeric values | `number \| Date \| null` | Smallest value; a `Date` when the smallest value is a `Date`. Returns `null` if there are none. |
1178
+ | `max` | numeric values | `number \| Date \| null` | Largest value; a `Date` when the largest value is a `Date`. Returns `null` if there are none. |
1179
+ | `count` | any values | `number` | Count of values that are not `null`, `undefined` or a blank string. |
1180
+ | `unique` | any values | `number` | Count of distinct values, blanks excluded (uses a `Set`). |
1181
+
1182
+ Cells are read through the column's `valueGetter`.
1017
1183
 
1018
1184
  All built-in functions are exported as the `BuiltInAggFn` type: `'sum' | 'avg' | 'count' | 'min' | 'max' | 'unique'`.
1019
1185
 
1020
- Use `formatAggregationValue(value, fnName)` to turn a raw result into a display string. `avg` is formatted to 2 decimal places; all other numbers use `toLocaleString()`.
1186
+ Use `formatAggregationValue(value, fnName)` to turn a raw result into a display string: `'—'` for `null` / `undefined`, `avg` to at most 2 decimal places, other numbers with `toLocaleString()`, anything else with `String(value)`.
1021
1187
 
1022
1188
  ### `getAggregationPosition` Callback
1023
1189
 
@@ -1025,13 +1191,17 @@ Use `formatAggregationValue(value, fnName)` to turn a raw result into a display
1025
1191
  getAggregationPosition?: (groupNode: GridTreeNode | null) => 'inline' | 'footer' | null
1026
1192
  ```
1027
1193
 
1028
- Called once per group node (and once with `null` for the grand-total / root level) to decide where the aggregated row appears:
1194
+ The return type is exported as `GridAggregationPosition` (`'inline' | 'footer' | null`, v3.0+).
1029
1195
 
1030
- | Return value | Effect |
1031
- | :--- | :--- |
1032
- | `'inline'` | Aggregation values appear inside the group header row itself. |
1033
- | `'footer'` | A separate aggregation row is rendered below the group's last row. |
1034
- | `null` | Aggregation result is hidden for this group. |
1196
+ Called on every render for each group node, with the node's current `isExpanded`, and once with `null` for the grand total. Only the answers matter, so an inline arrow function is fine.
1197
+
1198
+ | Return value | For a group | For the grand total (`null`) |
1199
+ | :--- | :--- | :--- |
1200
+ | `'inline'` | Aggregates on the group row (the default). | The footer row is shown. |
1201
+ | `'footer'` | Aggregates on a subtotal row placed after the group's children while the group is expanded; the group row shows none. While the group is collapsed there is no subtotal row, so the aggregates stay on the group row. | The footer row is shown (the default). |
1202
+ | `null` | No aggregates for this group, on screen or in `getGroupedExportRows()` (no `group-subtotal` entry). | The footer row is hidden, and `getGroupedExportRows()` has no `grand-total` entry. |
1203
+
1204
+ Subtotal rows are synthetic: `params.rowMeta` has `isGroupRow: true` and `isGroupFooter: true` (plus the group's `groupingField`, `groupingValue`, `groupLabel`), they carry the aggregates under each column's field, and they are never selected, edited or given a detail panel. Their row element has the class `ogx__row--group-footer`. To show subtotals below expanded groups only: `getAggregationPosition={(node) => (node === null ? 'footer' : node.isExpanded ? 'footer' : 'inline')}`.
1035
1205
 
1036
1206
  ### Aggregation + Row Grouping Example
1037
1207
 
@@ -1045,7 +1215,7 @@ import {
1045
1215
  GridRowGroupingModel,
1046
1216
  } from '@opencorestack/opengridx';
1047
1217
 
1048
- interface Employee extends GridRowModel {
1218
+ interface Employee {
1049
1219
  id: number;
1050
1220
  name: string;
1051
1221
  department: string;
@@ -1176,7 +1346,15 @@ export default function GroupedHeadersGrid() {
1176
1346
  }
1177
1347
  ```
1178
1348
 
1179
- The grid renders two header rows: the spanning group row on top, and the regular per-column header row below. Groups that are not referenced in `columnGroupingModel` are rendered without a spanning header.
1349
+ The grid renders one group header row per nesting level above the regular per-column header row. Columns that are not referenced in `columnGroupingModel` get an empty filler cell in the group rows.
1350
+
1351
+ Group rows follow the columns as they are rendered:
1352
+
1353
+ - **Order, visibility and pinning** — a group covers its visible member columns in their current order. A group whose members are not adjacent (or that spans a pinned-section boundary) renders one group cell per run of adjacent members. Group cells over pinned columns are sticky, so they stay above their columns on horizontal scroll.
1354
+ - **Widths** — a group cell is exactly as wide as its member columns, including flex, `auto` and percentage widths and manual resizes.
1355
+ - **`headerClassName`** — added to the group cell's class list (next to `ogx-col-group-cell` and `ogx-col-group-cell--group`).
1356
+ - **Column reordering** — header drag and the toolbar / columns-panel reorder keep groups intact: a column can only be moved onto a column of the same innermost group, and an ungrouped column only onto another ungrouped column.
1357
+ - **Accessibility** — group cells are `role="columnheader"` with `aria-colspan` (visible member columns) and `aria-colindex`; filler cells are `aria-hidden`. The grid's `aria-rowcount` includes the group header rows.
1180
1358
 
1181
1359
  ---
1182
1360
 
@@ -1187,7 +1365,7 @@ List view renders the grid as a single-column list of cards, replacing all norma
1187
1365
  ### `GridListViewColDef<R>`
1188
1366
 
1189
1367
  ```typescript
1190
- interface GridListViewColDef<R extends GridRowModel = GridRowModel> {
1368
+ interface GridListViewColDef<R extends GridValidRowModel = GridRowModel> {
1191
1369
  field: string;
1192
1370
  renderCell: (params: GridRenderCellParams<R>) => React.ReactNode;
1193
1371
  }
@@ -1198,9 +1376,9 @@ The `renderCell` function receives the full `GridRenderCellParams` (including `r
1198
1376
  ### Usage Example
1199
1377
 
1200
1378
  ```tsx
1201
- import { DataGrid, GridListViewColDef, GridRenderCellParams } from '@opencorestack/opengridx';
1379
+ import { DataGrid, GridListViewColDef, GridRenderCellParams, GridRowModel } from '@opencorestack/opengridx';
1202
1380
 
1203
- interface Employee extends GridRowModel {
1381
+ interface Employee {
1204
1382
  id: number;
1205
1383
  name: string;
1206
1384
  department: string;
@@ -1237,7 +1415,7 @@ export default function MobileGrid() {
1237
1415
  ### `exportToPdf`
1238
1416
 
1239
1417
  ```ts
1240
- function exportToPdf<R extends GridRowModel>(
1418
+ function exportToPdf<R extends GridValidRowModel>(
1241
1419
  rows: R[],
1242
1420
  columns: GridColDef<R>[],
1243
1421
  options?: PdfExportOptions
@@ -1255,6 +1433,7 @@ interface PdfExportOptions {
1255
1433
  logoUrl?: string;
1256
1434
  orientation?: 'portrait' | 'landscape';
1257
1435
  selectedRows?: (string | number)[];
1436
+ getRowId?: (row: GridRowModel) => GridRowId; // v3.0+
1258
1437
  aggregationResult?: Record<string, unknown> | null;
1259
1438
  aggregationModel?: GridAggregationModel | null;
1260
1439
  filterModel?: GridFilterModel | null;
@@ -1263,6 +1442,7 @@ interface PdfExportOptions {
1263
1442
  headerBackgroundColor?: string;
1264
1443
  headerTextColor?: string;
1265
1444
  fontSize?: number;
1445
+ font?: { name: string; data: string; boldData?: string }; // v3.0+
1266
1446
  }
1267
1447
  ```
1268
1448
 
@@ -1272,12 +1452,14 @@ interface PdfExportOptions {
1272
1452
  | `title` | `string` | — | Adds a branded header block above the table |
1273
1453
  | `logoUrl` | `string` | — | Data URI or URL for a logo image (requires `title`) |
1274
1454
  | `orientation` | `'portrait' \| 'landscape'` | `'landscape'` | Page orientation |
1275
- | `selectedRows` | `(string \| number)[]` | — | Export only rows with these IDs |
1455
+ | `selectedRows` | `(string \| number)[]` | — | Export only rows with these IDs. Takes precedence over `groupedRows`; footer totals are recomputed over the selection (v3.0+) |
1456
+ | `getRowId` | `(row) => GridRowId` | `row.id` | The grid's `getRowId`, to match `selectedRows` against rows that keep their id elsewhere (v3.0+) |
1276
1457
  | `aggregationResult` | `Record<string, unknown>` | — | From `apiRef.current.getAggregationResult()` |
1277
1458
  | `aggregationModel` | `GridAggregationModel` | — | From `apiRef.current.getAggregationModel()` |
1278
- | `filterModel` | `GridFilterModel` | — | From `apiRef.current.getFilterModel()` |
1279
- | `groupedRows` | `GridGroupedExportRow[]` | — | Pre-built grouped export rows from `apiRef.current.getGroupedExportRows()`. When provided, the PDF table reflects the grouping structure (group-header, indented leaves, subtotals, grand total). |
1459
+ | `filterModel` | `GridFilterModel` | — | From `apiRef.current.getFilterModel()`. Nested groups, the logic operator and quick-search terms are summarised; rules without a value are skipped |
1460
+ | `groupedRows` | `GridGroupedExportRow[]` | — | Pre-built grouped export rows from `apiRef.current.getGroupedExportRows()`. When provided (and `selectedRows` is empty), the PDF table reflects the grouping structure (group-header, indented leaves, subtotals, grand total). |
1280
1461
  | `alternateRowColor` | `boolean` | `true` | Alternating row background shading |
1281
- | `headerBackgroundColor` | `string` | `'#4f46e5'` | Column header cell background (hex) |
1282
- | `headerTextColor` | `string` | `'#ffffff'` | Column header cell text color (hex) |
1462
+ | `headerBackgroundColor` | `string` | `'#4f46e5'` | Column header cell background (`#rrggbb` / `#rgb`) |
1463
+ | `headerTextColor` | `string` | `'#ffffff'` | Column header cell text color (`#rrggbb` / `#rgb`) |
1283
1464
  | `fontSize` | `number` | `9` | Body cell font size in points |
1465
+ | `font` | `{ name, data, boldData? }` | — | Base64 TrueType font for text outside Latin-1 (₹, CJK, Cyrillic…). Without it, such characters are replaced with `?` (v3.0+) |