@opencorestack/opengridx 2.0.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/CHANGELOG.md +408 -3
  2. package/README.md +93 -72
  3. package/dist/index.d.ts +611 -121
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +7992 -5650
  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 -161
  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 +45 -22
  29. package/docs/components/datagrid.md +14 -12
  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 +100 -36
  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 +147 -41
  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 +56 -19
  54. package/docs/features/pinning.md +23 -5
  55. package/docs/features/selection.md +50 -10
  56. package/docs/features/sorting-pagination.md +66 -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 +44 -9
  60. package/docs/features/virtualization.md +64 -15
  61. package/docs/migration/v1-to-v2.md +115 -0
  62. package/docs/migration/v2-to-v3.md +1222 -0
  63. package/docs/performance.md +124 -0
  64. package/docs/roadmap.md +16 -10
  65. package/lib/components/Cell/Cell.css +13 -1
  66. package/lib/components/Cell/Cell.tsx +130 -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 +11 -0
  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 +84 -12
  76. package/lib/components/DataGrid/DataGrid.tsx +606 -872
  77. package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
  78. package/lib/components/DataGrid/GridEmptyState.tsx +7 -1
  79. package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
  80. package/lib/components/DataGrid/GridListView.tsx +166 -23
  81. package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
  82. package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
  83. package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
  84. package/lib/components/DataGrid/GridPinnedRows.tsx +65 -11
  85. package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
  86. package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
  87. package/lib/components/DataGrid/GridVirtualRows.tsx +48 -25
  88. package/lib/components/DetailPanel/DetailPanel.css +0 -6
  89. package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
  90. package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
  91. package/lib/components/FilterPanel/FilterPanel.css +13 -2
  92. package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
  93. package/lib/components/Header/ColumnMenu.tsx +91 -39
  94. package/lib/components/Header/Header.css +3 -3
  95. package/lib/components/Header/Header.tsx +179 -160
  96. package/lib/components/ListView/ListView.css +25 -0
  97. package/lib/components/ListView/ListViewRow.tsx +79 -10
  98. package/lib/components/Pagination/Pagination.tsx +15 -7
  99. package/lib/components/Row/Row.tsx +185 -75
  100. package/lib/components/Toolbar/GlobalSearch.css +6 -3
  101. package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
  102. package/lib/components/Toolbar/GridToolbar.css +12 -11
  103. package/lib/components/Toolbar/GridToolbar.tsx +133 -89
  104. package/lib/components/Toolbar/PivotPanel.tsx +89 -49
  105. package/lib/components/Tooltip/Tooltip.css +20 -0
  106. package/lib/components/Tooltip/Tooltip.tsx +68 -35
  107. package/lib/components/ui/Button.tsx +3 -0
  108. package/lib/components/ui/Checkbox.tsx +2 -1
  109. package/lib/components/ui/ExpandIcon.css +0 -9
  110. package/lib/components/ui/ExpandIcon.tsx +4 -1
  111. package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
  112. package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
  113. package/lib/hooks/core/useDataGrid.ts +115 -173
  114. package/lib/hooks/core/useGridAggregationApi.ts +72 -0
  115. package/lib/hooks/core/useGridApiMethods.ts +115 -0
  116. package/lib/hooks/core/useGridApiRef.ts +15 -3
  117. package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
  118. package/lib/hooks/core/useGridAriaRows.ts +38 -0
  119. package/lib/hooks/core/useGridClipboardApi.ts +38 -0
  120. package/lib/hooks/core/useGridColumnLookup.ts +30 -0
  121. package/lib/hooks/core/useGridColumns.tsx +284 -76
  122. package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
  123. package/lib/hooks/core/useGridControlledState.ts +99 -6
  124. package/lib/hooks/core/useGridDetailPanel.ts +51 -0
  125. package/lib/hooks/core/useGridDevWarnings.ts +123 -0
  126. package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
  127. package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
  128. package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
  129. package/lib/hooks/core/useGridHierarchy.ts +81 -0
  130. package/lib/hooks/core/useGridKeyboardNavigation.ts +452 -250
  131. package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
  132. package/lib/hooks/core/useGridPageCorrection.ts +55 -0
  133. package/lib/hooks/core/useGridRowIdOf.ts +32 -0
  134. package/lib/hooks/core/useGridRowInteractions.ts +81 -0
  135. package/lib/hooks/core/useGridRowPipeline.ts +69 -39
  136. package/lib/hooks/core/useGridRowSelection.ts +91 -0
  137. package/lib/hooks/core/useGridScrollSync.ts +80 -32
  138. package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
  139. package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
  140. package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
  141. package/lib/hooks/core/useGridToolbarProps.ts +97 -0
  142. package/lib/hooks/core/useGridViewport.ts +38 -0
  143. package/lib/hooks/core/useGridViewportSize.ts +26 -0
  144. package/lib/hooks/core/useGridVisibleRows.ts +20 -7
  145. package/lib/hooks/core/useLayout.ts +215 -195
  146. package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
  147. package/lib/hooks/core/useStableColumns.ts +34 -0
  148. package/lib/hooks/features/useAggregation.ts +86 -90
  149. package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
  150. package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
  151. package/lib/hooks/features/useGridClipboard.ts +133 -73
  152. package/lib/hooks/features/useGridDataSource.ts +354 -128
  153. package/lib/hooks/features/useGridEditing.ts +211 -51
  154. package/lib/hooks/features/useGridPivot.ts +92 -0
  155. package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
  156. package/lib/hooks/features/useGridSpanning.ts +410 -306
  157. package/lib/hooks/features/usePivot.ts +0 -0
  158. package/lib/hooks/features/useServerTreeChildren.ts +50 -0
  159. package/lib/hooks/useColumnReorder.ts +38 -44
  160. package/lib/hooks/useRowGrouping.ts +335 -214
  161. package/lib/hooks/useRowReorder.ts +62 -31
  162. package/lib/hooks/useTreeData.ts +330 -262
  163. package/lib/index.ts +30 -2
  164. package/lib/state/index.ts +1 -0
  165. package/lib/state/types.ts +7 -1
  166. package/lib/state/useGridStateStorage.ts +88 -29
  167. package/lib/theme/DataGridThemeProvider.tsx +143 -38
  168. package/lib/theme/gridThemeContext.ts +5 -0
  169. package/lib/theme/index.ts +5 -0
  170. package/lib/theme/presets.ts +5 -4
  171. package/lib/theme/types.ts +40 -2
  172. package/lib/types/index.ts +311 -108
  173. package/lib/utils/aggregation/index.ts +157 -0
  174. package/lib/utils/aria/index.ts +40 -0
  175. package/lib/utils/columnGroups.ts +110 -0
  176. package/lib/utils/columnLookup.ts +33 -0
  177. package/lib/utils/columnWidth.ts +22 -0
  178. package/lib/utils/dataSource/index.ts +46 -0
  179. package/lib/utils/detailPanel.ts +66 -0
  180. package/lib/utils/dragData.ts +44 -0
  181. package/lib/utils/editing/index.ts +30 -0
  182. package/lib/utils/export/exportShared.ts +197 -0
  183. package/lib/utils/export/exportToExcelAdvanced.ts +344 -121
  184. package/lib/utils/export/exportToPdf.ts +143 -92
  185. package/lib/utils/export/groupLabel.ts +14 -0
  186. package/lib/utils/export/index.ts +278 -272
  187. package/lib/utils/export/pdfText.ts +50 -0
  188. package/lib/utils/filtering/index.ts +277 -148
  189. package/lib/utils/focus/index.ts +115 -0
  190. package/lib/utils/gridApiRows.ts +35 -0
  191. package/lib/utils/gridModes.ts +77 -0
  192. package/lib/utils/grouping/groupedExportRows.ts +91 -0
  193. package/lib/utils/pagination/index.ts +56 -0
  194. package/lib/utils/pinning/index.ts +77 -33
  195. package/lib/utils/pivot/index.ts +368 -0
  196. package/lib/utils/scroll/index.ts +78 -0
  197. package/lib/utils/scroll/scrollEnd.ts +18 -0
  198. package/lib/utils/sorting/index.ts +154 -30
  199. package/lib/utils/syntheticRows.ts +12 -0
  200. package/lib/utils/values.ts +206 -0
  201. package/lib/utils/viewport.ts +14 -0
  202. package/llms.txt +272 -166
  203. package/package.json +18 -6
  204. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
  205. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
  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/Header/Header.test.tsx +0 -135
  210. package/lib/components/Pagination/Pagination.test.tsx +0 -64
  211. package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
  212. package/lib/hooks/core/useGridControlledState.test.ts +0 -86
  213. package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
  214. package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
  215. package/lib/hooks/features/useAggregation.test.ts +0 -175
  216. package/lib/hooks/features/usePivot.test.ts +0 -202
  217. package/lib/hooks/useRowGrouping.test.ts +0 -137
  218. package/lib/utils/export/exportToPdf.test.ts +0 -120
  219. package/lib/utils/export/groupedExport.test.ts +0 -144
  220. package/lib/utils/filtering/filtering.test.ts +0 -178
  221. package/lib/utils/sorting/sorting.test.ts +0 -88
