@mui/x-data-grid 7.25.0 → 7.27.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 (132) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/DataGrid/DataGrid.js +6 -0
  3. package/DataGrid/useDataGridComponent.js +3 -3
  4. package/components/GridRow.js +8 -2
  5. package/components/GridScrollArea.d.ts +5 -2
  6. package/components/GridScrollArea.js +32 -25
  7. package/components/GridSkeletonLoadingOverlay.js +2 -1
  8. package/components/containers/GridRoot.js +11 -9
  9. package/components/containers/GridRootStyles.js +3 -3
  10. package/components/virtualization/GridMainContainer.d.ts +2 -2
  11. package/components/virtualization/GridMainContainer.js +1 -1
  12. package/components/virtualization/GridVirtualScroller.js +21 -14
  13. package/constants/dataGridPropsDefaultValues.js +1 -0
  14. package/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.d.ts +1 -1
  15. package/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.js +6 -4
  16. package/hooks/core/useGridStateInitialization.js +3 -2
  17. package/hooks/features/clipboard/useGridClipboard.js +1 -1
  18. package/hooks/features/columnHeaders/useGridColumnHeaders.js +10 -14
  19. package/hooks/features/columns/gridColumnsSelector.d.ts +0 -5
  20. package/hooks/features/columns/gridColumnsSelector.js +0 -12
  21. package/hooks/features/dimensions/gridDimensionsSelectors.d.ts +16 -0
  22. package/hooks/features/dimensions/gridDimensionsSelectors.js +26 -1
  23. package/hooks/features/dimensions/index.d.ts +1 -1
  24. package/hooks/features/dimensions/index.js +1 -2
  25. package/hooks/features/dimensions/useGridDimensions.js +97 -70
  26. package/hooks/features/editing/gridEditingSelectors.d.ts +5 -1
  27. package/hooks/features/editing/gridEditingSelectors.js +6 -1
  28. package/hooks/features/editing/useGridRowEditing.js +4 -4
  29. package/hooks/features/filter/gridFilterState.d.ts +5 -0
  30. package/hooks/features/filter/gridFilterState.js +5 -0
  31. package/hooks/features/filter/useGridFilter.js +6 -13
  32. package/hooks/features/pagination/useGridPagination.js +1 -1
  33. package/hooks/features/pagination/useGridPaginationModel.d.ts +1 -1
  34. package/hooks/features/pagination/useGridPaginationModel.js +44 -0
  35. package/hooks/features/rowSelection/useGridRowSelection.js +1 -1
  36. package/hooks/features/rowSelection/utils.js +1 -1
  37. package/hooks/features/rows/gridRowsMetaState.d.ts +8 -0
  38. package/hooks/features/rows/gridRowsUtils.d.ts +0 -4
  39. package/hooks/features/rows/gridRowsUtils.js +0 -16
  40. package/hooks/features/rows/useGridRowsMeta.js +33 -17
  41. package/hooks/features/sorting/gridSortingSelector.js +10 -9
  42. package/hooks/features/virtualization/useGridVirtualScroller.d.ts +6 -0
  43. package/hooks/features/virtualization/useGridVirtualScroller.js +43 -27
  44. package/hooks/utils/useGridNativeEventListener.d.ts +0 -1
  45. package/hooks/utils/useGridNativeEventListener.js +12 -22
  46. package/hooks/utils/useGridSelector.d.ts +8 -1
  47. package/hooks/utils/useGridSelector.js +42 -8
  48. package/hooks/utils/useIsSSR.d.ts +1 -0
  49. package/hooks/utils/useIsSSR.js +5 -0
  50. package/index.js +1 -1
  51. package/internals/index.d.ts +2 -1
  52. package/internals/index.js +2 -1
  53. package/locales/plPL.js +31 -35
  54. package/locales/ukUA.js +9 -10
  55. package/models/api/gridStateApi.d.ts +1 -0
  56. package/models/events/gridEventLookup.d.ts +6 -0
  57. package/models/props/DataGridProps.d.ts +6 -0
  58. package/modern/DataGrid/DataGrid.js +6 -0
  59. package/modern/DataGrid/useDataGridComponent.js +3 -3
  60. package/modern/components/GridRow.js +8 -2
  61. package/modern/components/GridScrollArea.js +32 -25
  62. package/modern/components/GridSkeletonLoadingOverlay.js +2 -1
  63. package/modern/components/containers/GridRoot.js +11 -9
  64. package/modern/components/containers/GridRootStyles.js +3 -3
  65. package/modern/components/virtualization/GridMainContainer.js +1 -1
  66. package/modern/components/virtualization/GridVirtualScroller.js +21 -14
  67. package/modern/constants/dataGridPropsDefaultValues.js +1 -0
  68. package/modern/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.js +6 -4
  69. package/modern/hooks/core/useGridStateInitialization.js +3 -2
  70. package/modern/hooks/features/clipboard/useGridClipboard.js +1 -1
  71. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +10 -14
  72. package/modern/hooks/features/columns/gridColumnsSelector.js +0 -12
  73. package/modern/hooks/features/dimensions/gridDimensionsSelectors.js +26 -1
  74. package/modern/hooks/features/dimensions/index.js +1 -2
  75. package/modern/hooks/features/dimensions/useGridDimensions.js +97 -70
  76. package/modern/hooks/features/editing/gridEditingSelectors.js +6 -1
  77. package/modern/hooks/features/editing/useGridRowEditing.js +4 -4
  78. package/modern/hooks/features/filter/gridFilterState.js +5 -0
  79. package/modern/hooks/features/filter/useGridFilter.js +6 -13
  80. package/modern/hooks/features/pagination/useGridPagination.js +1 -1
  81. package/modern/hooks/features/pagination/useGridPaginationModel.js +44 -0
  82. package/modern/hooks/features/rowSelection/useGridRowSelection.js +1 -1
  83. package/modern/hooks/features/rowSelection/utils.js +1 -1
  84. package/modern/hooks/features/rows/gridRowsUtils.js +0 -16
  85. package/modern/hooks/features/rows/useGridRowsMeta.js +33 -17
  86. package/modern/hooks/features/sorting/gridSortingSelector.js +10 -9
  87. package/modern/hooks/features/virtualization/useGridVirtualScroller.js +43 -27
  88. package/modern/hooks/utils/useGridNativeEventListener.js +12 -22
  89. package/modern/hooks/utils/useGridSelector.js +42 -8
  90. package/modern/hooks/utils/useIsSSR.js +5 -0
  91. package/modern/index.js +1 -1
  92. package/modern/internals/index.js +2 -1
  93. package/modern/locales/plPL.js +31 -35
  94. package/modern/locales/ukUA.js +9 -10
  95. package/node/DataGrid/DataGrid.js +6 -0
  96. package/node/DataGrid/useDataGridComponent.js +3 -3
  97. package/node/components/GridRow.js +7 -2
  98. package/node/components/GridScrollArea.js +31 -24
  99. package/node/components/GridSkeletonLoadingOverlay.js +2 -1
  100. package/node/components/containers/GridRoot.js +10 -8
  101. package/node/components/containers/GridRootStyles.js +3 -3
  102. package/node/components/virtualization/GridMainContainer.js +1 -1
  103. package/node/components/virtualization/GridVirtualScroller.js +21 -14
  104. package/node/constants/dataGridPropsDefaultValues.js +1 -0
  105. package/node/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.js +6 -4
  106. package/node/hooks/core/useGridStateInitialization.js +3 -2
  107. package/node/hooks/features/clipboard/useGridClipboard.js +1 -1
  108. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +10 -14
  109. package/node/hooks/features/columns/gridColumnsSelector.js +1 -13
  110. package/node/hooks/features/dimensions/gridDimensionsSelectors.js +38 -2
  111. package/node/hooks/features/dimensions/index.js +13 -11
  112. package/node/hooks/features/dimensions/useGridDimensions.js +94 -67
  113. package/node/hooks/features/editing/gridEditingSelectors.js +5 -1
  114. package/node/hooks/features/editing/useGridRowEditing.js +4 -4
  115. package/node/hooks/features/filter/gridFilterState.js +6 -1
  116. package/node/hooks/features/filter/useGridFilter.js +5 -12
  117. package/node/hooks/features/pagination/useGridPagination.js +1 -1
  118. package/node/hooks/features/pagination/useGridPaginationModel.js +44 -0
  119. package/node/hooks/features/rowSelection/useGridRowSelection.js +1 -1
  120. package/node/hooks/features/rowSelection/utils.js +1 -1
  121. package/node/hooks/features/rows/gridRowsUtils.js +0 -17
  122. package/node/hooks/features/rows/useGridRowsMeta.js +31 -15
  123. package/node/hooks/features/sorting/gridSortingSelector.js +10 -9
  124. package/node/hooks/features/virtualization/useGridVirtualScroller.js +42 -26
  125. package/node/hooks/utils/useGridNativeEventListener.js +12 -23
  126. package/node/hooks/utils/useGridSelector.js +42 -8
  127. package/node/hooks/utils/useIsSSR.js +12 -0
  128. package/node/index.js +1 -1
  129. package/node/internals/index.js +20 -7
  130. package/node/locales/plPL.js +31 -35
  131. package/node/locales/ukUA.js +9 -10
  132. package/package.json +3 -2
