@mui/x-data-grid 7.20.0 → 7.21.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 (124) hide show
  1. package/CHANGELOG.md +120 -31
  2. package/DataGrid/DataGrid.js +1 -0
  3. package/DataGrid/index.d.ts +0 -1
  4. package/DataGrid/index.js +1 -2
  5. package/DataGrid/useDataGridComponent.js +4 -1
  6. package/DataGrid/useDataGridProps.d.ts +1 -5
  7. package/DataGrid/useDataGridProps.js +3 -62
  8. package/components/GridPagination.js +1 -0
  9. package/components/GridRow.js +25 -36
  10. package/components/base/GridOverlays.js +8 -0
  11. package/components/containers/GridRootStyles.js +1 -0
  12. package/components/menu/columnMenu/GridColumnMenu.js +32 -0
  13. package/components/toolbar/GridToolbarExport.d.ts +9 -3
  14. package/components/toolbar/GridToolbarExport.js +55 -3
  15. package/components/virtualization/GridVirtualScrollbar.js +4 -0
  16. package/components/virtualization/GridVirtualScroller.js +2 -2
  17. package/constants/dataGridPropsDefaultValues.d.ts +5 -0
  18. package/constants/dataGridPropsDefaultValues.js +60 -0
  19. package/hooks/core/pipeProcessing/gridPipeProcessingApi.d.ts +2 -1
  20. package/hooks/features/clipboard/useGridClipboard.js +2 -1
  21. package/hooks/features/columnHeaders/useGridColumnHeaders.js +3 -1
  22. package/hooks/features/columns/gridColumnsUtils.d.ts +1 -1
  23. package/hooks/features/columns/gridColumnsUtils.js +3 -0
  24. package/hooks/features/dimensions/useGridDimensions.js +4 -2
  25. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.d.ts +1 -1
  26. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +9 -5
  27. package/hooks/features/listView/gridListViewSelectors.d.ts +5 -0
  28. package/hooks/features/listView/gridListViewSelectors.js +4 -0
  29. package/hooks/features/listView/useGridListView.d.ts +10 -0
  30. package/hooks/features/listView/useGridListView.js +54 -0
  31. package/hooks/features/rowSelection/useGridRowSelection.js +11 -2
  32. package/hooks/features/rowSelection/utils.js +1 -1
  33. package/hooks/features/rows/gridRowsMetaInterfaces.d.ts +16 -0
  34. package/hooks/features/rows/gridRowsMetaInterfaces.js +1 -0
  35. package/hooks/features/rows/gridRowsUtils.d.ts +3 -0
  36. package/hooks/features/rows/gridRowsUtils.js +14 -1
  37. package/hooks/features/rows/useGridParamsApi.d.ts +2 -1
  38. package/hooks/features/rows/useGridParamsApi.js +4 -3
  39. package/hooks/features/rows/useGridRowsMeta.js +135 -154
  40. package/hooks/features/scroll/useGridScroll.d.ts +1 -1
  41. package/hooks/features/scroll/useGridScroll.js +3 -2
  42. package/hooks/features/virtualization/useGridVirtualScroller.js +23 -9
  43. package/hooks/utils/useGridApiMethod.js +2 -1
  44. package/index.d.ts +1 -0
  45. package/index.js +2 -1
  46. package/internals/index.d.ts +2 -1
  47. package/internals/index.js +2 -1
  48. package/models/api/gridRowsMetaApi.d.ts +15 -14
  49. package/models/colDef/gridColDef.d.ts +6 -0
  50. package/models/colDef/index.d.ts +1 -1
  51. package/models/gridApiCaches.d.ts +2 -0
  52. package/models/gridStateCommunity.d.ts +2 -0
  53. package/models/props/DataGridProps.d.ts +14 -3
  54. package/modern/DataGrid/DataGrid.js +1 -0
  55. package/modern/DataGrid/index.js +1 -2
  56. package/modern/DataGrid/useDataGridComponent.js +4 -1
  57. package/modern/DataGrid/useDataGridProps.js +3 -62
  58. package/modern/components/GridPagination.js +1 -0
  59. package/modern/components/GridRow.js +25 -36
  60. package/modern/components/base/GridOverlays.js +8 -0
  61. package/modern/components/containers/GridRootStyles.js +1 -0
  62. package/modern/components/menu/columnMenu/GridColumnMenu.js +32 -0
  63. package/modern/components/toolbar/GridToolbarExport.js +55 -3
  64. package/modern/components/virtualization/GridVirtualScrollbar.js +4 -0
  65. package/modern/components/virtualization/GridVirtualScroller.js +2 -2
  66. package/modern/constants/dataGridPropsDefaultValues.js +60 -0
  67. package/modern/hooks/features/clipboard/useGridClipboard.js +2 -1
  68. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +3 -1
  69. package/modern/hooks/features/columns/gridColumnsUtils.js +3 -0
  70. package/modern/hooks/features/dimensions/useGridDimensions.js +4 -2
  71. package/modern/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +9 -5
  72. package/modern/hooks/features/listView/gridListViewSelectors.js +4 -0
  73. package/modern/hooks/features/listView/useGridListView.js +54 -0
  74. package/modern/hooks/features/rowSelection/useGridRowSelection.js +11 -2
  75. package/modern/hooks/features/rowSelection/utils.js +1 -1
  76. package/modern/hooks/features/rows/gridRowsMetaInterfaces.js +1 -0
  77. package/modern/hooks/features/rows/gridRowsUtils.js +14 -1
  78. package/modern/hooks/features/rows/useGridParamsApi.js +4 -3
  79. package/modern/hooks/features/rows/useGridRowsMeta.js +135 -154
  80. package/modern/hooks/features/scroll/useGridScroll.js +3 -2
  81. package/modern/hooks/features/virtualization/useGridVirtualScroller.js +23 -9
  82. package/modern/hooks/utils/useGridApiMethod.js +2 -1
  83. package/modern/index.js +2 -1
  84. package/modern/internals/index.js +2 -1
  85. package/modern/utils/ResizeObserver.js +10 -0
  86. package/modern/utils/keyboardUtils.js +12 -4
  87. package/node/DataGrid/DataGrid.js +1 -0
  88. package/node/DataGrid/index.js +1 -12
  89. package/node/DataGrid/useDataGridComponent.js +4 -1
  90. package/node/DataGrid/useDataGridProps.js +6 -65
  91. package/node/components/GridPagination.js +1 -0
  92. package/node/components/GridRow.js +25 -36
  93. package/node/components/base/GridOverlays.js +8 -0
  94. package/node/components/containers/GridRootStyles.js +1 -0
  95. package/node/components/menu/columnMenu/GridColumnMenu.js +32 -0
  96. package/node/components/toolbar/GridToolbarExport.js +52 -0
  97. package/node/components/virtualization/GridVirtualScrollbar.js +4 -0
  98. package/node/components/virtualization/GridVirtualScroller.js +2 -2
  99. package/node/constants/dataGridPropsDefaultValues.js +66 -0
  100. package/node/hooks/features/clipboard/useGridClipboard.js +2 -1
  101. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +3 -1
  102. package/node/hooks/features/columns/gridColumnsUtils.js +3 -0
  103. package/node/hooks/features/dimensions/useGridDimensions.js +3 -1
  104. package/node/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +9 -5
  105. package/node/hooks/features/listView/gridListViewSelectors.js +11 -0
  106. package/node/hooks/features/listView/useGridListView.js +64 -0
  107. package/node/hooks/features/rowSelection/useGridRowSelection.js +11 -2
  108. package/node/hooks/features/rowSelection/utils.js +1 -1
  109. package/node/hooks/features/rows/gridRowsMetaInterfaces.js +5 -0
  110. package/node/hooks/features/rows/gridRowsUtils.js +16 -2
  111. package/node/hooks/features/rows/useGridParamsApi.js +4 -3
  112. package/node/hooks/features/rows/useGridRowsMeta.js +136 -154
  113. package/node/hooks/features/scroll/useGridScroll.js +3 -2
  114. package/node/hooks/features/virtualization/useGridVirtualScroller.js +23 -9
  115. package/node/hooks/utils/useGridApiMethod.js +3 -1
  116. package/node/index.js +13 -1
  117. package/node/internals/index.js +22 -0
  118. package/node/utils/ResizeObserver.js +16 -0
  119. package/node/utils/keyboardUtils.js +15 -5
  120. package/package.json +2 -2
  121. package/utils/ResizeObserver.d.ts +4 -0
  122. package/utils/ResizeObserver.js +10 -0
  123. package/utils/keyboardUtils.d.ts +1 -0
  124. package/utils/keyboardUtils.js +12 -4