package/docs/README.md CHANGED
@@ -43,6 +43,7 @@ In-depth guides on the grid's functional capabilities.
43
43
  - **[Tree Data & Grouping](features/tree-data-grouping.md)** - Hierarchical layout and Column grouping
44
44
  - **[Cell Spanning](features/cell-spanning.md)** - Row and Col span spanning
45
45
  - **[Master-Detail](features/master-detail.md)** - Expandable Detail Panels
46
+ - **[Keyboard & Accessibility](features/keyboard-navigation.md)** - Keys, focus behaviour and ARIA structure
46
47
  - **[List View](features/list-view.md)** - Card-based responsive layout
47
48
  - **[Infinite Scroll](features/infinite-scroll.md)** - Advanced performance loading
48
49
  - **[Data Source](features/data-source.md)** - Server-side fetching and SSR
@@ -61,6 +62,7 @@ Make the grid your own.
61
62
  ## 🏛️ Architecture — Internal Hooks
62
63
  Reference for core hooks extracted from `DataGrid.tsx`. Intended for contributors.
63
64
 
65
+ - **[DataGrid orchestration](architecture/datagrid-orchestration.md)** - Every hook `DataGrid.tsx` calls, in call order, and why the order matters
64
66
  - **[useGridControlledState](architecture/use-grid-controlled-state.md)** - Controlled/uncontrolled state for all 7 state pairs
65
67
  - **[useGridRowPipeline](architecture/use-grid-row-pipeline.md)** - Filter → pin → sort → paginate row pipeline
66
68
  - **[useGridVirtualization](architecture/use-grid-virtualization.md)** - Render context and scroll-position math
@@ -69,6 +71,9 @@ Reference for core hooks extracted from `DataGrid.tsx`. Intended for contributor
69
71
  - **[useGridScrollSync](architecture/use-grid-scroll-sync.md)** - RAF-batched scroll tick, scrollPosRef, onRowsScrollEnd threshold
70
72
  - **[useGridStateSnapshot](architecture/use-grid-state-snapshot.md)** - onStateChange callback delivery on sort/filter/pagination/column changes
71
73
 
74
+ ## 🔀 Migration Guides
75
+ - **[v2 → v3](migration/v2-to-v3.md)** - Underscore row fields removed, `params.row` identity, grouped export labels
76
+ - **[v1 → v2](migration/v1-to-v2.md)** - Removed dead props, `groupable` / `availableAggregationFunctions` enforcement
77
+
72
78
  ## 🛠️ Project Meta
73
79
  - **[Development Roadmap](roadmap.md)** - What's built and what's next