@@ -1,7 +1,9 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
+ import { gridFilterModelSelector, gridFilterActiveItemsSelector } from "../filter/gridFilterSelector.js";
3
4
  import { gridDensityFactorSelector } from "../density/index.js";
4
5
  import { useGridLogger, useGridSelector, useGridApiMethod, useGridApiEventHandler } from "../../utils/index.js";
6
+ import { isDeepEqual, runIf } from "../../../utils/utils.js";
5
7
  import { useGridRegisterPipeProcessor } from "../../core/pipeProcessing/index.js";
6
8
  import { gridPageCountSelector, gridPaginationModelSelector } from "./gridPaginationSelector.js";
7
9
  import { getPageCount, defaultPageSize, throwIfPageSizeExceedsTheLimit, getDefaultGridPaginationModel, getValidPage } from "./gridPaginationUtils.js";
@@ -31,6 +33,7 @@ export const getDerivedPaginationModel = (paginationState, signature, pagination
31
33
  export const useGridPaginationModel = (apiRef, props) => {
32
34
  const logger = useGridLogger(apiRef, 'useGridPaginationModel');
33
35
  const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
36
+ const previousFilterModel = React.useRef(gridFilterModelSelector(apiRef));
34
37
  const rowHeight = Math.floor(props.rowHeight * densityFactor);
35
38
  apiRef.current.registerControlState({
36
39
  stateId: 'paginationModel',
@@ -143,14 +146,55 @@ export const useGridPaginationModel = (apiRef, props) => {
143
146
  return;
144
147
  }
145
148
  const paginationModel = gridPaginationModelSelector(apiRef);
149
+ if (paginationModel.page === 0) {
150
+ return;
151
+ }
146
152
  const pageCount = gridPageCountSelector(apiRef);
147
153
  if (paginationModel.page > pageCount - 1) {
148
154
  apiRef.current.setPage(Math.max(0, pageCount - 1));
149
155
  }
150
156
  }, [apiRef]);
157
+
158
+ /**
159
+ * Goes to the first row of the grid
160
+ */
161
+ const navigateToStart = React.useCallback(() => {
162
+ const paginationModel = gridPaginationModelSelector(apiRef);
163
+ if (paginationModel.page !== 0) {
164
+ apiRef.current.setPage(0);
165
+ }
166
+
167
+ // If the page was not changed it might be needed to scroll to the top
168
+ const scrollPosition = apiRef.current.getScrollPosition();
169
+ if (scrollPosition.top !== 0) {
170
+ apiRef.current.scroll({
171
+ top: 0
172
+ });
173
+ }
174
+ }, [apiRef]);
175
+
176
+ /**
177
+ * Resets the page only if the active items or quick filter has changed from the last time.
178
+ * This is to avoid resetting the page when the filter model is changed
179
+ * because of and update of the operator from an item that does not have the value
180
+ * or reseting when the filter panel is just opened
181
+ */
182
+ const handleFilterModelChange = React.useCallback(filterModel => {
183
+ const currentActiveFilters = _extends({}, filterModel, {
184
+ // replace items with the active items
185
+ items: gridFilterActiveItemsSelector(apiRef)
186
+ });
187
+ if (isDeepEqual(currentActiveFilters, previousFilterModel.current)) {
188
+ return;
189
+ }
190
+ previousFilterModel.current = currentActiveFilters;
191
+ navigateToStart();
192
+ }, [apiRef, navigateToStart]);
151
193
  useGridApiEventHandler(apiRef, 'viewportInnerSizeChange', handleUpdateAutoPageSize);
152
194
  useGridApiEventHandler(apiRef, 'paginationModelChange', handlePaginationModelChange);
153
195
  useGridApiEventHandler(apiRef, 'rowCountChange', handleRowCountChange);
196
+ useGridApiEventHandler(apiRef, 'sortModelChange', runIf(props.resetPageOnSortFilter, navigateToStart));
197
+ useGridApiEventHandler(apiRef, 'filterModelChange', runIf(props.resetPageOnSortFilter, handleFilterModelChange));
154
198
 
155
199
  /**
156
200
  * EFFECTS
@@ -258,7 +258,7 @@ export const useGridRowSelection = (apiRef, props) => {
258
258
  if (props.filterMode === 'server') {
259
259
  return !rowsLookup[id];
260
260
  }
261
- return filteredRowsLookup[id] !== true;
261
+ return !rowsLookup[id] || filteredRowsLookup[id] === false;
262
262
  };
263
263
  let hasChanged = false;
264
264
  currentSelection.forEach(id => {
@@ -92,7 +92,7 @@ const getFilteredRowNodeSiblings = (tree, filteredRows, id) => {
92
92
  return [];
93
93
  }
94
94
  const parentNode = tree[parent];
95
- return parentNode.children.filter(childId => childId !== id && filteredRows[childId]);
95
+ return parentNode.children.filter(childId => childId !== id && filteredRows[childId] !== false);
96
96
  };
97
97
  export const findRowsToSelect = (apiRef, tree, selectedRow, autoSelectDescendants, autoSelectParents, addRow) => {
98
98
  const filteredRows = gridFilteredRowsLookupSelector(apiRef);
@@ -10,4 +10,12 @@ export interface GridRowsMetaState {
10
10
  * The grid rows positions.
11
11
  */
12
12
  positions: number[];
13
+ /**
14
+ * The total height of the pinned top rows.
15
+ */
16
+ pinnedTopRowsTotalHeight: number;
17
+ /**
18
+ * The total height of the pinned bottom rows.
19
+ */
20
+ pinnedBottomRowsTotalHeight: number;
13
21
  }
@@ -33,10 +33,6 @@ export declare const updateCacheWithNewRows: ({ previousCache, getRowId, updates
33
33
  updates: GridRowModelUpdate[];
34
34
  groupKeys?: string[];
35
35
  }) => GridRowsInternalCache;
36
- export declare function calculatePinnedRowsHeight(apiRef: RefObject<GridApiCommunity>): {
37
- top: number;
38
- bottom: number;
39
- };
40
36
  export declare const minimalContentHeight = "var(--DataGrid-overlayHeight, calc(var(--height) * 2))";
41
37
  export declare function computeRowsUpdates(apiRef: RefObject<GridApiCommunity>, updates: GridRowModelUpdate[], getRowId: DataGridProcessedProps['getRowId']): GridRowModelUpdate[];
42
38
  export declare const getValidRowHeight: (rowHeightProp: any, defaultRowHeight: number, warningMessage: string) => number;
@@ -1,5 +1,4 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
- import { gridPinnedRowsSelector } from "./gridRowsSelector.js";
3
2
  export const GRID_ROOT_GROUP_ID = `auto-generated-group-node-root`;
4
3
  export const GRID_ID_AUTOGENERATED = Symbol('mui.id_autogenerated');
5
4
  export const buildRootGroup = () => ({
@@ -269,21 +268,6 @@ export const updateCacheWithNewRows = ({
269
268
  rowCountPropBeforePartialUpdates: previousCache.rowCountPropBeforePartialUpdates
270
269
  };
271
270
  };
272
- export function calculatePinnedRowsHeight(apiRef) {
273
- const pinnedRows = gridPinnedRowsSelector(apiRef);
274
- const topPinnedRowsHeight = pinnedRows?.top?.reduce((acc, value) => {
275
- acc += apiRef.current.unstable_getRowHeight(value.id);
276
- return acc;
277
- }, 0) || 0;
278
- const bottomPinnedRowsHeight = pinnedRows?.bottom?.reduce((acc, value) => {
279
- acc += apiRef.current.unstable_getRowHeight(value.id);
280
- return acc;
281
- }, 0) || 0;
282
- return {
283
- top: topPinnedRowsHeight,
284
- bottom: bottomPinnedRowsHeight
285
- };
286
- }
287
271
  export const minimalContentHeight = 'var(--DataGrid-overlayHeight, calc(var(--height) * 2))';
288
272
  export function computeRowsUpdates(apiRef, updates, getRowId) {
289
273
  const nonPinnedRowsUpdates = [];
@@ -8,12 +8,10 @@ import { eslintUseValue } from "../../../utils/utils.js";
8
8
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
9
9
  import { useGridSelector } from "../../utils/useGridSelector.js";
10
10
  import { gridDensityFactorSelector } from "../density/densitySelector.js";
11
- import { gridFilterModelSelector } from "../filter/gridFilterSelector.js";
12
11
  import { gridPaginationSelector } from "../pagination/gridPaginationSelector.js";
13
- import { gridSortModelSelector } from "../sorting/gridSortingSelector.js";
14
12
  import { useGridRegisterPipeApplier } from "../../core/pipeProcessing/index.js";
15
- import { gridPinnedRowsSelector } from "./gridRowsSelector.js";
16
- import { gridDimensionsSelector } from "../dimensions/gridDimensionsSelectors.js";
13
+ import { gridPinnedRowsSelector, gridRowCountSelector } from "./gridRowsSelector.js";
14
+ import { gridDimensionsSelector, gridRowHeightSelector } from "../dimensions/gridDimensionsSelectors.js";
17
15
  import { getValidRowHeight, getRowHeightWarning } from "./gridRowsUtils.js";
18
16
  /* eslint-disable no-underscore-dangle */
19
17
 
@@ -21,10 +19,18 @@ export const rowsMetaStateInitializer = (state, props, apiRef) => {
21
19
  apiRef.current.caches.rowsMeta = {
22
20
  heights: new Map()
23
21
  };
22
+ const baseRowHeight = gridRowHeightSelector(apiRef.current.state);
23
+ const dataRowCount = gridRowCountSelector(apiRef);
24
+ const pagination = gridPaginationSelector(apiRef.current.state);
25
+ const rowCount = Math.min(pagination.enabled ? pagination.paginationModel.pageSize : dataRowCount, dataRowCount);
24
26
  return _extends({}, state, {
25
27
  rowsMeta: {
26
- currentPageTotalHeight: 0,
27
- positions: []
28
+ currentPageTotalHeight: rowCount * baseRowHeight,
29
+ positions: Array.from({
30
+ length: rowCount
31
+ }, (_, i) => i * baseRowHeight),
32
+ pinnedTopRowsTotalHeight: 0,
33
+ pinnedBottomRowsTotalHeight: 0
28
34
  }
29
35
  });
30
36
  };
@@ -44,12 +50,9 @@ export const useGridRowsMeta = (apiRef, props) => {
44
50
  const hasRowWithAutoHeight = React.useRef(false);
45
51
  const isHeightMetaValid = React.useRef(false);
46
52
  const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
47
- const filterModel = useGridSelector(apiRef, gridFilterModelSelector);
48
- const paginationState = useGridSelector(apiRef, gridPaginationSelector);
49
- const sortModel = useGridSelector(apiRef, gridSortModelSelector);
50
53
  const currentPage = useGridVisibleRows(apiRef, props);
51
54
  const pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
52
- const rowHeight = useGridSelector(apiRef, () => gridDimensionsSelector(apiRef.current.state).rowHeight);
55
+ const rowHeight = useGridSelector(apiRef, gridRowHeightSelector);
53
56
  const getRowHeightEntry = rowId => {
54
57
  let entry = heightCache.get(rowId);
55
58
  if (entry === undefined) {
@@ -114,8 +117,14 @@ export const useGridRowsMeta = (apiRef, props) => {
114
117
  }, [apiRef, currentPage.rows, getRowHeightProp, getEstimatedRowHeight, rowHeight, getRowSpacing, densityFactor]);
115
118
  const hydrateRowsMeta = React.useCallback(() => {
116
119
  hasRowWithAutoHeight.current = false;
117
- pinnedRows.top.forEach(processHeightEntry);
118
- pinnedRows.bottom.forEach(processHeightEntry);
120
+ const pinnedTopRowsTotalHeight = pinnedRows.top.reduce((acc, row) => {
121
+ const entry = processHeightEntry(row);
122
+ return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
123
+ }, 0);
124
+ const pinnedBottomRowsTotalHeight = pinnedRows.bottom.reduce((acc, row) => {
125
+ const entry = processHeightEntry(row);
126
+ return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
127
+ }, 0);
119
128
  const positions = [];
120
129
  const currentPageTotalHeight = currentPage.rows.reduce((acc, row) => {
121
130
  positions.push(acc);
@@ -127,14 +136,21 @@ export const useGridRowsMeta = (apiRef, props) => {
127
136
  // No row has height=auto, so all rows are already measured
128
137
  lastMeasuredRowIndex.current = Infinity;
129
138
  }
139
+ const didHeightsChange = pinnedTopRowsTotalHeight !== apiRef.current.state.rowsMeta.pinnedTopRowsTotalHeight || pinnedBottomRowsTotalHeight !== apiRef.current.state.rowsMeta.pinnedBottomRowsTotalHeight || currentPageTotalHeight !== apiRef.current.state.rowsMeta.currentPageTotalHeight;
140
+ const rowsMeta = {
141
+ currentPageTotalHeight,
142
+ positions,
143
+ pinnedTopRowsTotalHeight,
144
+ pinnedBottomRowsTotalHeight
145
+ };
130
146
  apiRef.current.setState(state => {
131
147
  return _extends({}, state, {
132
- rowsMeta: {
133
- currentPageTotalHeight,
134
- positions
135
- }
148
+ rowsMeta
136
149
  });
137
150
  });
151
+ if (didHeightsChange) {
152
+ apiRef.current.updateDimensions();
153
+ }
138
154
  isHeightMetaValid.current = true;
139
155
  }, [apiRef, pinnedRows, currentPage.rows, processHeightEntry]);
140
156
  const getRowHeight = rowId => {
@@ -184,7 +200,7 @@ export const useGridRowsMeta = (apiRef, props) => {
184
200
  // Because of variable row height this is needed for the virtualization
185
201
  useEnhancedEffect(() => {
186
202
  hydrateRowsMeta();
187
- }, [filterModel, paginationState, sortModel, hydrateRowsMeta]);
203
+ }, [hydrateRowsMeta]);
188
204
  const rowsMetaApi = {
189
205
  unstable_getRowHeight: getRowHeight,
190
206
  unstable_setLastMeasuredRowIndex: setLastMeasuredRowIndex,
@@ -24,15 +24,16 @@ export const gridSortedRowEntriesSelector = createSelectorMemoized(gridSortedRow
24
24
  id,
25
25
  model
26
26
  });
27
- }
28
- const rowNode = rowTree[id];
29
- if (rowNode && isAutogeneratedRowNode(rowNode)) {
30
- acc.push({
31
- id,
32
- model: {
33
- [GRID_ID_AUTOGENERATED]: id
34
- }
35
- });
27
+ } else {
28
+ const rowNode = rowTree[id];
29
+ if (rowNode && isAutogeneratedRowNode(rowNode)) {
30
+ acc.push({
31
+ id,
32
+ model: {
33
+ [GRID_ID_AUTOGENERATED]: id
34
+ }
35
+ });
36
+ }
36
37
  }
37
38
  return acc;
38
39
  }, []));
@@ -47,6 +47,12 @@ export declare const useGridVirtualScroller: () => {
47
47
  left: number;
48
48
  }>;
49
49
  };
50
+ getScrollAreaProps: () => {
51
+ scrollPosition: React.RefObject<{
52
+ top: number;
53
+ left: number;
54
+ }>;
55
+ };
50
56
  };
51
57
  export declare function areRenderContextsEqual(context1: GridRenderContext, context2: GridRenderContext): boolean;
52
58
  export declare function computeOffsetLeft(columnPositions: number[], renderContext: GridColumnsRenderContext, pinnedLeftLength: number): number;
@@ -6,15 +6,15 @@ import useLazyRef from '@mui/utils/useLazyRef';
6
6
  import useTimeout from '@mui/utils/useTimeout';
7
7
  import { useRtl } from '@mui/system/RtlProvider';
8
8
  import reactMajor from '@mui/x-internals/reactMajor';
9
+ import { gridDimensionsSelector, gridColumnsTotalWidthSelector, gridContentHeightSelector, gridHasFillerSelector, gridRowHeightSelector, gridVerticalScrollbarWidthSelector } from "../dimensions/gridDimensionsSelectors.js";
9
10
  import { useGridPrivateApiContext } from "../../utils/useGridPrivateApiContext.js";
10
11
  import { useGridRootProps } from "../../utils/useGridRootProps.js";
11
12
  import { useGridSelector } from "../../utils/useGridSelector.js";
12
13
  import { useRunOnce } from "../../utils/useRunOnce.js";
13
14
  import { gridVisibleColumnDefinitionsSelector, gridVisiblePinnedColumnDefinitionsSelector, gridColumnPositionsSelector, gridHasColSpanSelector } from "../columns/gridColumnsSelector.js";
14
- import { gridDimensionsSelector } from "../dimensions/gridDimensionsSelectors.js";
15
15
  import { gridPinnedRowsSelector } from "../rows/gridRowsSelector.js";
16
16
  import { useGridVisibleRows, getVisibleRows } from "../../utils/useGridVisibleRows.js";
17
- import { useGridApiEventHandler } from "../../utils/index.js";
17
+ import { useGridApiOptionHandler } from "../../utils/index.js";
18
18
  import * as platform from "../../../utils/platform.js";
19
19
  import { clamp, range } from "../../../utils/utils.js";
20
20
  import { selectedIdsLookupSelector } from "../rowSelection/gridRowSelectionSelector.js";
@@ -57,8 +57,6 @@ export const useGridVirtualScroller = () => {
57
57
  const visibleColumns = useGridSelector(apiRef, () => listView ? [gridListColumnSelector(apiRef.current.state)] : gridVisibleColumnDefinitionsSelector(apiRef));
58
58
  const enabledForRows = useGridSelector(apiRef, gridVirtualizationRowEnabledSelector) && !isJSDOM;
59
59
  const enabledForColumns = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector) && !isJSDOM;
60
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
61
- const outerSize = dimensions.viewportOuterSize;
62
60
  const pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
63
61
  const pinnedColumnDefinitions = gridVisiblePinnedColumnDefinitionsSelector(apiRef);
64
62
  const pinnedColumns = listView ? EMPTY_PINNED_COLUMN_FIELDS : pinnedColumnDefinitions;
@@ -71,10 +69,14 @@ export const useGridVirtualScroller = () => {
71
69
  const scrollerRef = apiRef.current.virtualScrollerRef;
72
70
  const scrollbarVerticalRef = apiRef.current.virtualScrollbarVerticalRef;
73
71
  const scrollbarHorizontalRef = apiRef.current.virtualScrollbarHorizontalRef;
74
- const contentHeight = dimensions.contentSize.height;
75
- const columnsTotalWidth = dimensions.columnsTotalWidth;
76
72
  const hasColSpan = useGridSelector(apiRef, gridHasColSpanSelector);
77
73
  const isRenderContextReady = React.useRef(false);
74
+ const rowHeight = useGridSelector(apiRef, gridRowHeightSelector);
75
+ const contentHeight = useGridSelector(apiRef, gridContentHeightSelector);
76
+ const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector);
77
+ const needsHorizontalScrollbar = useGridSelector(apiRef, needsHorizontalScrollbarSelector);
78
+ const verticalScrollbarWidth = useGridSelector(apiRef, gridVerticalScrollbarWidthSelector);
79
+ const gridHasFiller = useGridSelector(apiRef, gridHasFillerSelector);
78
80
  const previousSize = React.useRef(null);
79
81
  const mainRefCallback = React.useCallback(node => {
80
82
  mainRef.current = node;
@@ -141,7 +143,7 @@ export const useGridVirtualScroller = () => {
141
143
  const focusedVirtualCell = useGridSelector(apiRef, gridFocusedVirtualCellSelector);
142
144
  const scrollTimeout = useTimeout();
143
145
  const frozenContext = React.useRef(undefined);
144
- const scrollCache = useLazyRef(() => createScrollCache(isRtl, rootProps.rowBufferPx, rootProps.columnBufferPx, dimensions.rowHeight * 15, MINIMUM_COLUMN_WIDTH * 6)).current;
146
+ const scrollCache = useLazyRef(() => createScrollCache(isRtl, rootProps.rowBufferPx, rootProps.columnBufferPx, rowHeight * 15, MINIMUM_COLUMN_WIDTH * 6)).current;
145
147
  const updateRenderContext = React.useCallback(nextRenderContext => {
146
148
  if (areRenderContextsEqual(nextRenderContext, apiRef.current.state.virtualization.renderContext)) {
147
149
  return;
@@ -156,19 +158,21 @@ export const useGridVirtualScroller = () => {
156
158
  });
157
159
 
158
160
  // The lazy-loading hook is listening to `renderedRowsIntervalChange`,
159
- // but only does something if the dimensions are also available.
160
- // So we wait until we have valid dimensions before publishing the first event.
161
- if (dimensions.isReady && didRowsIntervalChange) {
161
+ // but only does something if we already have a render context, because
162
+ // otherwise we would call an update directly on mount
163
+ const isReady = gridDimensionsSelector(apiRef.current.state).isReady;
164
+ if (isReady && didRowsIntervalChange) {
162
165
  previousRowContext.current = nextRenderContext;
163
166
  apiRef.current.publishEvent('renderedRowsIntervalChange', nextRenderContext);
164
167
  }
165
168
  previousContextScrollPosition.current = scrollPosition.current;
166
- }, [apiRef, dimensions.isReady]);
169
+ }, [apiRef]);
167
170
  const triggerUpdateRenderContext = useEventCallback(() => {
168
171
  const scroller = scrollerRef.current;
169
172
  if (!scroller) {
170
173
  return undefined;
171
174
  }
175
+ const dimensions = gridDimensionsSelector(apiRef.current.state);
172
176
  const maxScrollTop = Math.ceil(dimensions.minimumSize.height - dimensions.viewportOuterSize.height);
173
177
  const maxScrollLeft = Math.ceil(dimensions.minimumSize.width - dimensions.viewportInnerSize.width);
174
178
 
@@ -188,7 +192,7 @@ export const useGridVirtualScroller = () => {
188
192
  const columnScroll = Math.abs(scrollPosition.current.left - previousContextScrollPosition.current.left);
189
193
 
190
194
  // PERF: use the computed minimum column width instead of a static one
191
- const didCrossThreshold = rowScroll >= dimensions.rowHeight || columnScroll >= MINIMUM_COLUMN_WIDTH;
195
+ const didCrossThreshold = rowScroll >= rowHeight || columnScroll >= MINIMUM_COLUMN_WIDTH;
192
196
  const didChangeDirection = scrollCache.direction !== direction;
193
197
  const shouldUpdate = didCrossThreshold || didChangeDirection;
194
198
  if (!shouldUpdate) {
@@ -210,7 +214,7 @@ export const useGridVirtualScroller = () => {
210
214
  }
211
215
  }
212
216
  scrollCache.direction = direction;
213
- scrollCache.buffer = bufferForDirection(isRtl, direction, rootProps.rowBufferPx, rootProps.columnBufferPx, dimensions.rowHeight * 15, MINIMUM_COLUMN_WIDTH * 6);
217
+ scrollCache.buffer = bufferForDirection(isRtl, direction, rootProps.rowBufferPx, rootProps.columnBufferPx, rowHeight * 15, MINIMUM_COLUMN_WIDTH * 6);
214
218
  const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns);
215
219
  const nextRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache);
216
220
 
@@ -222,6 +226,10 @@ export const useGridVirtualScroller = () => {
222
226
  return nextRenderContext;
223
227
  });
224
228
  const forceUpdateRenderContext = () => {
229
+ // skip update if dimensions are not ready and virtualization is enabled
230
+ if (!gridDimensionsSelector(apiRef.current.state).isReady && (enabledForRows || enabledForColumns)) {
231
+ return;
232
+ }
225
233
  const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns);
226
234
  const nextRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache);
227
235
  // Reset the frozen context when the render context changes, see the illustration in https://github.com/mui/mui-x/pull/12353
@@ -250,7 +258,12 @@ export const useGridVirtualScroller = () => {
250
258
  if (!params.rows && !currentPage.range) {
251
259
  return [];
252
260
  }
253
- const baseRenderContext = params.renderContext ?? renderContext;
261
+ let baseRenderContext = renderContext;
262
+ if (params.renderContext) {
263
+ baseRenderContext = params.renderContext;
264
+ baseRenderContext.firstColumnIndex = renderContext.firstColumnIndex;
265
+ baseRenderContext.lastColumnIndex = renderContext.lastColumnIndex;
266
+ }
254
267
  const isLastSection = !hasBottomPinnedRows && params.position === undefined || hasBottomPinnedRows && params.position === 'bottom';
255
268
  const isPinnedSection = params.position !== undefined;
256
269
  let rowIndexOffset;
@@ -344,7 +357,7 @@ export const useGridVirtualScroller = () => {
344
357
  }
345
358
  }
346
359
  let currentRenderContext = baseRenderContext;
347
- if (!isPinnedSection && frozenContext.current && rowIndexInPage >= frozenContext.current.firstRowIndex && rowIndexInPage < frozenContext.current.lastRowIndex) {
360
+ if (frozenContext.current && rowIndexInPage >= frozenContext.current.firstRowIndex && rowIndexInPage < frozenContext.current.lastRowIndex) {
348
361
  currentRenderContext = frozenContext.current;
349
362
  }
350
363
  const isVirtualFocusRow = rowIndexInPage === virtualRowIndex;
@@ -359,7 +372,7 @@ export const useGridVirtualScroller = () => {
359
372
  index: rowIndex,
360
373
  selected: isSelected,
361
374
  offsetLeft: offsetLeft,
362
- columnsTotalWidth: dimensions.columnsTotalWidth,
375
+ columnsTotalWidth: columnsTotalWidth,
363
376
  rowHeight: baseRowHeight,
364
377
  pinnedColumns: pinnedColumns,
365
378
  visibleColumns: visibleColumns,
@@ -370,8 +383,8 @@ export const useGridVirtualScroller = () => {
370
383
  isLastVisible: isLastVisible,
371
384
  isNotVisible: isVirtualFocusRow,
372
385
  showBottomBorder: showBottomBorder,
373
- scrollbarWidth: dimensions.hasScrollY ? dimensions.scrollbarSize : 0,
374
- gridHasFiller: dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width
386
+ scrollbarWidth: verticalScrollbarWidth,
387
+ gridHasFiller: gridHasFiller
375
388
  }, rowProps), id));
376
389
  if (isVirtualFocusRow) {
377
390
  return;
@@ -388,7 +401,6 @@ export const useGridVirtualScroller = () => {
388
401
  });
389
402
  return rows;
390
403
  };
391
- const needsHorizontalScrollbar = outerSize.width && columnsTotalWidth > outerSize.width;
392
404
  const scrollerStyle = React.useMemo(() => ({
393
405
  overflowX: !needsHorizontalScrollbar || listView ? 'hidden' : undefined,
394
406
  overflowY: rootProps.autoHeight ? 'hidden' : undefined
@@ -424,14 +436,11 @@ export const useGridVirtualScroller = () => {
424
436
  scrollerRef.current.scrollLeft = 0;
425
437
  }
426
438
  }, [listView, scrollerRef]);
427
- useRunOnce(outerSize.width !== 0, () => {
428
- const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns);
429
- const initialRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache);
430
- updateRenderContext(initialRenderContext);
439
+ useRunOnce(renderContext !== EMPTY_RENDER_CONTEXT, () => {
431
440
  apiRef.current.publishEvent('scrollPositionChange', {
432
441
  top: scrollPosition.current.top,
433
442
  left: scrollPosition.current.left,
434
- renderContext: initialRenderContext
443
+ renderContext
435
444
  });
436
445
  isRenderContextReady.current = true;
437
446
  if (rootProps.initialState?.scroll && scrollerRef.current) {
@@ -484,9 +493,9 @@ export const useGridVirtualScroller = () => {
484
493
  apiRef.current.register('private', {
485
494
  updateRenderContext: forceUpdateRenderContext
486
495
  });
487
- useGridApiEventHandler(apiRef, 'columnsChange', forceUpdateRenderContext);
488
- useGridApiEventHandler(apiRef, 'filteredRowsSet', forceUpdateRenderContext);
489
- useGridApiEventHandler(apiRef, 'rowExpansionChange', forceUpdateRenderContext);
496
+ useGridApiOptionHandler(apiRef, 'sortedRowsSet', forceUpdateRenderContext);
497
+ useGridApiOptionHandler(apiRef, 'paginationModelChange', forceUpdateRenderContext);
498
+ useGridApiOptionHandler(apiRef, 'columnsChange', forceUpdateRenderContext);
490
499
  return {
491
500
  renderContext,
492
501
  setPanels,
@@ -522,9 +531,16 @@ export const useGridVirtualScroller = () => {
522
531
  ref: scrollbarHorizontalRef,
523
532
  role: 'presentation',
524
533
  scrollPosition
534
+ }),
535
+ getScrollAreaProps: () => ({
536
+ scrollPosition
525
537
  })
526
538
  };
527
539
  };
540
+ // dimension selectors
541
+ function needsHorizontalScrollbarSelector(state) {
542
+ return state.dimensions.viewportOuterSize.width > 0 && state.dimensions.columnsTotalWidth > state.dimensions.viewportOuterSize.width;
543
+ }
528
544
  function inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns) {
529
545
  const dimensions = gridDimensionsSelector(apiRef.current.state);
530
546
  const currentPage = getVisibleRows(apiRef, rootProps);
@@ -1,4 +1,3 @@
1
- import * as React from 'react';
2
1
  import { RefObject } from '@mui/x-internals/types';
3
2
  import { GridPrivateApiCommon } from '../../models/api/gridApiCommon';
4
3
  export declare const useGridNativeEventListener: <PrivateApi extends GridPrivateApiCommon, K extends keyof HTMLElementEventMap>(apiRef: RefObject<PrivateApi>, ref: React.RefObject<HTMLDivElement | null> | (() => HTMLElement | undefined | null), eventName: K, handler?: (event: HTMLElementEventMap[K]) => any, options?: AddEventListenerOptions) => void;
@@ -1,27 +1,17 @@
1
- import * as React from 'react';
2
- import { isFunction } from "../../utils/utils.js";
3
1
  import { useGridLogger } from "./useGridLogger.js";
2
+ import { useGridApiOptionHandler } from "./useGridApiEventHandler.js";
4
3
  export const useGridNativeEventListener = (apiRef, ref, eventName, handler, options) => {
5
4
  const logger = useGridLogger(apiRef, 'useNativeEventListener');
6
- const [added, setAdded] = React.useState(false);
7
- const handlerRef = React.useRef(handler);
8
- const targetElement = isFunction(ref) ? ref() : ref?.current ?? null;
9
- const wrapHandler = React.useCallback(event => {
10
- return handlerRef.current && handlerRef.current(event);
11
- }, []);
12
- React.useEffect(() => {
13
- handlerRef.current = handler;
14
- }, [handler]);
15
- React.useEffect(() => {
16
- if (targetElement && eventName && !added) {
17
- logger.debug(`Binding native ${eventName} event`);
18
- targetElement.addEventListener(eventName, wrapHandler, options);
19
- setAdded(true);
20
- const unsubscribe = () => {
21
- logger.debug(`Clearing native ${eventName} event`);
22
- targetElement.removeEventListener(eventName, wrapHandler, options);
23
- };
24
- apiRef.current.subscribeEvent('unmount', unsubscribe);
5
+ useGridApiOptionHandler(apiRef, 'rootMount', () => {
6
+ const targetElement = typeof ref === 'function' ? ref() : ref.current;
7
+ if (!targetElement || !eventName || !handler) {
8
+ return undefined;
25
9
  }
26
- }, [targetElement, wrapHandler, eventName, added, logger, options, apiRef]);
10
+ logger.debug(`Binding native ${eventName} event`);
11
+ targetElement.addEventListener(eventName, handler, options);
12
+ return () => {
13
+ logger.debug(`Clearing native ${eventName} event`);
14
+ targetElement.removeEventListener(eventName, handler, options);
15
+ };
16
+ });
27
17
  };
@@ -5,6 +5,13 @@ import type { OutputSelector, OutputSelectorV8 } from '../../utils/createSelecto
5
5
  type Selector<Api extends GridApiCommon, Args, T> = ((state: Api['state']) => T) | OutputSelectorV8<Api['state'], Args, T>;
6
6
  export declare const objectShallowCompare: typeof fastObjectShallowCompare;
7
7
  export declare const argsEqual: (prev: any, curr: any) => boolean;
8
+ type Refs<T> = {
9
+ state: T;
10
+ equals: <U = T>(a: U, b: U) => boolean;
11
+ selector: Selector<any, any, T>;
12
+ args: any;
13
+ subscription: undefined | (() => void);
14
+ };
8
15
  export declare const useGridSelector: <Api extends GridApiCommon, T>(apiRef: RefObject<Api>, selector: ((state: Api["state"]) => T) | OutputSelector<Api["state"], T>, equals?: (a: T, b: T) => boolean) => T;
9
- export declare const useGridSelectorV8: <Api extends GridApiCommon, Args, T>(apiRef: RefObject<Api>, selector: Selector<Api, Args, T>, args?: Args, equals?: <U = T>(a: U, b: U) => boolean) => T;
16
+ export declare const useGridSelectorV8: <Api extends GridApiCommon, Args, T>(apiRef: RefObject<Api>, selector: Selector<Api, Args, T>, args?: Args, equals?: Refs<T>["equals"]) => T;
10
17
  export {};
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { fastObjectShallowCompare } from '@mui/x-internals/fastObjectShallowCompare';
3
3
  import { warnOnce } from '@mui/x-internals/warning';
4
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
5
  import { useLazyRef } from "./useLazyRef.js";
5
- import { useOnMount } from "./useOnMount.js";
6
6
  function isOutputSelector(selector) {
7
7
  return selector.acceptsApiRef;
8
8
  }
@@ -42,8 +42,10 @@ const createRefs = () => ({
42
42
  state: null,
43
43
  equals: null,
44
44
  selector: null,
45
- args: null
45
+ args: undefined
46
46
  });
47
+ const EMPTY = [];
48
+ const emptyGetSnapshot = () => null;
47
49
 
48
50
  // TODO v8: Remove this function
49
51
  export const useGridSelector = (apiRef, selector, equals = defaultCompare) => {
@@ -60,15 +62,31 @@ export const useGridSelector = (apiRef, selector, equals = defaultCompare) => {
60
62
  refs.current.state = state;
61
63
  refs.current.equals = equals;
62
64
  refs.current.selector = selector;
63
- useOnMount(() => {
64
- return apiRef.current.store.subscribe(() => {
65
+ const subscribe = React.useCallback(() => {
66
+ if (refs.current.subscription) {
67
+ return null;
68
+ }
69
+ refs.current.subscription = apiRef.current.store.subscribe(() => {
65
70
  const newState = applySelector(apiRef, refs.current.selector);
66
71
  if (!refs.current.equals(refs.current.state, newState)) {
67
72
  refs.current.state = newState;
68
73
  setState(newState);
69
74
  }
70
75
  });
71
- });
76
+ return null;
77
+ },
78
+ // eslint-disable-next-line react-hooks/exhaustive-deps
79
+ EMPTY);
80
+ const unsubscribe = React.useCallback(() => {
81
+ return () => {
82
+ if (refs.current.subscription) {
83
+ refs.current.subscription();
84
+ refs.current.subscription = undefined;
85
+ }
86
+ };
87
+ // eslint-disable-next-line react-hooks/exhaustive-deps
88
+ }, EMPTY);
89
+ useSyncExternalStore(unsubscribe, subscribe, emptyGetSnapshot);
72
90
  return state;
73
91
  };
74
92
 
@@ -96,14 +114,30 @@ export const useGridSelectorV8 = (apiRef, selector, args = undefined, equals = d
96
114
  setState(newState);
97
115
  }
98
116
  }
99
- useOnMount(() => {
100
- return apiRef.current.store.subscribe(() => {
117
+ const subscribe = React.useCallback(() => {
118
+ if (refs.current.subscription) {
119
+ return null;
120
+ }
121
+ refs.current.subscription = apiRef.current.store.subscribe(() => {
101
122
  const newState = applySelectorV8(apiRef, refs.current.selector, refs.current.args, apiRef.current.instanceId);
102
123
  if (!refs.current.equals(refs.current.state, newState)) {
103
124
  refs.current.state = newState;
104
125
  setState(newState);
105
126
  }
106
127
  });
107
- });
128
+ return null;
129
+ },
130
+ // eslint-disable-next-line react-hooks/exhaustive-deps
131
+ EMPTY);
132
+ const unsubscribe = React.useCallback(() => {
133
+ return () => {
134
+ if (refs.current.subscription) {
135
+ refs.current.subscription();
136
+ refs.current.subscription = undefined;
137
+ }
138
+ };
139
+ // eslint-disable-next-line react-hooks/exhaustive-deps
140
+ }, EMPTY);
141
+ useSyncExternalStore(unsubscribe, subscribe, emptyGetSnapshot);
108
142
  return state;
109
143
  };
@@ -0,0 +1 @@
1
+ export declare const useIsSSR: () => boolean;
@@ -0,0 +1,5 @@
1
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
2
+ const emptySubscribe = () => () => {};
3
+ const clientSnapshot = () => false;
4
+ const serverSnapshot = () => true;
5
+ export const useIsSSR = () => useSyncExternalStore(emptySubscribe, clientSnapshot, serverSnapshot);