@@ -16,6 +16,7 @@ import { gridHeaderFilteringEditFieldSelector, gridHeaderFilteringMenuSelector }
16
16
  import { useGridRegisterPipeProcessor } from "../../core/pipeProcessing/index.js";
17
17
  import { isEventTargetInPortal } from "../../../utils/domUtils.js";
18
18
  import { enrichPageRowsWithPinnedRows, getLeftColumnIndex, getRightColumnIndex, findNonRowSpannedCell } from "./utils.js";
19
+ import { gridListColumnSelector } from "../listView/gridListViewSelectors.js";
19
20
 
20
21
  /**
21
22
  * @requires useGridSorting (method) - can be after
@@ -30,6 +31,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
30
31
  const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation');
31
32
  const initialCurrentPageRows = useGridVisibleRows(apiRef, props).rows;
32
33
  const isRtl = useRtl();
34
+ const listView = props.unstable_listView;
33
35
  const currentPageRows = React.useMemo(() => enrichPageRowsWithPinnedRows(apiRef, initialCurrentPageRows), [apiRef, initialCurrentPageRows]);
34
36
  const headerFilteringEnabled = props.signature !== 'DataGrid' && props.headerFilters;
35
37
 
@@ -50,7 +52,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
50
52
  colIndex = nextCellColSpanInfo.rightVisibleCellIndex;
51
53
  }
52
54
  }
53
- const field = gridVisibleColumnFieldsSelector(apiRef)[colIndex];
55
+ const field = listView ? gridListColumnSelector(apiRef.current.state).field : gridVisibleColumnFieldsSelector(apiRef)[colIndex];
54
56
  const nonRowSpannedRowId = findNonRowSpannedCell(apiRef, rowId, field, rowSpanScanDirection);
55
57
  // `scrollToIndexes` requires a rowIndex relative to all visible rows.
56
58
  // Those rows do not include pinned rows, but pinned rows do not need scroll anyway.
@@ -61,7 +63,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
61
63
  rowIndex: rowIndexRelativeToAllRows
62
64
  });
63
65
  apiRef.current.setCellFocus(nonRowSpannedRowId, field);
64
- }, [apiRef, logger]);
66
+ }, [apiRef, logger, listView]);
65
67
  const goToHeader = React.useCallback((colIndex, event) => {
66
68
  logger.debug(`Navigating to header col ${colIndex}`);
67
69
  apiRef.current.scrollToIndexes({
@@ -384,12 +386,14 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
384
386
  return;
385
387
  }
386
388
  const viewportPageSize = apiRef.current.getViewportPageSize();
387
- const colIndexBefore = params.field ? apiRef.current.getColumnIndex(params.field) : 0;
389
+ const getColumnIndexFn = listView ? () => 0 : apiRef.current.getColumnIndex;
390
+ const colIndexBefore = params.field ? getColumnIndexFn(params.field) : 0;
388
391
  const rowIndexBefore = currentPageRows.findIndex(row => row.id === params.id);
389
392
  const firstRowIndexInPage = 0;
390
393
  const lastRowIndexInPage = currentPageRows.length - 1;
391
394
  const firstColIndex = 0;
392
- const lastColIndex = gridVisibleColumnDefinitionsSelector(apiRef).length - 1;
395
+ const visibleColumns = listView ? [gridListColumnSelector(apiRef.current.state)] : gridVisibleColumnDefinitionsSelector(apiRef);
396
+ const lastColIndex = visibleColumns.length - 1;
393
397
  let shouldPreventDefault = true;
394
398
  switch (event.key) {
395
399
  case 'ArrowDown':
@@ -508,7 +512,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
508
512
  if (shouldPreventDefault) {
509
513
  event.preventDefault();
510
514
  }
511
- }, [apiRef, currentPageRows, isRtl, goToCell, getRowIdFromIndex, headerFilteringEnabled, goToHeaderFilter, goToHeader]);
515
+ }, [apiRef, currentPageRows, isRtl, goToCell, getRowIdFromIndex, headerFilteringEnabled, goToHeaderFilter, goToHeader, listView]);
512
516
  const checkIfCanStartEditing = React.useCallback((initialValue, {
513
517
  event
514
518
  }) => {
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Get a list column definition
3
+ */
4
+ export const gridListColumnSelector = state => state.listViewColumn;
@@ -0,0 +1,54 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import * as React from 'react';
3
+ import { gridDimensionsSelector } from "../dimensions/index.js";
4
+ import { useGridApiEventHandler } from "../../utils/useGridApiEventHandler.js";
5
+ export const listViewStateInitializer = (state, props, apiRef) => _extends({}, state, {
6
+ listViewColumn: _extends({}, props.unstable_listColumn, {
7
+ computedWidth: getListColumnWidth(apiRef)
8
+ })
9
+ });
10
+ export function useGridListView(apiRef, props) {
11
+ /*
12
+ * EVENTS
13
+ */
14
+ const updateListColumnWidth = () => {
15
+ apiRef.current.setState(state => {
16
+ if (!state.listViewColumn) {
17
+ return state;
18
+ }
19
+ return _extends({}, state, {
20
+ listViewColumn: _extends({}, state.listViewColumn, {
21
+ computedWidth: getListColumnWidth(apiRef)
22
+ })
23
+ });
24
+ });
25
+ };
26
+ const prevInnerWidth = React.useRef(null);
27
+ const handleGridSizeChange = viewportInnerSize => {
28
+ if (prevInnerWidth.current !== viewportInnerSize.width) {
29
+ prevInnerWidth.current = viewportInnerSize.width;
30
+ updateListColumnWidth();
31
+ }
32
+ };
33
+ useGridApiEventHandler(apiRef, 'viewportInnerSizeChange', handleGridSizeChange);
34
+ useGridApiEventHandler(apiRef, 'columnVisibilityModelChange', updateListColumnWidth);
35
+
36
+ /*
37
+ * EFFECTS
38
+ */
39
+ React.useEffect(() => {
40
+ const listColumn = props.unstable_listColumn;
41
+ if (listColumn) {
42
+ apiRef.current.setState(state => {
43
+ return _extends({}, state, {
44
+ listViewColumn: _extends({}, listColumn, {
45
+ computedWidth: getListColumnWidth(apiRef)
46
+ })
47
+ });
48
+ });
49
+ }
50
+ }, [apiRef, props.unstable_listColumn]);
51
+ }
52
+ function getListColumnWidth(apiRef) {
53
+ return gridDimensionsSelector(apiRef.current.state).viewportInnerSize.width;
54
+ }
@@ -246,7 +246,11 @@ export const useGridRowSelection = (apiRef, props) => {
246
246
  /*
247
247
  * EVENTS
248
248
  */
249
+ const isFirstRender = React.useRef(true);
249
250
  const removeOutdatedSelection = React.useCallback((sortModelUpdated = false) => {
251
+ if (isFirstRender.current) {
252
+ return;
253
+ }
250
254
  const currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
251
255
  const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
252
256
 
@@ -254,7 +258,7 @@ export const useGridRowSelection = (apiRef, props) => {
254
258
  const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
255
259
  let hasChanged = false;
256
260
  currentSelection.forEach(id => {
257
- if (filteredRowsLookup[id] === false) {
261
+ if (filteredRowsLookup[id] !== true) {
258
262
  if (props.keepNonExistentRowsSelected) {
259
263
  return;
260
264
  }
@@ -408,7 +412,7 @@ export const useGridRowSelection = (apiRef, props) => {
408
412
  handleSingleRowSelection(params.id, event);
409
413
  return;
410
414
  }
411
- if (event.key === 'a' && (event.ctrlKey || event.metaKey)) {
415
+ if (String.fromCharCode(event.keyCode) === 'A' && (event.ctrlKey || event.metaKey)) {
412
416
  event.preventDefault();
413
417
  selectRows(apiRef.current.getAllRowIds(), true);
414
418
  }
@@ -462,4 +466,9 @@ export const useGridRowSelection = (apiRef, props) => {
462
466
  React.useEffect(() => {
463
467
  runIfRowSelectionIsEnabled(removeOutdatedSelection);
464
468
  }, [removeOutdatedSelection, runIfRowSelectionIsEnabled]);
469
+ React.useEffect(() => {
470
+ if (isFirstRender.current) {
471
+ isFirstRender.current = false;
472
+ }
473
+ }, []);
465
474
  };
@@ -57,7 +57,7 @@ export function getCheckboxPropsSelector(groupId, autoSelectParents) {
57
57
  }
58
58
  }
59
59
  return {
60
- isIndeterminate: selectedDescendantsCount > 0 && selectedDescendantsCount < selectableDescendantsCount || selectedDescendantsCount === selectableDescendantsCount && rowSelectionLookup[groupId] === undefined,
60
+ isIndeterminate: selectedDescendantsCount > 0 && (selectedDescendantsCount < selectableDescendantsCount || rowSelectionLookup[groupId] === undefined),
61
61
  isChecked: autoSelectParents ? selectedDescendantsCount > 0 : rowSelectionLookup[groupId] === groupId
62
62
  };
63
63
  });
@@ -306,4 +306,17 @@ export function computeRowsUpdates(apiRef, updates, getRowId) {
306
306
  }
307
307
  });
308
308
  return nonPinnedRowsUpdates;
309
- }
309
+ }
310
+ let warnedOnceInvalidRowHeight = false;
311
+ export const getValidRowHeight = (rowHeightProp, defaultRowHeight, warningMessage) => {
312
+ if (typeof rowHeightProp === 'number' && rowHeightProp > 0) {
313
+ return rowHeightProp;
314
+ }
315
+ if (process.env.NODE_ENV !== 'production' && !warnedOnceInvalidRowHeight && typeof rowHeightProp !== 'undefined' && rowHeightProp !== null) {
316
+ console.warn(warningMessage);
317
+ warnedOnceInvalidRowHeight = true;
318
+ }
319
+ return defaultRowHeight;
320
+ };
321
+ export const rowHeightWarning = [`MUI X: The \`rowHeight\` prop should be a number greater than 0.`, `The default value will be used instead.`].join('\n');
322
+ export const getRowHeightWarning = [`MUI X: The \`getRowHeight\` prop should return a number greater than 0 or 'auto'.`, `The default value will be used instead.`].join('\n');
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import { getGridCellElement, getGridColumnHeaderElement, getGridRowElement } from "../../../utils/domUtils.js";
3
3
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
4
4
  import { gridFocusCellSelector, gridTabIndexCellSelector } from "../focus/gridFocusStateSelector.js";
5
+ import { gridListColumnSelector } from "../listView/gridListViewSelectors.js";
5
6
  export class MissingRowIdError extends Error {}
6
7
 
7
8
  /**
@@ -12,7 +13,7 @@ export class MissingRowIdError extends Error {}
12
13
  * TODO: Impossible priority - useGridEditing also needs to be after useGridParamsApi
13
14
  * TODO: Impossible priority - useGridFocus also needs to be after useGridParamsApi
14
15
  */
15
- export function useGridParamsApi(apiRef) {
16
+ export function useGridParamsApi(apiRef, props) {
16
17
  const getColumnHeaderParams = React.useCallback(field => ({
17
18
  field,
18
19
  colDef: apiRef.current.getColumn(field)
@@ -30,7 +31,7 @@ export function useGridParamsApi(apiRef) {
30
31
  return params;
31
32
  }, [apiRef]);
32
33
  const getCellParams = React.useCallback((id, field) => {
33
- const colDef = apiRef.current.getColumn(field);
34
+ const colDef = props.unstable_listView ? gridListColumnSelector(apiRef.current.state) : apiRef.current.getColumn(field);
34
35
  const row = apiRef.current.getRow(id);
35
36
  const rowNode = apiRef.current.getRowNode(id);
36
37
  if (!row || !rowNode) {
@@ -59,7 +60,7 @@ export function useGridParamsApi(apiRef) {
59
60
  }
60
61
  params.isEditable = colDef && apiRef.current.isCellEditable(params);
61
62
  return params;
62
- }, [apiRef]);
63
+ }, [apiRef, props.unstable_listView]);
63
64
  const getCellValue = React.useCallback((id, field) => {
64
65
  const colDef = apiRef.current.getColumn(field);
65
66
  const row = apiRef.current.getRow(id);
@@ -1,7 +1,9 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
- import { unstable_debounce as debounce } from '@mui/utils';
3
+ import useLazyRef from '@mui/utils/useLazyRef';
4
+ import { ResizeObserver } from "../../../utils/ResizeObserver.js";
4
5
  import { useGridVisibleRows } from "../../utils/useGridVisibleRows.js";
6
+ import { eslintUseValue } from "../../../utils/utils.js";
5
7
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
6
8
  import { useGridSelector } from "../../utils/useGridSelector.js";
7
9
  import { gridDensityFactorSelector } from "../density/densitySelector.js";
@@ -10,29 +12,21 @@ import { gridPaginationSelector } from "../pagination/gridPaginationSelector.js"
10
12
  import { gridSortModelSelector } from "../sorting/gridSortingSelector.js";
11
13
  import { useGridRegisterPipeApplier } from "../../core/pipeProcessing/index.js";
12
14
  import { gridPinnedRowsSelector } from "./gridRowsSelector.js";
13
- import { DATA_GRID_PROPS_DEFAULT_VALUES } from "../../../DataGrid/useDataGridProps.js";
15
+ import { gridDimensionsSelector } from "../dimensions/gridDimensionsSelectors.js";
16
+ import { getValidRowHeight, getRowHeightWarning } from "./gridRowsUtils.js";
17
+ /* eslint-disable no-underscore-dangle */
14
18
 
15
- // TODO: I think the row heights can now be encoded as a single `size` instead of `sizes.baseXxxx`
16
-
17
- export const rowsMetaStateInitializer = state => _extends({}, state, {
18
- rowsMeta: {
19
- currentPageTotalHeight: 0,
20
- positions: []
21
- }
22
- });
23
- let warnedOnceInvalidRowHeight = false;
24
- const getValidRowHeight = (rowHeightProp, defaultRowHeight, warningMessage) => {
25
- if (typeof rowHeightProp === 'number' && rowHeightProp > 0) {
26
- return rowHeightProp;
27
- }
28
- if (process.env.NODE_ENV !== 'production' && !warnedOnceInvalidRowHeight && typeof rowHeightProp !== 'undefined' && rowHeightProp !== null) {
29
- console.warn(warningMessage);
30
- warnedOnceInvalidRowHeight = true;
31
- }
32
- return defaultRowHeight;
19
+ export const rowsMetaStateInitializer = (state, props, apiRef) => {
20
+ apiRef.current.caches.rowsMeta = {
21
+ heights: new Map()
22
+ };
23
+ return _extends({}, state, {
24
+ rowsMeta: {
25
+ currentPageTotalHeight: 0,
26
+ positions: []
27
+ }
28
+ });
33
29
  };
34
- const rowHeightWarning = [`MUI X: The \`rowHeight\` prop should be a number greater than 0.`, `The default value will be used instead.`].join('\n');
35
- const getRowHeightWarning = [`MUI X: The \`getRowHeight\` prop should return a number greater than 0 or 'auto'.`, `The default value will be used instead.`].join('\n');
36
30
 
37
31
  /**
38
32
  * @requires useGridPageSize (method)
@@ -44,105 +38,94 @@ export const useGridRowsMeta = (apiRef, props) => {
44
38
  getRowSpacing,
45
39
  getEstimatedRowHeight
46
40
  } = props;
47
- const rowsHeightLookup = React.useRef(Object.create(null));
48
-
49
- // Inspired by https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/utils/CellSizeAndPositionManager.js
41
+ const heightCache = apiRef.current.caches.rowsMeta.heights;
50
42
  const lastMeasuredRowIndex = React.useRef(-1);
51
43
  const hasRowWithAutoHeight = React.useRef(false);
44
+ const isHeightMetaValid = React.useRef(false);
52
45
  const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
53
46
  const filterModel = useGridSelector(apiRef, gridFilterModelSelector);
54
47
  const paginationState = useGridSelector(apiRef, gridPaginationSelector);
55
48
  const sortModel = useGridSelector(apiRef, gridSortModelSelector);
56
49
  const currentPage = useGridVisibleRows(apiRef, props);
57
50
  const pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
58
- const validRowHeight = getValidRowHeight(props.rowHeight, DATA_GRID_PROPS_DEFAULT_VALUES.rowHeight, rowHeightWarning);
59
- const rowHeight = Math.floor(validRowHeight * densityFactor);
60
- const hydrateRowsMeta = React.useCallback(() => {
61
- hasRowWithAutoHeight.current = false;
62
- const calculateRowProcessedSizes = row => {
63
- if (!rowsHeightLookup.current[row.id]) {
64
- rowsHeightLookup.current[row.id] = {
65
- sizes: {
66
- baseCenter: rowHeight
67
- },
68
- isResized: false,
69
- autoHeight: false,
70
- needsFirstMeasurement: true // Assume all rows will need to be measured by default
71
- };
72
- }
73
- const {
74
- isResized,
75
- needsFirstMeasurement,
76
- sizes
77
- } = rowsHeightLookup.current[row.id];
78
- let baseRowHeight = typeof rowHeight === 'number' && rowHeight > 0 ? rowHeight : 52;
79
- const existingBaseRowHeight = sizes.baseCenter;
80
- if (isResized) {
81
- // Do not recalculate resized row height and use the value from the lookup
82
- baseRowHeight = existingBaseRowHeight;
83
- } else if (getRowHeightProp) {
84
- const rowHeightFromUser = getRowHeightProp(_extends({}, row, {
85
- densityFactor
86
- }));
87
- if (rowHeightFromUser === 'auto') {
88
- if (needsFirstMeasurement) {
89
- const estimatedRowHeight = getEstimatedRowHeight ? getEstimatedRowHeight(_extends({}, row, {
90
- densityFactor
91
- })) : rowHeight;
51
+ const rowHeight = useGridSelector(apiRef, () => gridDimensionsSelector(apiRef.current.state).rowHeight);
52
+ const getRowHeightEntry = rowId => {
53
+ let entry = heightCache.get(rowId);
54
+ if (entry === undefined) {
55
+ entry = {
56
+ content: rowHeight,
57
+ spacingTop: 0,
58
+ spacingBottom: 0,
59
+ detail: 0,
60
+ autoHeight: false,
61
+ needsFirstMeasurement: true
62
+ };
63
+ heightCache.set(rowId, entry);
64
+ }
65
+ return entry;
66
+ };
67
+ const processHeightEntry = React.useCallback(row => {
68
+ // HACK: rowHeight trails behind the most up-to-date value just enough to
69
+ // mess the initial rowsMeta hydration :/
70
+ const baseRowHeight = gridDimensionsSelector(apiRef.current.state).rowHeight;
71
+ eslintUseValue(rowHeight);
72
+ const entry = apiRef.current.getRowHeightEntry(row.id);
73
+ if (!getRowHeightProp) {
74
+ entry.content = baseRowHeight;
75
+ entry.needsFirstMeasurement = false;
76
+ } else {
77
+ const rowHeightFromUser = getRowHeightProp(_extends({}, row, {
78
+ densityFactor
79
+ }));
80
+ if (rowHeightFromUser === 'auto') {
81
+ if (entry.needsFirstMeasurement) {
82
+ const estimatedRowHeight = getEstimatedRowHeight ? getEstimatedRowHeight(_extends({}, row, {
83
+ densityFactor
84
+ })) : baseRowHeight;
92
85
 
93
- // If the row was not measured yet use the estimated row height
94
- baseRowHeight = estimatedRowHeight ?? rowHeight;
95
- } else {
96
- baseRowHeight = existingBaseRowHeight;
97
- }
98
- hasRowWithAutoHeight.current = true;
99
- rowsHeightLookup.current[row.id].autoHeight = true;
100
- } else {
101
- // Default back to base rowHeight if getRowHeight returns invalid value.
102
- baseRowHeight = getValidRowHeight(rowHeightFromUser, rowHeight, getRowHeightWarning);
103
- rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
104
- rowsHeightLookup.current[row.id].autoHeight = false;
86
+ // If the row was not measured yet use the estimated row height
87
+ entry.content = estimatedRowHeight ?? baseRowHeight;
105
88
  }
89
+ hasRowWithAutoHeight.current = true;
90
+ entry.autoHeight = true;
106
91
  } else {
107
- rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
108
- }
109
- const initialHeights = {
110
- baseCenter: baseRowHeight
111
- };
112
- if (getRowSpacing) {
113
- const indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(row.id);
114
- const spacing = getRowSpacing(_extends({}, row, {
115
- isFirstVisible: indexRelativeToCurrentPage === 0,
116
- isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1,
117
- indexRelativeToCurrentPage
118
- }));
119
- initialHeights.spacingTop = spacing.top ?? 0;
120
- initialHeights.spacingBottom = spacing.bottom ?? 0;
92
+ // Default back to base rowHeight if getRowHeight returns invalid value.
93
+ entry.content = getValidRowHeight(rowHeightFromUser, baseRowHeight, getRowHeightWarning);
94
+ entry.needsFirstMeasurement = false;
95
+ entry.autoHeight = false;
121
96
  }
122
- const processedSizes = apiRef.current.unstable_applyPipeProcessors('rowHeight', initialHeights, row);
123
- rowsHeightLookup.current[row.id].sizes = processedSizes;
124
- return processedSizes;
125
- };
97
+ }
98
+ if (getRowSpacing) {
99
+ const indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(row.id);
100
+ const spacing = getRowSpacing(_extends({}, row, {
101
+ isFirstVisible: indexRelativeToCurrentPage === 0,
102
+ isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1,
103
+ indexRelativeToCurrentPage
104
+ }));
105
+ entry.spacingTop = spacing.top ?? 0;
106
+ entry.spacingBottom = spacing.bottom ?? 0;
107
+ } else {
108
+ entry.spacingTop = 0;
109
+ entry.spacingBottom = 0;
110
+ }
111
+ apiRef.current.unstable_applyPipeProcessors('rowHeight', entry, row);
112
+ return entry;
113
+ }, [apiRef, currentPage.rows.length, getRowHeightProp, getEstimatedRowHeight, rowHeight, getRowSpacing, densityFactor]);
114
+ const hydrateRowsMeta = React.useCallback(() => {
115
+ hasRowWithAutoHeight.current = false;
116
+ pinnedRows.top.forEach(processHeightEntry);
117
+ pinnedRows.bottom.forEach(processHeightEntry);
126
118
  const positions = [];
127
119
  const currentPageTotalHeight = currentPage.rows.reduce((acc, row) => {
128
120
  positions.push(acc);
129
- let otherSizes = 0;
130
- const processedSizes = calculateRowProcessedSizes(row);
131
- /* eslint-disable-next-line guard-for-in */
132
- for (const key in processedSizes) {
133
- const value = processedSizes[key];
134
- if (key !== 'baseCenter') {
135
- otherSizes += value;
136
- }
137
- }
138
- return acc + processedSizes.baseCenter + otherSizes;
121
+ const entry = processHeightEntry(row);
122
+ const total = entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
123
+ return acc + total;
139
124
  }, 0);
140
- pinnedRows?.top?.forEach(row => {
141
- calculateRowProcessedSizes(row);
142
- });
143
- pinnedRows?.bottom?.forEach(row => {
144
- calculateRowProcessedSizes(row);
145
- });
125
+ if (!hasRowWithAutoHeight.current) {
126
+ // No row has height=auto, so all rows are already measured
127
+ lastMeasuredRowIndex.current = Infinity;
128
+ }
146
129
  apiRef.current.setState(state => {
147
130
  return _extends({}, state, {
148
131
  rowsMeta: {
@@ -151,70 +134,68 @@ export const useGridRowsMeta = (apiRef, props) => {
151
134
  }
152
135
  });
153
136
  });
154
- if (!hasRowWithAutoHeight.current) {
155
- // No row has height=auto, so all rows are already measured
156
- lastMeasuredRowIndex.current = Infinity;
157
- }
158
- apiRef.current.forceUpdate();
159
- }, [apiRef, currentPage.rows, rowHeight, getRowHeightProp, getRowSpacing, getEstimatedRowHeight, pinnedRows, densityFactor]);
160
- const getRowHeight = React.useCallback(rowId => {
161
- const height = rowsHeightLookup.current[rowId];
162
- return height ? height.sizes.baseCenter : rowHeight;
163
- }, [rowHeight]);
164
- const getRowInternalSizes = rowId => rowsHeightLookup.current[rowId]?.sizes;
165
- const setRowHeight = React.useCallback((id, height) => {
166
- rowsHeightLookup.current[id].sizes.baseCenter = height;
167
- rowsHeightLookup.current[id].isResized = true;
168
- rowsHeightLookup.current[id].needsFirstMeasurement = false;
169
- hydrateRowsMeta();
170
- }, [hydrateRowsMeta]);
171
- const debouncedHydrateRowsMeta = React.useMemo(() => debounce(hydrateRowsMeta, props.rowPositionsDebounceMs), [hydrateRowsMeta, props.rowPositionsDebounceMs]);
172
- const storeMeasuredRowHeight = React.useCallback((id, height) => {
173
- if (!rowsHeightLookup.current[id] || !rowsHeightLookup.current[id].autoHeight) {
174
- return;
175
- }
176
-
177
- // Only trigger hydration if the value is different, otherwise we trigger a loop
178
- const needsHydration = rowsHeightLookup.current[id].sizes.baseCenter !== height;
179
- rowsHeightLookup.current[id].needsFirstMeasurement = false;
180
- rowsHeightLookup.current[id].sizes.baseCenter = height;
181
- if (needsHydration) {
182
- debouncedHydrateRowsMeta();
183
- }
184
- }, [debouncedHydrateRowsMeta]);
185
- const rowHasAutoHeight = React.useCallback(id => {
186
- return rowsHeightLookup.current[id]?.autoHeight || false;
187
- }, []);
188
- const getLastMeasuredRowIndex = React.useCallback(() => {
137
+ isHeightMetaValid.current = true;
138
+ }, [apiRef, pinnedRows, currentPage.rows, processHeightEntry]);
139
+ const getRowHeight = rowId => {
140
+ return heightCache.get(rowId)?.content ?? rowHeight;
141
+ };
142
+ const storeRowHeightMeasurement = (id, height) => {
143
+ const entry = apiRef.current.getRowHeightEntry(id);
144
+ const didChange = entry.content !== height;
145
+ entry.needsFirstMeasurement = false;
146
+ entry.content = height;
147
+ isHeightMetaValid.current && (isHeightMetaValid.current = !didChange);
148
+ };
149
+ const rowHasAutoHeight = id => {
150
+ return heightCache.get(id)?.autoHeight ?? false;
151
+ };
152
+ const getLastMeasuredRowIndex = () => {
189
153
  return lastMeasuredRowIndex.current;
190
- }, []);
191
- const setLastMeasuredRowIndex = React.useCallback(index => {
154
+ };
155
+ const setLastMeasuredRowIndex = index => {
192
156
  if (hasRowWithAutoHeight.current && index > lastMeasuredRowIndex.current) {
193
157
  lastMeasuredRowIndex.current = index;
194
158
  }
195
- }, []);
196
- const resetRowHeights = React.useCallback(() => {
197
- rowsHeightLookup.current = {};
159
+ };
160
+ const resetRowHeights = () => {
161
+ heightCache.clear();
198
162
  hydrateRowsMeta();
199
- }, [hydrateRowsMeta]);
163
+ };
164
+ const resizeObserver = useLazyRef(() => new ResizeObserver(entries => {
165
+ for (let i = 0; i < entries.length; i += 1) {
166
+ const entry = entries[i];
167
+ const height = entry.borderBoxSize && entry.borderBoxSize.length > 0 ? entry.borderBoxSize[0].blockSize : entry.contentRect.height;
168
+ const rowId = entry.target.__mui_id;
169
+ apiRef.current.unstable_storeRowHeightMeasurement(rowId, height);
170
+ }
171
+ if (!isHeightMetaValid.current) {
172
+ apiRef.current.requestPipeProcessorsApplication('rowHeight');
173
+ }
174
+ })).current;
175
+ const observeRowHeight = (element, rowId) => {
176
+ element.__mui_id = rowId;
177
+ resizeObserver.observe(element);
178
+ return () => resizeObserver.unobserve(element);
179
+ };
180
+ useGridRegisterPipeApplier(apiRef, 'rowHeight', hydrateRowsMeta);
200
181
 
201
182
  // The effect is used to build the rows meta data - currentPageTotalHeight and positions.
202
183
  // Because of variable row height this is needed for the virtualization
203
184
  React.useEffect(() => {
204
185
  hydrateRowsMeta();
205
- }, [rowHeight, filterModel, paginationState, sortModel, hydrateRowsMeta]);
206
- useGridRegisterPipeApplier(apiRef, 'rowHeight', hydrateRowsMeta);
186
+ }, [filterModel, paginationState, sortModel, hydrateRowsMeta]);
207
187
  const rowsMetaApi = {
208
- unstable_setLastMeasuredRowIndex: setLastMeasuredRowIndex,
209
188
  unstable_getRowHeight: getRowHeight,
210
- unstable_getRowInternalSizes: getRowInternalSizes,
211
- unstable_setRowHeight: setRowHeight,
212
- unstable_storeRowHeightMeasurement: storeMeasuredRowHeight,
189
+ unstable_setLastMeasuredRowIndex: setLastMeasuredRowIndex,
190
+ unstable_storeRowHeightMeasurement: storeRowHeightMeasurement,
213
191
  resetRowHeights
214
192
  };
215
193
  const rowsMetaPrivateApi = {
216
- getLastMeasuredRowIndex,
217
- rowHasAutoHeight
194
+ hydrateRowsMeta,
195
+ observeRowHeight,
196
+ rowHasAutoHeight,
197
+ getRowHeightEntry,
198
+ getLastMeasuredRowIndex
218
199
  };
219
200
  useGridApiMethod(apiRef, rowsMetaApi, 'public');
220
201
  useGridApiMethod(apiRef, rowsMetaPrivateApi, 'private');
@@ -9,6 +9,7 @@ import { gridRowsMetaSelector } from "../rows/gridRowsMetaSelector.js";
9
9
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
10
10
  import { gridExpandedSortedRowEntriesSelector } from "../filter/gridFilterSelector.js";
11
11
  import { gridDimensionsSelector } from "../dimensions/index.js";
12
+ import { gridListColumnSelector } from "../listView/gridListViewSelectors.js";
12
13
 
13
14
  // Logic copied from https://www.w3.org/TR/wai-aria-practices/examples/listbox/js/listbox.js
14
15
  // Similar to https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
@@ -53,7 +54,7 @@ export const useGridScroll = (apiRef, props) => {
53
54
  const scrollToIndexes = React.useCallback(params => {
54
55
  const dimensions = gridDimensionsSelector(apiRef.current.state);
55
56
  const totalRowCount = gridRowCountSelector(apiRef);
56
- const visibleColumns = gridVisibleColumnDefinitionsSelector(apiRef);
57
+ const visibleColumns = props.unstable_listView ? [gridListColumnSelector(apiRef.current.state)] : gridVisibleColumnDefinitionsSelector(apiRef);
57
58
  const scrollToHeader = params.rowIndex == null;
58
59
  if (!scrollToHeader && totalRowCount === 0 || visibleColumns.length === 0) {
59
60
  return false;
@@ -100,7 +101,7 @@ export const useGridScroll = (apiRef, props) => {
100
101
  return true;
101
102
  }
102
103
  return false;
103
- }, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows]);
104
+ }, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows, props.unstable_listView]);
104
105
  const scroll = React.useCallback(params => {
105
106
  if (virtualScrollerRef.current && virtualScrollbarHorizontalRef.current && params.left !== undefined && colRef.current) {
106
107
  const direction = isRtl ? -1 : 1;