74
- - **[Research Notes](research/FEATURE_CATALOG.md)** - Comparative analysis and design decisions
@@ -0,0 +1,81 @@
1
+ # DataGrid orchestration
2
+
3
+ `lib/components/DataGrid/DataGrid.tsx` is the orchestration layer: it destructures the props, calls
4
+ hooks and pure utils in a fixed order, and renders JSX. Feature logic lives in the hooks and utils
5
+ listed here; add new logic there, not in the component.
6
+
7
+ ## Why the order matters
8
+
9
+ Effects run in the order their hooks are called (within the same phase: layout effects first, then
10
+ passive effects). Several hooks install methods on `apiRef` or read state that an earlier hook wrote
11
+ in an effect, so **keep the call order when you move or add a hook**. In particular:
12
+
13
+ - `useGridApiRefBinding` points the consumer's `apiRef` at the live API in a layout effect, before any
14
+ parent layout effect runs.
15
+ - `useGridApiMethods` (layout effects) installs the state methods before `useGridAggregationApi`,
16
+ `useGridClipboardApi` and `useGridScrollToIndexesApi` install theirs. `useGridApiMethods`,
17
+ `useGridAggregationApi` and `useGridScrollToIndexesApi` install once per `apiRef` in a layout
18
+ effect and read the latest values from a ref refreshed in a layout effect, so the methods are
19
+ live in a parent's layout effect and never answer from a previous render.
20
+ - `useGridLiveRowSelection` prunes the selection, and `useGridPageCorrection` corrects a page past the
21
+ end, in effects that run before the later hooks' effects.
22
+
23
+ ## Call order
24
+
25
+ | # | Hook / util | File | Produces |
26
+ | :- | :--- | :--- | :--- |
27
+ | 0 | `useStableColumns` | `hooks/core/useStableColumns.ts` | The `columns` prop with a stable identity while every column is shallowly equal (inline columns do not re-run the row passes) |
28
+ | 1 | `useGridControlledState` | `hooks/core/useGridControlledState.ts` | Controlled/uncontrolled sort, filter, aggregation, visibility, pinning, pivot, pagination, selection, density |
29
+ | 2 | `useGridPivot` | `hooks/features/useGridPivot.ts` | Pivot rows/columns (or the source ones) and the pipeline filter/sort models |
30
+ | 3 | `resolveGridModes` (pure) | `utils/gridModes.ts` | Which features are in effect: tree data, row grouping, pagination, row reordering, list view, grouping column |
31
+ | 4 | `useGridThemeDimensions` | `hooks/core/useGridThemeDimensions.ts` | Row and header heights (props → theme provider → defaults) |
32
+ | 5 | `useGridGroupingColumn` | `hooks/core/useGridGroupingColumn.ts` | `activeColumns` with the `__group__` column, and its auto-pin |
33
+ | 6 | `useGridColumnLookup` | `hooks/core/useGridColumnLookup.ts` | Column lookup for filtering/sorting |
34
+ | 7 | `useServerAggregationResults` | `hooks/features/useAggregation.ts` | Server-provided totals |
35
+ | 8 | `useGridKeyboardMode` | `hooks/core/useGridFocusHandlers.ts` | `setKeyboardMode` (toggles `ogx--kb` on the root) |
36
+ | 9 | `useDataGrid` | `hooks/core/useDataGrid.ts` | Row store, dimensions, data-source state, the live `apiRef` |
37
+ | 10 | `useGridRowIdOf` | `hooks/core/useGridRowIdOf.ts` | `getRowIdOf` and its stable event-handler twin |
38
+ | 11 | `useGridApiRefBinding` | `hooks/core/useGridApiRefBinding.ts` | Binds the `apiRef` prop (layout effect) |
39
+ | 12 | `useGridHierarchy` | `hooks/core/useGridHierarchy.ts` | `useTreeData` + `useRowGrouping`, the active handlers and `rowMetaMap` |
40
+ | 13 | `useGridEditing` | `hooks/features/useGridEditing.ts` | Cell editing state and handlers |
41
+ | 14 | `useGridLiveRowSelection` | `hooks/core/useGridLiveRowSelection.ts` | Selection without removed rows |
42
+ | 15 | `useGridDataSource` | `hooks/features/useGridDataSource.ts` | Server fetching |
43
+ | 16 | `useServerTreeChildren` | `hooks/features/useServerTreeChildren.ts` | Lazy server tree children |
44
+ | 17 | `useGridDetailPanel` | `hooks/core/useGridDetailPanel.ts` | Detail-panel expansion, toggle, measured heights |
45
+ | 18 | `useGridColumns` | `hooks/core/useGridColumns.tsx` | Effective/ordered/visible columns, widths, reorder |
46
+ | 19 | `useGridStateSnapshot` | `hooks/core/useGridStateSnapshot.ts` | `onStateChange` delivery |
47
+ | 20 | `useGridRowPipeline` | `hooks/core/useGridRowPipeline.ts` | Filter → pin → sort → paginate |
48
+ | 21 | `useGridPageCorrection` | `hooks/core/useGridPageCorrection.ts` | Moves a page left past the end back to the last page |
49
+ | 22 | `getPaginationRowCount` (pure) | `utils/pagination/index.ts` | What the pager pages through |
50
+ | 23 | `useGridRowInteractions` | `hooks/core/useGridRowInteractions.ts` | Row selection, `handleRowClick`, `handleSelectionChange` |
51
+ | 24 | `useGridApiMethods` | `hooks/core/useGridApiMethods.ts` | State API methods (`getAllFilteredRows` via `utils/gridApiRows.ts`) |
52
+ | 25 | `useRowReorder` | `hooks/useRowReorder.ts` | Row drag-reorder |
53
+ | 26 | `useAggregation` | `hooks/features/useAggregation.ts` | Footer aggregation result |
54
+ | 27 | `useGridAggregationApi` | `hooks/core/useGridAggregationApi.ts` | `getAggregationResult`, `getAggregationModel`, `getGroupedExportRows` |
55
+ | 28 | `useLayout` | `hooks/core/useLayout.ts` | Row heights, pinned column split, widths |
56
+ | 29 | `useGridSpanning` | `hooks/features/useGridSpanning.ts` | Row/column spans |
57
+ | 30 | `useGridClipboardApi` | `hooks/core/useGridClipboardApi.ts` | Ctrl/Cmd+C and `copySelectedRows` |
58
+ | 31 | `useGridScrollToIndexesApi` | `hooks/core/useGridScrollToIndexesApi.ts` | `scrollToIndexes` |
59
+ | 32 | `useGridViewport` | `hooks/core/useGridViewport.ts` | Scroll sync, viewport measurement, viewport callback ref |
60
+ | 33 | `useGridVirtualization` | `hooks/core/useGridVirtualization.ts` | Render window |
61
+ | 34 | `useGridSortHandlers` | `hooks/core/useGridHeaderHandlers.ts` | Header sort / shift-sort |
62
+ | 35 | `useGridKeyboardNavigation` | `hooks/core/useGridKeyboardNavigation.ts` | Focus and keyboard handling |
63
+ | 36 | `useGridPointerFocusHandlers` | `hooks/core/useGridFocusHandlers.ts` | Cell and header click focus |
64
+ | 37 | `useGridColumnMenuHandlers` | `hooks/core/useGridHeaderHandlers.ts` | Column menu Hide / Pin (`pinColumnTo` in `utils/pinning`) |
65
+ | 38 | `useGridAriaRows` | `hooks/core/useGridAriaRows.ts` | `aria-rowcount` and `aria-rowindex` bases |
66
+ | 39 | `useGridSpanRowWindow` | `hooks/features/useGridSpanRenderWindow.ts` | Render window widened to whole row spans |
67
+ | 40 | `useGridVisibleRows` | `hooks/core/useGridVisibleRows.ts` | Pinned + virtual centre rows to render |
68
+ | 41 | `useGridSpanColumnWindow` | `hooks/features/useGridSpanRenderWindow.ts` | Columns to render, widened to whole column spans |
69
+ | 42 | `useGridDevWarnings` | `hooks/core/useGridDevWarnings.ts` | Dev-only warnings |
70
+ | 43 | `useColumnGroupReorderGuard` | `hooks/features/useColumnGroupReorderGuard.ts` | Header drag-reorder kept inside column groups |
71
+ | 44 | `useGridColumnsPanel` | `hooks/core/useGridColumnsPanel.ts` | Columns panel (toolbar or standalone) |
72
+ | 45 | `useGridStylesheetWarning` | `hooks/core/useGridDevWarnings.ts` | Warns when the stylesheet is missing |
73
+ | 46 | `useGridToolbarProps` | `hooks/core/useGridToolbarProps.ts` | Props for `slots.toolbar` |
74
+
75
+ ## Rendered components
76
+
77
+ `GridToolbarSlot`, `GridStandaloneColumnPanel`, `GridListView` (list view) or the viewport
78
+ (`Header`, `GridPinnedRows` top, `GridEmptyState`, `GridVirtualRows`, `GridPinnedRows` bottom,
79
+ `GridAggregationFooter`), then `slots.footer` or `GridPaginationArea`, `GridLiveRegion`,
80
+ `GridLoadingOverlay` and `GridErrorOverlay`. The props all three row renderers take alike are built
81
+ once in `DataGrid.tsx` (`rowRenderProps`) and spread into each.
@@ -0,0 +1,102 @@
1
+ # `GridRowMeta`
2
+
3
+ Hierarchy metadata for a grid row, available via `params.rowMeta` in `renderCell` since v1.1.
4
+
5
+ **File:** `lib/types/index.ts`
6
+
7
+ ---
8
+
9
+ ## Why it exists
10
+
11
+ Before v1.1, `useTreeData` and `useRowGrouping` injected internal fields (`_hasChildren`, `_treeDepth`, etc.) directly onto each row object and declared them on `GridRowModel`. This caused two problems:
12
+
13
+ 1. **Collision risk** — a user dataset with a field called `_hasChildren` would be silently corrupted.
14
+ 2. **API leakage** — implementation details appeared as typed public API, inviting users to depend on them.
15
+
16
+ `GridRowMeta` moves these fields to a separate `Map<GridRowId, GridRowMeta>` that never touches the user's data.
17
+
18
+ ---
19
+
20
+ ## Interface
21
+
22
+ ```ts
23
+ export interface GridRowMeta {
24
+ hasChildren?: boolean;
25
+ treeDepth?: number;
26
+ groupingField?: string;
27
+ groupingValue?: unknown;
28
+ groupLabel?: string;
29
+ descendantCount?: number;
30
+ isExpanded?: boolean;
31
+ isGroupRow?: boolean;
32
+ isGroupFooter?: boolean; // v3.0
33
+ }
34
+ ```
35
+
36
+ | Field | Meaning |
37
+ | :--- | :--- |
38
+ | `hasChildren` | The row has children to show under the active filter, so the expand toggle is rendered. A lazy tree node whose children are not loaded yet (`serverChildrenCount > 0`) counts as having children. |
39
+ | `descendantCount` | Data rows below the row, at any depth, that pass the filter: row-grouping leaves, or tree-data rows (auto-created tree parents are not counted). `undefined` for a lazy node whose children are not loaded. It is the `(n)` next to group labels. |
40
+ | `groupLabel` | The label shown for a synthetic row: the group label (`groupingValueFormatter` or `"field: value"`), or the path segment of an auto-created tree parent. |
41
+ | `isGroupRow` | The row is synthetic: a row-grouping group row, a group subtotal row, or a tree-data parent created for a path segment that has no row of its own (its row object is just `{ id }`). Synthetic rows are never selected, edited or given a detail panel, clicking one toggles it, and `valueGetter` is not called for them. Tree-data parents that are your own rows have `isGroupRow: false`. |
42
+ | `isGroupFooter` | The row is the subtotal row of a group, shown after its children when `getAggregationPosition` returns `'footer'`. |
43
+
44
+ ---
45
+
46
+ ## Data flow
47
+
48
+ ```
49
+ useTreeData / useRowGrouping
50
+ └─ returns rowMetaMap: Map<GridRowId, GridRowMeta>
51
+
52
+ useGridHierarchy (called by DataGrid.tsx)
53
+ └─ selects the active map (tree data XOR row grouping XOR empty)
54
+ └─ uses rowMetaMap in:
55
+ ├─ handleRowClick / selection / editing (skip or toggle synthetic rows)
56
+ └─ GridVirtualRows / GridPinnedRows / GridListView
57
+ (resolve meta per row: rowMetaMap.get(id), id = getRowId(row))
58
+ └─ Row (rowMeta prop)
59
+ └─ Cell (passes rowMeta into GridRenderCellParams)
60
+ └─ renderCell, including the hierarchy renderers useGridColumns injects (read params.rowMeta)
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Accessing metadata in `renderCell`
66
+
67
+ ```tsx
68
+ renderCell: (params) => {
69
+ // params.rowMeta is undefined for flat rows
70
+ if (params.rowMeta?.hasChildren) {
71
+ return <strong>{params.formattedValue} ({params.rowMeta.descendantCount})</strong>;
72
+ }
73
+ return params.formattedValue;
74
+ }
75
+ ```
76
+
77
+ Since v3.0 `renderCell` is called for every row, including synthetic group, subtotal and auto-created tree-parent rows, in every column, and its output is shown. In the column that holds the expand toggle, the output replaces the default group label next to the toggle. **Returning `undefined` for a synthetic row keeps the grid's default content** (the group label and count in the toggle column, the aggregate or nothing elsewhere), so a renderCell written for data rows can opt out in one line:
78
+
79
+ ```tsx
80
+ renderCell: (params) => (params.rowMeta?.isGroupRow ? undefined : <Avatar user={params.row.user} />)
81
+ ```
82
+
83
+ A renderCell that throws on a synthetic row shows the `⚠` cell indicator; in the toggle column the toggle stays usable.
84
+
85
+ ---
86
+
87
+ ## Runtime shim (removed in v3.0)
88
+
89
+ From v1.1 to v2.x, `useTreeData` and `useRowGrouping` still copied each row and injected the underscore fields (`_hasChildren`, `_treeDepth`, `_isExpanded`, `_groupingField`, `_groupingValue`, `_descendantCount`, `_isGroupRow`) onto the copy for backward compatibility. (An earlier revision of this doc wrongly said the injection was removed in v2.0.)
90
+
91
+ **Since v3.0 nothing is injected.** Both hooks pass the consumer's row objects through unchanged and all hierarchy information lives in `rowMetaMap` / `params.rowMeta`. `params.row` under grouping is therefore the same object the consumer passed in `rows`, just as it is without grouping. See [Migrating from v2 to v3](../migration/v2-to-v3.md).
92
+
93
+ ---
94
+
95
+ ## Non-hierarchy rows
96
+
97
+ `params.rowMeta` is `undefined` when no tree-data or row-grouping is active. While a hierarchy is active, **every** row rendered in it has an entry, data rows included:
98
+
99
+ - Row grouping: a leaf (data) row has `{ hasChildren: false, treeDepth, isGroupRow: false }`; group rows add `groupingField`, `groupingValue`, `groupLabel`, `descendantCount` and `isExpanded`; subtotal rows are like their group row with `isGroupFooter: true`, `hasChildren: false` and `treeDepth + 1`.
100
+ - Tree data: every node has `hasChildren`, `treeDepth`, `descendantCount`, `isGroupRow` and `isExpanded`; auto-created parents also have `groupLabel`.
101
+
102
+ Still guard with optional chaining (`params.rowMeta?.hasChildren`) so the same `renderCell` works with and without a hierarchy.
@@ -0,0 +1,128 @@
1
+ # `useGridColumns`
2
+
3
+ Internal hook. Manages the full column lifecycle: injects hierarchy cell renderers, maintains column widths and order state, derives ordered/visible column lists, and computes the navigation column array used by keyboard navigation and spanning.
4
+
5
+ **File:** `lib/hooks/core/useGridColumns.tsx` (`.tsx` — contains JSX for hierarchy cell renderers)
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ Before extraction, ~195 lines of column management lived inline in `DataGrid.tsx`. Pulling them out gives this logic its own test surface and removes hierarchy rendering concerns from the main component.
12
+
13
+ The injected hierarchy renderers read hierarchy metadata from `params.rowMeta` (the `GridRowMeta` entry from `rowMetaMap`), never from the row object: rows are the consumer's own objects and carry no `_treeDepth` / `_hasChildren` fields (removed in v3.0). See [GridRowMeta](grid-row-meta.md).
14
+
15
+ ---
16
+
17
+ ## Parameters
18
+
19
+ ```ts
20
+ interface UseGridColumnsParams<R extends GridRowModel> {
21
+ activeColumns: GridColDef<R>[]; // pivot-resolved column list
22
+ isHierarchyEnabled: boolean; // true when treeData OR rowGrouping active
23
+ isRowGrouping: boolean;
24
+ isTreeData: boolean;
25
+ activeHierarchyHandlers: { toggleExpansion: (id: GridRowId) => void } | null;
26
+ columnVisibilityModel: Record<string, boolean>;
27
+ columnOrder?: string[]; // controlled; undefined = uncontrolled
28
+ onColumnOrderChange?: (params: GridColumnOrderChangeParams) => void;
29
+ onColumnOrderModelChange?: (columnOrder: string[]) => void; // whole new order; not in pivot mode
30
+ disableColumnReorder: boolean;
31
+ pivotMode: boolean; // pivot is active (DataGrid passes useGridPivot's isActive)
32
+ checkboxSelection: boolean;
33
+ hasDetailPanel: boolean;
34
+ rowReordering: boolean;
35
+ initialState?: GridInitialState;
36
+ setColumns: (cols: GridColDef[]) => void; // state store updater from useDataGrid
37
+ pinnedColumns?: GridColumnPinning;
38
+ getRowId?: (row: R) => GridRowId; // id passed to toggleExpansion; defaults to row.id
39
+ aggregationModel?: GridAggregationModel; // formats aggregates on row-grouping group rows
40
+ groupingRows?: ReadonlyMap<GridRowId, unknown>; // row grouping's synthetic group rows, by id
41
+ }
42
+ ```
43
+
44
+ > **`hasDetailPanel` is resolved before the hook call** — it depends only on `getDetailPanelContent` (a prop): `useGridDetailPanel`, called just before `useGridColumns` in `DataGrid.tsx`, returns it as `Boolean(getDetailPanelContent)`.
45
+
46
+ ---
47
+
48
+ ## Returns
49
+
50
+ ```ts
51
+ interface UseGridColumnsResult<R extends GridRowModel> {
52
+ effectiveColumns: GridColDef<R>[]; // hierarchy renderers injected
53
+ hierarchyField: string | undefined; // the column that gets the expand toggle / indent / group label
54
+ orderedColumns: GridColDef<R>[]; // sorted by effectiveColumnOrder
55
+ visibleOrderedColumns: GridColDef<R>[]; // filtered by columnVisibilityModel
56
+ navigationColumns: Array<GridColDef<R> | { field: string; sortable: false; editable: false }>; // system + visible data cols
57
+ columnIndexMap: Map<string, number>; // visible data column field -> public colIndex
58
+ columnWidths: Record<string, number>;
59
+ effectiveColumnOrder: string[];
60
+ setInternalColumnOrder: React.Dispatch<...>;
61
+ columnReorderHandlers: ReturnType<typeof useColumnReorder>; // header drag
62
+ moveColumn: (fromField: string, toField: string) => void; // Columns panel drag
63
+ resetColumnOrder: () => void; // Columns panel Reset
64
+ handleColumnResize: (field: string, newWidth: number) => void;
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ## `effectiveColumns` — hierarchy renderer injection
71
+
72
+ When `isHierarchyEnabled` is `false`, `effectiveColumns` is `activeColumns` unchanged.
73
+
74
+ When `true`, the **hierarchy column** (`hierarchyField`) is the leftmost column actually on screen: the first visible column in render order (left-pinned in `pinnedColumns.left` order, then unpinned in column order, then right-pinned). Hiding, reordering or pinning columns therefore moves the toggle instead of dropping it. That column gets a `renderCell` override that:
75
+ 1. Reads `params.rowMeta` (`treeDepth`, `hasChildren`, `isExpanded`, `isGroupRow`, `groupLabel`, `descendantCount`, …)
76
+ 2. Renders indented padding (`treeDepth * 24px`)
77
+ 3. Shows an `<ExpandIcon>` if the row has children; clicking it calls `toggleExpansion(getRowId(row))`
78
+ 4. Renders the content next to it: the consumer's `renderCell` output if the column has one (isolated in its own `CellErrorBoundary`, so a throw keeps the toggle), otherwise the default — `params.formattedValue` for data rows, the group label plus `(count)` for row-grouping group rows and tree-data parents, nothing for subtotal (group footer) rows. For a synthetic row, a consumer `renderCell` returning `undefined` falls back to the default.
79
+
80
+ All other columns get a `renderCell` wrapper. Data rows use the column's own `renderCell` or `params.formattedValue`. On synthetic group rows the consumer's `renderCell` decides (`undefined` keeps the default); by default the grouping-field column is empty (the label already shows the value) and other columns show their value (the aggregate) or nothing.
81
+
82
+ Under row grouping, a column with an `aggregationModel` entry also gets `valueGetter` / `valueFormatter` wrappers for group rows (identified through `groupingRows`): the cell shows the aggregate stored on the group row (a `valueGetter` would recompute it from fields a group row does not have), formatted with `formatAggregateForColumn`, the formatter the footer, pivot cells and exports use (so `count` / `unique` are never put in the column's currency or unit format). Leaf rows keep the column's own getter and formatter.
83
+
84
+ ---
85
+
86
+ ## Column order
87
+
88
+ - **Uncontrolled:** the order is the columns' own order until the user reorders (`setInternalColumnOrder`); it is derived, not snapshotted at mount, so it stays right when the columns change or the grid mounts in pivot mode.
89
+ - **Controlled:** `columnOrder` wins, except in pivot mode.
90
+ - **Every move works on the full current order**, `orderedColumns.map(c => c.field)`: all current columns, including those added after mount, those missing from a stored or controlled order, and the synthetic `__group__` column. The header drag (`columnReorderHandlers`), `moveColumn` and `resetColumnOrder` all go through one commit step: it stores the new order (uncontrolled or pivot), fires `onColumnOrderChange` with indices in that full order (not for Reset), and fires `onColumnOrderModelChange` with the whole order (not in pivot mode). Before v3.0 the indices came from `orderedColumns` but were applied to `effectiveColumnOrder`, so a column added after the order was stored, or a partial controlled order, made a drag move the wrong column or nothing.
91
+ - **Pivot mode:** the generated pivot columns keep an order of their own, reset whenever the generated column set changes. Pivoting never rewrites the normal order, a controlled `columnOrder` (which names source columns) does not apply, and reordering updates the pivot order. `setInternalColumnOrder` writes to the order that is active.
92
+
93
+ ## Visibility
94
+
95
+ `visibleOrderedColumns` drops columns hidden by `columnVisibilityModel`, except, in pivot mode, the pivot row-label columns (marked `hideable: false`): they share their field with the source column, which the visibility model may hide in normal mode, and the pivot rows are labelled by them.
96
+
97
+ ---
98
+
99
+ ## `navigationColumns` and `columnIndexMap`
100
+
101
+ `navigationColumns` is what Row and Header render, in render order: system column stubs, then the visible data columns sorted left-pinned, unpinned, right-pinned (the same order `useLayout` renders them in):
102
+
103
+ ```
104
+ [{ field: '__reorder_col__', sortable: false, editable: false }] (if rowReordering)
105
+ [{ field: '__expand_col__', sortable: false, editable: false }] (if hasDetailPanel)
106
+ [{ field: '__checkbox_col__', sortable: false, editable: false }] (if checkboxSelection)
107
+ ...visibleOrderedColumns in pinned render order
108
+ ```
109
+
110
+ Hidden columns are not in it, so arrow keys never land on a column that is not rendered (v3.0; it used to be built from `orderedColumns`, in unpinned order). Its length is the grid's `aria-colcount`.
111
+
112
+ `columnIndexMap` maps each visible data column's field to its position in that render order, system columns excluded. Row, Cell and Header use it for the public `colIndex` (`GridCellParams`, `renderCell`, `renderHeader`) and for `aria-colindex` (`colIndex + 1 +` the number of system columns), so both are absolute and do not depend on the horizontal render window.
113
+
114
+ `navigationColumns` is consumed by `useGridKeyboardNavigation` (to map arrow-key movements across all focusable columns). `useGridSpanning` does not use it: spans are computed over the rendered data columns only (the layout's left-pinned, unpinned and right-pinned columns), so system columns and hidden columns are never part of a span.
115
+
116
+ ---
117
+
118
+ ## Relationship to DataGrid
119
+
120
+ DataGrid calls this hook after computing `isHierarchyEnabled`, `activeHierarchyHandlers`, and hoisting `hasDetailPanel`. The hook's return values are destructured directly into the variables the JSX return and downstream hooks expect.
121
+
122
+ ---
123
+
124
+ ## 🔗 Related
125
+ - [useGridRowPipeline](use-grid-row-pipeline.md)
126
+ - [useGridVirtualization](use-grid-virtualization.md)
127
+ - [useGridVisibleRows](use-grid-visible-rows.md)
128
+ - [DataGrid](../components/datagrid.md)
@@ -0,0 +1,157 @@
1
+ # `useGridControlledState`
2
+
3
+ Internal hook. Centralises the controlled/uncontrolled state pattern for every piece of DataGrid state that can be driven externally via props.
4
+
5
+ **File:** `lib/hooks/core/useGridControlledState.ts`
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ Eight DataGrid state pairs follow the same pattern:
12
+
13
+ ```
14
+ [internal, setInternal] = useState(defaultValue)
15
+ isControlled = prop !== undefined
16
+ effective = isControlled ? prop : internal
17
+ ```
18
+
19
+ Before this hook existed, those 85 lines lived inline in `DataGrid.tsx`. `useGridControlledState` extracts them into a single location so the controlled/uncontrolled contract is tested and reasoned about in one place.
20
+
21
+ ---
22
+
23
+ ## Managed state pairs
24
+
25
+ | State | Prop (controlled) | Change callback |
26
+ | :--- | :--- | :--- |
27
+ | Sort | `sortModel` | `onSortModelChange` |
28
+ | Filter | `filterModel` | `onFilterModelChange` |
29
+ | Aggregation | `aggregationModel` | `onAggregationModelChange` |
30
+ | Column visibility | `columnVisibilityModel` | `onColumnVisibilityModelChange` |
31
+ | Pinned columns | `pinnedColumns` | `onPinnedColumnsChange` |
32
+ | Pivot | `pivotModel` | `onPivotModelChange` |
33
+ | Pagination | `paginationModel` | `onPaginationModelChange` |
34
+ | Row selection | `rowSelectionModel` | `onRowSelectionModelChange` |
35
+
36
+ It also resolves `density` (the `density` prop, else `initialState.density`, else `'standard'`).
37
+
38
+ This hook is the single source of truth for these models. The `useDataGrid` reducer holds only
39
+ the row store, dimensions and dataSource status (it used to keep its own sort, filter, pagination
40
+ and selection copies, which drifted from what the grid rendered and made `apiRef` setters no-ops).
41
+ Everything that reads or changes these models — the header, toolbar, pager, row checkboxes and
42
+ the imperative API (`useGridApiMethods`) — goes through the values and `handle*Change` callbacks
43
+ returned here.
44
+
45
+ ---
46
+
47
+ ## Parameters
48
+
49
+ ```ts
50
+ interface UseGridControlledStateParams {
51
+ initialState?: GridInitialState;
52
+
53
+ sortModel?: GridSortItem[];
54
+ onSortModelChange?: (model: GridSortItem[]) => void;
55
+
56
+ filterModel?: GridFilterModel;
57
+ onFilterModelChange?: (model: GridFilterModel) => void;
58
+
59
+ aggregationModel?: GridAggregationModel;
60
+ onAggregationModelChange?: (model: GridAggregationModel) => void;
61
+
62
+ columnVisibilityModel?: Record<string, boolean>;
63
+ onColumnVisibilityModelChange?: (model: Record<string, boolean>) => void;
64
+
65
+ pinnedColumns?: GridColumnPinning;
66
+ onPinnedColumnsChange?: (model: GridColumnPinning) => void;
67
+
68
+ pivotModel?: GridPivotModel;
69
+ onPivotModelChange?: (model: GridPivotModel) => void;
70
+
71
+ paginationModel?: GridPaginationModel;
72
+ onPaginationModelChange?: (model: GridPaginationModel) => void;
73
+ pageSizeOptions?: number[]; // picks the uncontrolled default page size
74
+
75
+ rowSelectionModel?: GridRowId[];
76
+ onRowSelectionModelChange?: (model: GridRowId[]) => void;
77
+
78
+ density?: 'compact' | 'standard' | 'comfortable';
79
+ }
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Returns
85
+
86
+ ```ts
87
+ interface UseGridControlledStateReturn {
88
+ // Sort
89
+ sortModel: GridSortItem[];
90
+ isSortControlled: boolean;
91
+ setInternalSortModel: React.Dispatch<React.SetStateAction<GridSortItem[]>>;
92
+ handleSortModelChange: (model: GridSortItem[]) => void;
93
+
94
+ // Filter (seeded from initialState.filter.filterModel)
95
+ filterModel: GridFilterModel;
96
+ isFilterControlled: boolean;
97
+ handleFilterModelChange: (model: GridFilterModel) => void;
98
+
99
+ // Aggregation
100
+ aggregationModel: GridAggregationModel;
101
+ handleAggregationModelChange: (model: GridAggregationModel) => void;
102
+
103
+ // Column visibility
104
+ columnVisibilityModel: Record<string, boolean>;
105
+ handleColumnVisibilityModelChange: (model: Record<string, boolean>) => void;
106
+
107
+ // Pinned columns
108
+ pinnedColumns: GridColumnPinning;
109
+ handlePinnedColumnsChange: (model: GridColumnPinning) => void;
110
+
111
+ // Pivot
112
+ currentPivotModel: GridPivotModel;
113
+ handlePivotModelChange: (model: GridPivotModel) => void;
114
+
115
+ // Pagination
116
+ effectivePaginationModel: GridPaginationModel;
117
+ handlePaginationModelChange: (model: GridPaginationModel) => void;
118
+
119
+ // Row selection
120
+ rowSelectionModel: GridRowId[];
121
+ selectedRowIds: Set<GridRowId>; // derived Set for O(1) lookup
122
+ isSelectionControlled: boolean;
123
+ setInternalRowSelectionModel: React.Dispatch<React.SetStateAction<GridRowId[]>>;
124
+ handleRowSelectionModelChange: (model: GridRowId[]) => void;
125
+
126
+ // Density
127
+ density: 'compact' | 'standard' | 'comfortable';
128
+ }
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Key behaviours
134
+
135
+ **Controlled mode:** When a prop is provided (`prop !== undefined`), the effective value is always `prop`. The internal state is not updated — the caller is responsible for updating the prop via the change callback.
136
+
137
+ **Uncontrolled mode:** When a prop is `undefined`, the effective value is the internal state. The `handle*Change` callback updates internal state and also fires the external callback (if provided) for observability.
138
+
139
+ **Selection:** `handleRowSelectionModelChange` applies a new model like the other handlers. The row-click, checkbox, Space-key and select-all rules (including `disableMultipleRowSelection`) live in `useGridRowSelection`, which calls it. `getPendingRowSelectionModel()` (stable identity) returns the model most recently passed to `handleRowSelectionModelChange` that has not rendered yet, or `null`; it is cleared on every commit (a controlled parent may reject a change). `useGridApiMethods` prefers it over the rendered (pruned) selection, so `apiRef.getSelectedRows()` and `copySelectedRows()` are current inside `onRowSelectionModelChange` and right after `apiRef.selectRows`.
140
+
141
+ **Default page size:** without `paginationModel` or `initialState.pagination`, the page size is 100 when `pageSizeOptions` offers it, otherwise the first option, so the page-size select always shows the size in use.
142
+
143
+ **`aggregationModel` identity follows content:** an inline `aggregationModel={{ salary: 'sum' }}` is a new object on every parent render. The returned `aggregationModel` keeps its identity while its content is unchanged (it is keyed on its JSON), so the server fetches and memos that depend on it do not rerun for an unchanged model.
144
+
145
+ **`selectedRowIds` (Set):** The array `rowSelectionModel` is converted to a `Set` via `useMemo` for O(1) membership checks during row rendering. Both are returned so callers can choose the right structure.
146
+
147
+ ---
148
+
149
+ ## Relationship to DataGrid
150
+
151
+ `DataGrid.tsx` calls this hook at the top of its body and destructures the returned values directly into the variables used by the rest of the component. The hook replaces the controlled/uncontrolled block that previously occupied lines ~200–285 of `DataGrid.tsx`.
152
+
153
+ ---
154
+
155
+ ## 🔗 Related
156
+ - [useGridRowPipeline](use-grid-row-pipeline.md)
157
+ - [DataGrid](../components/datagrid.md)
@@ -0,0 +1,120 @@
1
+ # `useGridRowPipeline`
2
+
3
+ Internal hook. Transforms the raw `rows` array through a deterministic five-stage pipeline — filter → pin → sort → paginate → assemble — producing the final ordered list of rows the viewport renders.
4
+
5
+ **File:** `lib/hooks/core/useGridRowPipeline.ts`
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ Before this hook existed, the five `useMemo` stages lived inline in `DataGrid.tsx` (~85 lines). Extracting them into one place makes the pipeline independently testable and removes noise from the main component.
12
+
13
+ ---
14
+
15
+ ## Pipeline stages
16
+
17
+ ```
18
+ effectiveRows
19
+ │
20
+ ▼ filterRows (client) / getVisibleRows (hierarchy) / passthrough (filterMode='server')
21
+ filteredRows
22
+ │
23
+ ▼ getPinnedRowGroups
24
+ pinnedTopRows, unpinnedRows, pinnedBottomRows
25
+ │
26
+ ▼ sortRows (client) / passthrough (sortingMode='server' / hierarchy)
27
+ sortedUnpinnedRows
28
+ │
29
+ ▼ slice [currentPage * pageSize, (currentPage+1) * pageSize] — skipped when pagination=false or paginationMode='server'
30
+ paginatedUnpinnedRows
31
+ │
32
+ ▼ [...pinnedTop, ...center, ...pinnedBottom]
33
+ allRenderableRows ← the rows the grid view and the list view render
34
+ ```
35
+
36
+ **Server modes do not need a `dataSource`.** `filterMode`, `sortingMode` and `paginationMode` set to
37
+ `'server'` mean the rows already arrived filtered, sorted or paged, whether a `dataSource` fetched
38
+ them or the consumer did (for example from `onPaginationModelChange`). Before v3.0 the passthrough
39
+ applied only when a `dataSource` was also set, so a consumer-fetched page was sliced again (page 2
40
+ rendered empty) and server results were re-filtered and re-sorted with client rules.
41
+
42
+ ---
43
+
44
+ ## Parameters
45
+
46
+ ```ts
47
+ interface UseGridRowPipelineParams<R extends GridRowModel> {
48
+ effectiveRows: R[]; // rows after dataSource / prop merge
49
+ activeHierarchyHandlers: { getVisibleRows: () => R[] } | null;
50
+ filterMode: 'client' | 'server';
51
+ filterModel: GridFilterModel;
52
+ sortModel: GridSortItem[];
53
+ sortingMode: 'client' | 'server';
54
+ pagination: boolean; // must be true to enable slicing
55
+ paginationMode: 'client' | 'server' | 'infinite';
56
+ effectivePaginationModel: GridPaginationModel;
57
+ pinnedRows?: GridRowPinning; // { top: GridRowId[], bottom: GridRowId[] }
58
+ getRowId?: (row: R) => GridRowId; // resolves ids for pinnedRows; rows are not required to carry `id`
59
+ columnLookup?: GridColumnLookup; // from useGridColumnLookup(activeColumns, columnVisibilityModel)
60
+ }
61
+ ```
62
+
63
+ `columnLookup` (built by `useGridColumnLookup`, `lib/utils/columnLookup.ts`) gives `filterRows` / `sortRows` the column definitions: cells are read through `valueGetter` (`getCellValue` in `lib/utils/values.ts`), the column `type` selects numeric / date comparison, and the quick filter searches only the visible, filterable columns. `DataGrid` passes the same lookup to `useTreeData` and `useRowGrouping`, which compile the filter once per pass with `createRowFilter` and sort with `compareRowsBySortModel` / `compareValues`. The lookup's identity changes only when the columns or the set of hidden columns change, so an inline `columnVisibilityModel` object does not re-run filter and sort.
64
+
65
+ > **`GridRowPinning` vs `GridPinnedRows<R>`** — `GridRowPinning` (used here) holds `top: GridRowId[]`; `GridPinnedRows<R>` holds `top: R[]`. These are distinct types. `getPinnedRowGroups` expects `GridRowPinning`.
66
+
67
+ ---
68
+
69
+ ## Returns
70
+
71
+ ```ts
72
+ interface GridRowPipelineResult<R extends GridRowModel> {
73
+ filteredRows: R[];
74
+ dataRows: R[]; // filtered data rows, independent of hierarchy expansion
75
+ pinnedTopRows: R[];
76
+ unpinnedRows: R[];
77
+ pinnedBottomRows: R[];
78
+ sortedUnpinnedRows: R[];
79
+ paginatedUnpinnedRows: R[];
80
+ currentPage: number; // the page actually sliced (clamped to the last page)
81
+ allRenderableRows: R[]; // the rows the grid view and the list view render
82
+ }
83
+ ```
84
+
85
+ All intermediate results are returned so features like the row count badge or aggregation can read them without re-deriving them. The client pager counts `sortedUnpinnedRows` (filtered, pinned rows excluded, since pinned rows show on every page); select-all acts on `dataRows`.
86
+
87
+ ---
88
+
89
+ ## Page clamping
90
+
91
+ When the grid pages client-side, the slice uses `currentPage`: `effectivePaginationModel.page` clamped to `0 … pageCount - 1`, where `pageCount = ceil(sortedUnpinnedRows.length / pageSize)` (at least 1). A page size below 1 is treated as 1 (`lib/utils/pagination`). So when the rows shrink (new `rows`, a filter, collapsed tree nodes) and the requested page is past the end, the last page is shown instead of an empty body. Under server pagination (`paginationMode="server"`) `currentPage` is the requested page, because the server owns the page count.
92
+
93
+ `DataGrid` passes `currentPage` to the pager and calls `useGridPageCorrection`, which reports the corrected model through `onPaginationModelChange` (and stores it when uncontrolled) once rows are present and nothing is loading. A restored page is therefore not thrown away before the rows arrive.
94
+
95
+ ---
96
+
97
+ ## Hierarchy mode shortcut
98
+
99
+ When `activeHierarchyHandlers` is set (tree data or row grouping), the hook delegates row ordering entirely to the hierarchy controller. Pinning and client-side sort/filter are bypassed: `getVisibleRows()` becomes `filteredRows` and `sortedUnpinnedRows`. Pagination still applies, so `allRenderableRows` is the current page of the visible hierarchy, the same rows the grid view renders.
100
+
101
+ ---
102
+
103
+ ## Infinite scroll loading rows
104
+
105
+ `allRenderableRows` holds only real rows. While an infinite-scroll page loads, `GridVirtualRows` draws the placeholder rows itself (from `dataSourceLoading`) and `useLayout` adds their height, so the placeholders never reach `renderCell`, the list view, keyboard navigation, selection or spanning. (Before v3.0 the hook appended `{ id: '__skeleton_N__', _isSkeleton: true }` objects to `allRenderableRows`.)
106
+
107
+ ---
108
+
109
+ ## Relationship to DataGrid
110
+
111
+ `DataGrid.tsx` calls this hook and destructures `allRenderableRows` for the viewport, `filteredRows` for the row count display, and `pinnedTopRows` / `pinnedBottomRows` for the sticky row sections.
112
+
113
+ ---
114
+
115
+ ## 🔗 Related
116
+ - [useGridControlledState](use-grid-controlled-state.md)
117
+ - [useGridVirtualization](use-grid-virtualization.md)
118
+ - [Filtering](../features/filtering.md)
119
+ - [Sorting & Pagination](../features/sorting-pagination.md)
120
+ - [Tree Data & Grouping](../features/tree-data-grouping.md)