@mui/x-data-grid 7.0.0-beta.4 → 7.0.0-beta.6

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 (116) hide show
  1. package/CHANGELOG.md +271 -61
  2. package/DataGrid/DataGrid.js +2 -0
  3. package/colDef/gridBooleanOperators.js +1 -1
  4. package/components/GridPinnedRows.d.ts +1 -2
  5. package/components/GridRow.d.ts +7 -9
  6. package/components/GridRow.js +41 -54
  7. package/components/cell/GridCell.d.ts +2 -3
  8. package/components/cell/GridCell.js +10 -10
  9. package/components/cell/GridSkeletonCell.d.ts +3 -2
  10. package/components/cell/GridSkeletonCell.js +14 -6
  11. package/components/columnSelection/GridCellCheckboxRenderer.js +6 -4
  12. package/components/columnsManagement/GridColumnsManagement.js +1 -1
  13. package/components/containers/GridRootStyles.js +9 -4
  14. package/components/virtualization/GridBottomContainer.js +1 -1
  15. package/components/virtualization/GridTopContainer.js +1 -1
  16. package/components/virtualization/GridVirtualScroller.js +7 -5
  17. package/components/virtualization/GridVirtualScrollerRenderZone.js +9 -3
  18. package/hooks/core/pipeProcessing/useGridPipeProcessing.js +22 -20
  19. package/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -8
  20. package/hooks/features/columns/gridColumnsSelector.d.ts +6 -0
  21. package/hooks/features/columns/gridColumnsSelector.js +8 -1
  22. package/hooks/features/columns/useGridColumns.js +4 -0
  23. package/hooks/features/dimensions/useGridDimensions.js +1 -0
  24. package/hooks/features/editing/useGridRowEditing.js +1 -2
  25. package/hooks/features/filter/useGridFilter.js +2 -2
  26. package/hooks/features/rows/useGridParamsApi.js +6 -10
  27. package/hooks/features/rows/useGridRows.js +8 -4
  28. package/hooks/features/rows/useGridRowsMeta.js +5 -13
  29. package/hooks/features/sorting/gridSortingUtils.js +9 -1
  30. package/hooks/features/sorting/useGridSorting.js +2 -2
  31. package/hooks/features/virtualization/gridVirtualizationSelectors.d.ts +0 -9
  32. package/hooks/features/virtualization/gridVirtualizationSelectors.js +0 -7
  33. package/hooks/features/virtualization/useGridVirtualScroller.d.ts +3 -0
  34. package/hooks/features/virtualization/useGridVirtualScroller.js +88 -138
  35. package/hooks/features/virtualization/useGridVirtualization.d.ts +0 -8
  36. package/hooks/features/virtualization/useGridVirtualization.js +1 -6
  37. package/hooks/utils/useTimeout.d.ts +5 -3
  38. package/hooks/utils/useTimeout.js +13 -5
  39. package/index.js +1 -1
  40. package/internals/index.d.ts +1 -1
  41. package/internals/index.js +1 -1
  42. package/models/api/gridApiCommon.d.ts +2 -1
  43. package/models/api/gridInfiniteLoaderApi.d.ts +6 -0
  44. package/models/api/gridInfiniteLoaderApi.js +1 -0
  45. package/models/colDef/gridColDef.d.ts +7 -0
  46. package/modern/DataGrid/DataGrid.js +2 -0
  47. package/modern/colDef/gridBooleanOperators.js +1 -1
  48. package/modern/components/GridRow.js +40 -53
  49. package/modern/components/cell/GridCell.js +10 -10
  50. package/modern/components/cell/GridSkeletonCell.js +14 -6
  51. package/modern/components/columnSelection/GridCellCheckboxRenderer.js +6 -4
  52. package/modern/components/columnsManagement/GridColumnsManagement.js +1 -1
  53. package/modern/components/containers/GridRootStyles.js +9 -4
  54. package/modern/components/virtualization/GridBottomContainer.js +1 -1
  55. package/modern/components/virtualization/GridTopContainer.js +1 -1
  56. package/modern/components/virtualization/GridVirtualScroller.js +7 -5
  57. package/modern/components/virtualization/GridVirtualScrollerRenderZone.js +8 -3
  58. package/modern/hooks/core/pipeProcessing/useGridPipeProcessing.js +22 -20
  59. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -8
  60. package/modern/hooks/features/columns/gridColumnsSelector.js +8 -1
  61. package/modern/hooks/features/columns/useGridColumns.js +2 -0
  62. package/modern/hooks/features/dimensions/useGridDimensions.js +1 -0
  63. package/modern/hooks/features/editing/useGridRowEditing.js +1 -2
  64. package/modern/hooks/features/filter/useGridFilter.js +2 -2
  65. package/modern/hooks/features/rows/useGridParamsApi.js +6 -10
  66. package/modern/hooks/features/rows/useGridRows.js +8 -4
  67. package/modern/hooks/features/rows/useGridRowsMeta.js +5 -13
  68. package/modern/hooks/features/sorting/gridSortingUtils.js +9 -1
  69. package/modern/hooks/features/sorting/useGridSorting.js +2 -2
  70. package/modern/hooks/features/virtualization/gridVirtualizationSelectors.js +0 -7
  71. package/modern/hooks/features/virtualization/useGridVirtualScroller.js +85 -136
  72. package/modern/hooks/features/virtualization/useGridVirtualization.js +1 -6
  73. package/modern/hooks/utils/useTimeout.js +13 -5
  74. package/modern/index.js +1 -1
  75. package/modern/internals/index.js +1 -1
  76. package/modern/models/api/gridInfiniteLoaderApi.js +1 -0
  77. package/modern/utils/createSelector.js +12 -20
  78. package/modern/utils/utils.js +9 -0
  79. package/node/DataGrid/DataGrid.js +1 -0
  80. package/node/colDef/gridBooleanOperators.js +1 -1
  81. package/node/components/GridRow.js +40 -53
  82. package/node/components/cell/GridCell.js +10 -10
  83. package/node/components/cell/GridSkeletonCell.js +15 -7
  84. package/node/components/columnSelection/GridCellCheckboxRenderer.js +6 -4
  85. package/node/components/columnsManagement/GridColumnsManagement.js +1 -1
  86. package/node/components/containers/GridRootStyles.js +9 -4
  87. package/node/components/virtualization/GridBottomContainer.js +1 -1
  88. package/node/components/virtualization/GridTopContainer.js +1 -1
  89. package/node/components/virtualization/GridVirtualScroller.js +7 -5
  90. package/node/components/virtualization/GridVirtualScrollerRenderZone.js +7 -2
  91. package/node/hooks/core/pipeProcessing/useGridPipeProcessing.js +22 -20
  92. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +8 -5
  93. package/node/hooks/features/columns/gridColumnsSelector.js +9 -2
  94. package/node/hooks/features/columns/useGridColumns.js +2 -0
  95. package/node/hooks/features/dimensions/useGridDimensions.js +1 -0
  96. package/node/hooks/features/editing/useGridRowEditing.js +1 -2
  97. package/node/hooks/features/filter/useGridFilter.js +2 -2
  98. package/node/hooks/features/rows/useGridParamsApi.js +6 -10
  99. package/node/hooks/features/rows/useGridRows.js +8 -4
  100. package/node/hooks/features/rows/useGridRowsMeta.js +5 -13
  101. package/node/hooks/features/sorting/gridSortingUtils.js +9 -1
  102. package/node/hooks/features/sorting/useGridSorting.js +2 -2
  103. package/node/hooks/features/virtualization/gridVirtualizationSelectors.js +1 -8
  104. package/node/hooks/features/virtualization/useGridVirtualScroller.js +86 -136
  105. package/node/hooks/features/virtualization/useGridVirtualization.js +2 -7
  106. package/node/hooks/utils/useTimeout.js +13 -4
  107. package/node/index.js +1 -1
  108. package/node/internals/index.js +0 -7
  109. package/node/models/api/gridInfiniteLoaderApi.js +5 -0
  110. package/node/utils/createSelector.js +14 -23
  111. package/node/utils/utils.js +12 -1
  112. package/package.json +2 -2
  113. package/utils/createSelector.d.ts +0 -1
  114. package/utils/createSelector.js +12 -22
  115. package/utils/utils.d.ts +4 -0
  116. package/utils/utils.js +9 -0
@@ -1,23 +1,24 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import { unstable_useForkRef as useForkRef } from '@mui/utils';
4
- import { styled } from '@mui/material/styles';
4
+ import { styled, useTheme } from '@mui/material/styles';
5
5
  import { useGridSelector } from '../../utils';
6
6
  import { useGridRootProps } from '../../utils/useGridRootProps';
7
7
  import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
8
8
  import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
9
9
  import { GridColumnHeaderItem } from '../../../components/columnHeaders/GridColumnHeaderItem';
10
10
  import { gridDimensionsSelector } from '../dimensions';
11
- import { gridOffsetsSelector, gridRenderContextColumnsSelector, gridVirtualizationColumnEnabledSelector } from '../virtualization';
11
+ import { gridRenderContextColumnsSelector, gridVirtualizationColumnEnabledSelector } from '../virtualization';
12
+ import { computeOffsetLeft } from '../virtualization/useGridVirtualScroller';
12
13
  import { GridColumnGroupHeader } from '../../../components/columnHeaders/GridColumnGroupHeader';
13
- import { GridPinnedColumnPosition, gridVisiblePinnedColumnDefinitionsSelector } from '../columns';
14
+ import { GridPinnedColumnPosition, gridColumnPositionsSelector, gridVisiblePinnedColumnDefinitionsSelector } from '../columns';
14
15
  import { GridScrollbarFillerCell as ScrollbarFiller } from '../../../components/GridScrollbarFillerCell';
15
16
  import { gridClasses } from '../../../constants/gridClasses';
16
17
  import { jsx as _jsx } from "react/jsx-runtime";
17
18
  import { jsxs as _jsxs } from "react/jsx-runtime";
18
19
  const SpaceFiller = styled('div')({
19
20
  /* GridRootStyles conflict */
20
- '&&': {
21
+ '&&&': {
21
22
  padding: 0,
22
23
  width: 'calc(var(--DataGrid-width) - var(--DataGrid-columnsTotalWidth))'
23
24
  }
@@ -49,14 +50,16 @@ export const useGridColumnHeaders = props => {
49
50
  const [dragCol, setDragCol] = React.useState('');
50
51
  const [resizeCol, setResizeCol] = React.useState('');
51
52
  const apiRef = useGridPrivateApiContext();
53
+ const theme = useTheme();
52
54
  const rootProps = useGridRootProps();
53
55
  const hasVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector);
54
56
  const innerRef = React.useRef(null);
55
57
  const handleInnerRef = useForkRef(innerRefProp, innerRef);
56
58
  const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
57
- const offsets = useGridSelector(apiRef, gridOffsetsSelector);
59
+ const columnPositions = useGridSelector(apiRef, gridColumnPositionsSelector);
58
60
  const renderContext = useGridSelector(apiRef, gridRenderContextColumnsSelector);
59
- const visiblePinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector);
61
+ const pinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector);
62
+ const offsetLeft = computeOffsetLeft(columnPositions, renderContext, theme.direction, pinnedColumns.left.length);
60
63
  React.useEffect(() => {
61
64
  apiRef.current.columnHeadersContainerElementRef.current.scrollLeft = 0;
62
65
  }, [apiRef]);
@@ -88,8 +91,8 @@ export const useGridColumnHeaders = props => {
88
91
  const getFillers = (params, children, leftOverflow, borderTop = false) => {
89
92
  const isPinnedRight = params?.position === GridPinnedColumnPosition.RIGHT;
90
93
  const isNotPinned = params?.position === undefined;
91
- const hasScrollbarFiller = visiblePinnedColumns.right.length > 0 && isPinnedRight || visiblePinnedColumns.right.length === 0 && isNotPinned;
92
- const leftOffsetWidth = offsets.left - leftOverflow;
94
+ const hasScrollbarFiller = pinnedColumns.right.length > 0 && isPinnedRight || pinnedColumns.right.length === 0 && isNotPinned;
95
+ const leftOffsetWidth = offsetLeft - leftOverflow;
93
96
  return /*#__PURE__*/_jsxs(React.Fragment, {
94
97
  children: [isNotPinned && /*#__PURE__*/_jsx("div", {
95
98
  role: "presentation",
@@ -134,4 +134,11 @@ export const gridFilterableColumnLookupSelector = createSelectorMemoized(gridCol
134
134
  acc[col.field] = col;
135
135
  }
136
136
  return acc;
137
- }, {}));
137
+ }, {}));
138
+
139
+ /**
140
+ * Checks if some column has a colSpan field.
141
+ * @category Columns
142
+ * @ignore - Do not document
143
+ */
144
+ export const gridHasColSpanSelector = createSelectorMemoized(gridColumnDefinitionsSelector, columns => columns.some(column => column.colSpan !== undefined));
@@ -44,6 +44,7 @@ export function useGridColumns(apiRef, props) {
44
44
  logger.debug('Updating columns state.');
45
45
  apiRef.current.setState(mergeColumnsState(columnsState));
46
46
  apiRef.current.publishEvent('columnsChange', columnsState.orderedFields);
47
+ apiRef.current.updateRenderContext?.();
47
48
  apiRef.current.forceUpdate();
48
49
  }, [logger, apiRef]);
49
50
 
@@ -73,6 +74,7 @@ export function useGridColumns(apiRef, props) {
73
74
  keepOnlyColumnsToUpsert: false
74
75
  })
75
76
  }));
77
+ apiRef.current.updateRenderContext?.();
76
78
  apiRef.current.forceUpdate();
77
79
  }
78
80
  }, [apiRef]);
@@ -227,6 +227,7 @@ export function useGridDimensions(apiRef, props) {
227
227
  set('--DataGrid-headersTotalHeight', `${dimensions.headersTotalHeight}px`);
228
228
  set('--DataGrid-topContainerHeight', `${dimensions.topContainerHeight}px`);
229
229
  set('--DataGrid-bottomContainerHeight', `${dimensions.bottomContainerHeight}px`);
230
+ set('--height', `${dimensions.rowHeight}px`);
230
231
  }, [root, dimensions]);
231
232
  const isFirstSizing = React.useRef(true);
232
233
  const handleResize = React.useCallback(size => {
@@ -21,7 +21,7 @@ export const useGridRowEditing = (apiRef, props) => {
21
21
  const [rowModesModel, setRowModesModel] = React.useState({});
22
22
  const rowModesModelRef = React.useRef(rowModesModel);
23
23
  const prevRowModesModel = React.useRef({});
24
- const focusTimeout = React.useRef(null);
24
+ const focusTimeout = React.useRef();
25
25
  const nextFocusedCell = React.useRef(null);
26
26
  const {
27
27
  processRowUpdate,
@@ -76,7 +76,6 @@ export const useGridRowEditing = (apiRef, props) => {
76
76
  // focus we check if the next cell that received focus is from a different row.
77
77
  nextFocusedCell.current = null;
78
78
  focusTimeout.current = setTimeout(() => {
79
- focusTimeout.current = null;
80
79
  if (nextFocusedCell.current?.id !== params.id) {
81
80
  // The row might have been deleted during the click
82
81
  if (!apiRef.current.getRow(params.id)) {
@@ -105,7 +105,7 @@ export const useGridFilter = (apiRef, props) => {
105
105
  const filterModel = gridFilterModelSelector(apiRef);
106
106
  const existingItems = [...filterModel.items];
107
107
  items.forEach(item => {
108
- const itemIndex = items.findIndex(filterItem => filterItem.id === item.id);
108
+ const itemIndex = existingItems.findIndex(filterItem => filterItem.id === item.id);
109
109
  if (itemIndex === -1) {
110
110
  existingItems.push(item);
111
111
  } else {
@@ -113,7 +113,7 @@ export const useGridFilter = (apiRef, props) => {
113
113
  }
114
114
  });
115
115
  apiRef.current.setFilterModel(_extends({}, filterModel, {
116
- items
116
+ items: existingItems
117
117
  }), 'upsertFilterItems');
118
118
  }, [apiRef]);
119
119
  const deleteFilterItem = React.useCallback(itemToDelete => {
@@ -31,12 +31,13 @@ export function useGridParamsApi(apiRef) {
31
31
  }, [apiRef]);
32
32
  const getCellParams = React.useCallback((id, field) => {
33
33
  const colDef = apiRef.current.getColumn(field);
34
- const value = apiRef.current.getCellValue(id, field);
35
34
  const row = apiRef.current.getRow(id);
36
35
  const rowNode = apiRef.current.getRowNode(id);
37
36
  if (!row || !rowNode) {
38
37
  throw new MissingRowIdError(`No row with id #${id} found`);
39
38
  }
39
+ const rawValue = row[field];
40
+ const value = colDef?.valueGetter ? colDef.valueGetter(rawValue, row, colDef, apiRef) : rawValue;
40
41
  const cellFocus = gridFocusCellSelector(apiRef);
41
42
  const cellTabIndex = gridTabIndexCellSelector(apiRef);
42
43
  const params = {
@@ -60,19 +61,14 @@ export function useGridParamsApi(apiRef) {
60
61
  }, [apiRef]);
61
62
  const getCellValue = React.useCallback((id, field) => {
62
63
  const colDef = apiRef.current.getColumn(field);
63
- if (!colDef || !colDef.valueGetter) {
64
- const rowModel = apiRef.current.getRow(id);
65
- if (!rowModel) {
66
- throw new MissingRowIdError(`No row with id #${id} found`);
67
- }
68
- return rowModel[field];
69
- }
70
64
  const row = apiRef.current.getRow(id);
71
65
  if (!row) {
72
66
  throw new MissingRowIdError(`No row with id #${id} found`);
73
67
  }
74
- const value = row[colDef.field];
75
- return colDef.valueGetter(value, row, colDef, apiRef);
68
+ if (!colDef || !colDef.valueGetter) {
69
+ return row[field];
70
+ }
71
+ return colDef.valueGetter(row[colDef.field], row, colDef, apiRef);
76
72
  }, [apiRef]);
77
73
  const getRowValue = React.useCallback((row, colDef) => {
78
74
  const field = colDef.field;
@@ -260,13 +260,16 @@ export const useGridRows = (apiRef, props) => {
260
260
  const dataRowIdToIdLookup = _extends({}, gridRowsDataRowIdToIdLookupSelector(apiRef));
261
261
  const rootGroup = tree[GRID_ROOT_GROUP_ID];
262
262
  const rootGroupChildren = [...rootGroup.children];
263
+ const seenIds = new Set();
263
264
  for (let i = 0; i < newRows.length; i += 1) {
264
265
  const rowModel = newRows[i];
265
266
  const rowId = getRowIdFromRowModel(rowModel, props.getRowId, 'A row was provided without id when calling replaceRows().');
266
- const [replacedRowId] = rootGroupChildren.splice(firstRowToRender + i, 1, rowId);
267
- delete dataRowIdToModelLookup[replacedRowId];
268
- delete dataRowIdToIdLookup[replacedRowId];
269
- delete tree[replacedRowId];
267
+ const [removedRowId] = rootGroupChildren.splice(firstRowToRender + i, 1, rowId);
268
+ if (!seenIds.has(removedRowId)) {
269
+ delete dataRowIdToModelLookup[removedRowId];
270
+ delete dataRowIdToIdLookup[removedRowId];
271
+ delete tree[removedRowId];
272
+ }
270
273
  const rowTreeNodeConfig = {
271
274
  id: rowId,
272
275
  depth: 0,
@@ -277,6 +280,7 @@ export const useGridRows = (apiRef, props) => {
277
280
  dataRowIdToModelLookup[rowId] = rowModel;
278
281
  dataRowIdToIdLookup[rowId] = rowId;
279
282
  tree[rowId] = rowTreeNodeConfig;
283
+ seenIds.add(rowId);
280
284
  }
281
285
  tree[GRID_ROOT_GROUP_ID] = _extends({}, rootGroup, {
282
286
  children: rootGroupChildren
@@ -106,14 +106,9 @@ export const useGridRowsMeta = (apiRef, props) => {
106
106
  } else {
107
107
  rowsHeightLookup.current[row.id].needsFirstMeasurement = false;
108
108
  }
109
- const initialHeights = {};
110
- /* eslint-disable-next-line no-restricted-syntax */
111
- for (const key in sizes) {
112
- if (/^base[A-Z]/.test(key)) {
113
- initialHeights[key] = sizes[key];
114
- }
115
- }
116
- initialHeights.baseCenter = baseRowHeight;
109
+ const initialHeights = {
110
+ baseCenter: baseRowHeight
111
+ };
117
112
  if (getRowSpacing) {
118
113
  const indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(row.id);
119
114
  const spacing = getRowSpacing(_extends({}, row, {
@@ -131,19 +126,16 @@ export const useGridRowsMeta = (apiRef, props) => {
131
126
  const positions = [];
132
127
  const currentPageTotalHeight = currentPage.rows.reduce((acc, row) => {
133
128
  positions.push(acc);
134
- let maximumBaseSize = 0;
135
129
  let otherSizes = 0;
136
130
  const processedSizes = calculateRowProcessedSizes(row);
137
131
  /* eslint-disable-next-line no-restricted-syntax, guard-for-in */
138
132
  for (const key in processedSizes) {
139
133
  const value = processedSizes[key];
140
- if (/^base[A-Z]/.test(key)) {
141
- maximumBaseSize = value > maximumBaseSize ? value : maximumBaseSize;
142
- } else {
134
+ if (key !== 'baseCenter') {
143
135
  otherSizes += value;
144
136
  }
145
137
  }
146
- return acc + maximumBaseSize + otherSizes;
138
+ return acc + processedSizes.baseCenter + otherSizes;
147
139
  }, 0);
148
140
  pinnedRows?.top?.forEach(row => {
149
141
  calculateRowProcessedSizes(row);
@@ -28,7 +28,15 @@ const parseSortItem = (sortItem, apiRef) => {
28
28
  if (!column || sortItem.sort === null) {
29
29
  return null;
30
30
  }
31
- const comparator = isDesc(sortItem.sort) ? (...args) => -1 * column.sortComparator(...args) : column.sortComparator;
31
+ let comparator;
32
+ if (column.getSortComparator) {
33
+ comparator = column.getSortComparator(sortItem.sort);
34
+ } else {
35
+ comparator = isDesc(sortItem.sort) ? (...args) => -1 * column.sortComparator(...args) : column.sortComparator;
36
+ }
37
+ if (!comparator) {
38
+ return null;
39
+ }
32
40
  const getSortCellParams = id => ({
33
41
  id,
34
42
  field: column.field,
@@ -40,7 +40,7 @@ export const useGridSorting = (apiRef, props) => {
40
40
  const existingIdx = sortModel.findIndex(c => c.field === field);
41
41
  let newSortModel = [...sortModel];
42
42
  if (existingIdx > -1) {
43
- if (!sortItem) {
43
+ if (sortItem?.sort == null) {
44
44
  newSortModel.splice(existingIdx, 1);
45
45
  } else {
46
46
  newSortModel.splice(existingIdx, 1, sortItem);
@@ -116,7 +116,7 @@ export const useGridSorting = (apiRef, props) => {
116
116
  const sortItem = createSortItem(column, direction);
117
117
  let sortModel;
118
118
  if (!allowMultipleSorting || props.disableMultipleColumnsSorting) {
119
- sortModel = !sortItem ? [] : [sortItem];
119
+ sortModel = sortItem?.sort == null ? [] : [sortItem];
120
120
  } else {
121
121
  sortModel = upsertSortModel(column.field, sortItem);
122
122
  }
@@ -24,13 +24,6 @@ export const gridVirtualizationColumnEnabledSelector = createSelector(gridVirtua
24
24
  */
25
25
  export const gridRenderContextSelector = createSelector(gridVirtualizationSelector, state => state.renderContext);
26
26
 
27
- /**
28
- * Get the offsets
29
- * @category Virtualization
30
- * @ignore - do not document.
31
- */
32
- export const gridOffsetsSelector = createSelector(gridVirtualizationSelector, state => state.offsets);
33
-
34
27
  /**
35
28
  * Get the render context, with only columns filled in.
36
29
  * This is cached, so it can be used to only re-render when the column interval changes.
@@ -3,24 +3,23 @@ import * as React from 'react';
3
3
  import * as ReactDOM from 'react-dom';
4
4
  import { unstable_useEnhancedEffect as useEnhancedEffect, unstable_useEventCallback as useEventCallback } from '@mui/utils';
5
5
  import { useTheme } from '@mui/material/styles';
6
- import { defaultMemoize } from 'reselect';
7
6
  import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
8
7
  import { useGridRootProps } from '../../utils/useGridRootProps';
9
8
  import { useGridSelector } from '../../utils/useGridSelector';
10
- import { useLazyRef } from '../../utils/useLazyRef';
11
9
  import { useResizeObserver } from '../../utils/useResizeObserver';
12
10
  import { useRunOnce } from '../../utils/useRunOnce';
13
- import { gridVisibleColumnDefinitionsSelector, gridVisiblePinnedColumnDefinitionsSelector, gridColumnPositionsSelector } from '../columns/gridColumnsSelector';
11
+ import { gridVisibleColumnDefinitionsSelector, gridVisiblePinnedColumnDefinitionsSelector, gridColumnPositionsSelector, gridHasColSpanSelector } from '../columns/gridColumnsSelector';
14
12
  import { gridDimensionsSelector } from '../dimensions/gridDimensionsSelectors';
15
13
  import { gridPinnedRowsSelector } from '../rows/gridRowsSelector';
16
14
  import { gridFocusCellSelector, gridTabIndexCellSelector } from '../focus/gridFocusStateSelector';
17
15
  import { useGridVisibleRows, getVisibleRows } from '../../utils/useGridVisibleRows';
18
- import { clamp } from '../../../utils/utils';
16
+ import { useGridApiEventHandler } from '../../utils';
17
+ import { clamp, range } from '../../../utils/utils';
19
18
  import { selectedIdsLookupSelector } from '../rowSelection/gridRowSelectionSelector';
20
19
  import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector';
21
20
  import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils';
22
21
  import { getMinimalContentHeight } from '../rows/gridRowsUtils';
23
- import { gridOffsetsSelector, gridRenderContextSelector, gridVirtualizationEnabledSelector, gridVirtualizationColumnEnabledSelector } from './gridVirtualizationSelectors';
22
+ import { gridRenderContextSelector, gridVirtualizationEnabledSelector, gridVirtualizationColumnEnabledSelector } from './gridVirtualizationSelectors';
24
23
  import { EMPTY_RENDER_CONTEXT } from './useGridVirtualization';
25
24
  import { jsx as _jsx } from "react/jsx-runtime";
26
25
  export const EMPTY_DETAIL_PANELS = Object.freeze(new Map());
@@ -49,40 +48,29 @@ export const useGridVirtualScroller = () => {
49
48
  const scrollbarHorizontalRef = React.useRef(null);
50
49
  const contentHeight = dimensions.contentSize.height;
51
50
  const columnsTotalWidth = dimensions.columnsTotalWidth;
51
+ const hasColSpan = useGridSelector(apiRef, gridHasColSpanSelector);
52
52
  useResizeObserver(mainRef, () => apiRef.current.resize());
53
53
  const previousContext = React.useRef(EMPTY_RENDER_CONTEXT);
54
54
  const previousRowContext = React.useRef(EMPTY_RENDER_CONTEXT);
55
- const offsets = useGridSelector(apiRef, gridOffsetsSelector);
56
55
  const renderContext = useGridSelector(apiRef, gridRenderContextSelector);
57
56
  const scrollPosition = React.useRef({
58
57
  top: 0,
59
58
  left: 0
60
59
  }).current;
61
60
  const prevTotalWidth = React.useRef(columnsTotalWidth);
62
- const getRenderedColumns = useLazyRef(createGetRenderedColumns).current;
63
- const indexOfRowWithFocusedCell = React.useMemo(() => {
64
- if (cellFocus !== null) {
65
- return currentPage.rows.findIndex(row => row.id === cellFocus.id);
66
- }
67
- return -1;
68
- }, [cellFocus, currentPage.rows]);
69
- const indexOfColumnWithFocusedCell = React.useMemo(() => {
70
- if (cellFocus !== null) {
71
- return visibleColumns.findIndex(column => column.field === cellFocus.field);
72
- }
73
- return -1;
74
- }, [cellFocus, visibleColumns]);
61
+ const focusedCell = {
62
+ rowIndex: React.useMemo(() => cellFocus ? currentPage.rows.findIndex(row => row.id === cellFocus.id) : -1, [cellFocus, currentPage.rows]),
63
+ columnIndex: React.useMemo(() => cellFocus ? visibleColumns.findIndex(column => column.field === cellFocus.field) : -1, [cellFocus, visibleColumns])
64
+ };
75
65
  const updateRenderContext = React.useCallback((nextRenderContext, rawRenderContext) => {
76
66
  if (areRenderContextsEqual(nextRenderContext, apiRef.current.state.virtualization.renderContext)) {
77
67
  return;
78
68
  }
79
69
  const didRowsIntervalChange = nextRenderContext.firstRowIndex !== previousRowContext.current.firstRowIndex || nextRenderContext.lastRowIndex !== previousRowContext.current.lastRowIndex;
80
- const nextOffsets = computeOffsets(apiRef, nextRenderContext, theme.direction, pinnedColumns.left.length);
81
70
  apiRef.current.setState(state => {
82
71
  return _extends({}, state, {
83
72
  virtualization: _extends({}, state.virtualization, {
84
- renderContext: nextRenderContext,
85
- offsets: nextOffsets
73
+ renderContext: nextRenderContext
86
74
  })
87
75
  });
88
76
  });
@@ -96,7 +84,7 @@ export const useGridVirtualScroller = () => {
96
84
  }
97
85
  previousContext.current = rawRenderContext;
98
86
  prevTotalWidth.current = dimensions.columnsTotalWidth;
99
- }, [apiRef, pinnedColumns.left.length, theme.direction, dimensions.isReady, dimensions.columnsTotalWidth]);
87
+ }, [apiRef, dimensions.isReady, dimensions.columnsTotalWidth]);
100
88
  const triggerUpdateRenderContext = () => {
101
89
  const inputs = inputsSelector(apiRef, rootProps, enabled, enabledForColumns);
102
90
  const [nextRenderContext, rawRenderContext] = computeRenderContext(inputs, scrollPosition);
@@ -157,9 +145,12 @@ export const useGridVirtualScroller = () => {
157
145
  const handleTouchMove = useEventCallback(event => {
158
146
  apiRef.current.publishEvent('virtualScrollerTouchMove', {}, event);
159
147
  });
160
- const minFirstColumn = pinnedColumns.left.length;
161
- const maxLastColumn = visibleColumns.length - pinnedColumns.right.length;
162
148
  const getRows = (params = {}) => {
149
+ if (!params.rows && !currentPage.range) {
150
+ return [];
151
+ }
152
+ const columnPositions = gridColumnPositionsSelector(apiRef);
153
+ const currentRenderContext = params.renderContext ?? renderContext;
163
154
  const isLastSection = !hasBottomPinnedRows && params.position === undefined || hasBottomPinnedRows && params.position === 'bottom';
164
155
  const isPinnedSection = params.position !== undefined;
165
156
  let rowIndexOffset;
@@ -176,77 +167,57 @@ export const useGridVirtualScroller = () => {
176
167
  rowIndexOffset = pinnedRows.top.length;
177
168
  break;
178
169
  }
179
- const firstRowToRender = renderContext.firstRowIndex;
180
- const lastRowToRender = renderContext.lastRowIndex;
181
- const firstColumnToRender = renderContext.firstColumnIndex;
182
- const lastColumnToRender = renderContext.lastColumnIndex;
183
- if (!params.rows && !currentPage.range) {
184
- return [];
185
- }
186
- const renderedRows = params.rows ?? currentPage.rows.slice(firstRowToRender, lastRowToRender);
187
-
188
- // If the selected row is not within the current range of rows being displayed,
189
- // we need to render it at either the top or bottom of the rows,
190
- // depending on whether it is above or below the range.
191
- let isRowWithFocusedCellNotInRange = false;
192
- if (!isPinnedSection && indexOfRowWithFocusedCell > -1 && (firstRowToRender > indexOfRowWithFocusedCell || lastRowToRender < indexOfRowWithFocusedCell)) {
193
- isRowWithFocusedCellNotInRange = true;
194
- const rowWithFocusedCell = currentPage.rows[indexOfRowWithFocusedCell];
195
- if (indexOfRowWithFocusedCell > firstRowToRender) {
196
- renderedRows.push(rowWithFocusedCell);
197
- } else {
198
- renderedRows.unshift(rowWithFocusedCell);
170
+ const rowModels = params.rows ?? currentPage.rows;
171
+ const firstRowToRender = currentRenderContext.firstRowIndex;
172
+ const lastRowToRender = Math.min(currentRenderContext.lastRowIndex, rowModels.length);
173
+ const rowIndexes = params.rows ? range(0, params.rows.length) : range(firstRowToRender, lastRowToRender);
174
+ let virtualRowIndex = -1;
175
+ if (!isPinnedSection && focusedCell.rowIndex !== -1) {
176
+ if (focusedCell.rowIndex < firstRowToRender) {
177
+ virtualRowIndex = focusedCell.rowIndex;
178
+ rowIndexes.unshift(virtualRowIndex);
199
179
  }
200
- }
201
- let isColumnWihFocusedCellNotInRange = false;
202
- if (!isPinnedSection && (firstColumnToRender > indexOfColumnWithFocusedCell || lastColumnToRender < indexOfColumnWithFocusedCell)) {
203
- isColumnWihFocusedCellNotInRange = true;
204
- }
205
- const {
206
- focusedCellColumnIndexNotInRange,
207
- renderedColumns
208
- } = getRenderedColumns(visibleColumns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn, isColumnWihFocusedCellNotInRange ? indexOfColumnWithFocusedCell : -1);
209
- renderedRows.forEach(row => {
210
- apiRef.current.calculateColSpan({
211
- rowId: row.id,
212
- minFirstColumn,
213
- maxLastColumn,
214
- columns: visibleColumns
215
- });
216
- if (pinnedColumns.left.length > 0) {
217
- apiRef.current.calculateColSpan({
218
- rowId: row.id,
219
- minFirstColumn: 0,
220
- maxLastColumn: pinnedColumns.left.length,
221
- columns: visibleColumns
222
- });
223
- }
224
- if (pinnedColumns.right.length > 0) {
225
- apiRef.current.calculateColSpan({
226
- rowId: row.id,
227
- minFirstColumn: visibleColumns.length - pinnedColumns.right.length,
228
- maxLastColumn: visibleColumns.length,
229
- columns: visibleColumns
230
- });
180
+ if (focusedCell.rowIndex >= lastRowToRender) {
181
+ virtualRowIndex = focusedCell.rowIndex;
182
+ rowIndexes.push(virtualRowIndex);
231
183
  }
232
- });
184
+ }
233
185
  const rows = [];
234
186
  const rowProps = rootProps.slotProps?.row;
235
- let isRowWithFocusedCellRendered = false;
236
- for (let i = 0; i < renderedRows.length; i += 1) {
187
+ rowIndexes.forEach(rowIndexInPage => {
237
188
  const {
238
189
  id,
239
190
  model
240
- } = renderedRows[i];
241
- const rowIndexInPage = (currentPage?.range?.firstRowIndex || 0) + firstRowToRender + i;
242
- let index = rowIndexOffset + rowIndexInPage;
243
- if (isRowWithFocusedCellNotInRange && cellFocus?.id === id) {
244
- index = indexOfRowWithFocusedCell;
245
- isRowWithFocusedCellRendered = true;
246
- } else if (isRowWithFocusedCellRendered) {
247
- index -= 1;
191
+ } = rowModels[rowIndexInPage];
192
+
193
+ // NOTE: This is an expensive feature, the colSpan code could be optimized.
194
+ if (hasColSpan) {
195
+ const minFirstColumn = pinnedColumns.left.length;
196
+ const maxLastColumn = visibleColumns.length - pinnedColumns.right.length;
197
+ apiRef.current.calculateColSpan({
198
+ rowId: id,
199
+ minFirstColumn,
200
+ maxLastColumn,
201
+ columns: visibleColumns
202
+ });
203
+ if (pinnedColumns.left.length > 0) {
204
+ apiRef.current.calculateColSpan({
205
+ rowId: id,
206
+ minFirstColumn: 0,
207
+ maxLastColumn: pinnedColumns.left.length,
208
+ columns: visibleColumns
209
+ });
210
+ }
211
+ if (pinnedColumns.right.length > 0) {
212
+ apiRef.current.calculateColSpan({
213
+ rowId: id,
214
+ minFirstColumn: visibleColumns.length - pinnedColumns.right.length,
215
+ maxLastColumn: visibleColumns.length,
216
+ columns: visibleColumns
217
+ });
218
+ }
248
219
  }
249
- const isRowNotVisible = isRowWithFocusedCellNotInRange && cellFocus.id === id;
220
+ const hasFocus = cellFocus?.id === id;
250
221
  const baseRowHeight = !apiRef.current.rowHasAutoHeight(id) ? apiRef.current.unstable_getRowHeight(id) : 'auto';
251
222
  let isSelected;
252
223
  if (selectedRowsLookup[id] == null) {
@@ -262,47 +233,51 @@ export const useGridVirtualScroller = () => {
262
233
  if (isLastSection) {
263
234
  if (!isPinnedSection) {
264
235
  const lastIndex = currentPage.rows.length - 1;
265
- const isLastVisibleRowIndex = isRowWithFocusedCellNotInRange ? firstRowToRender + i === lastIndex + 1 : firstRowToRender + i === lastIndex;
236
+ const isLastVisibleRowIndex = rowIndexInPage === lastIndex;
266
237
  if (isLastVisibleRowIndex) {
267
238
  isLastVisible = true;
268
239
  }
269
240
  } else {
270
- isLastVisible = i === renderedRows.length - 1;
241
+ isLastVisible = rowIndexInPage === rowModels.length - 1;
271
242
  }
272
243
  }
273
- const focusedCell = cellFocus !== null && cellFocus.id === id ? cellFocus.field : null;
274
- const columnWithFocusedCellNotInRange = focusedCellColumnIndexNotInRange !== undefined && visibleColumns[focusedCellColumnIndexNotInRange];
275
- const renderedColumnsWithFocusedCell = columnWithFocusedCellNotInRange && focusedCell ? [columnWithFocusedCellNotInRange, ...renderedColumns] : renderedColumns;
244
+ const isVirtualRow = rowIndexInPage === virtualRowIndex;
245
+ const isNotVisible = isVirtualRow;
276
246
  let tabbableCell = null;
277
247
  if (cellTabIndex !== null && cellTabIndex.id === id) {
278
248
  const cellParams = apiRef.current.getCellParams(id, cellTabIndex.field);
279
249
  tabbableCell = cellParams.cellMode === 'view' ? cellTabIndex.field : null;
280
250
  }
251
+ const offsetLeft = computeOffsetLeft(columnPositions, currentRenderContext, theme.direction, pinnedColumns.left.length);
252
+ const rowIndex = (currentPage?.range?.firstRowIndex || 0) + rowIndexOffset + rowIndexInPage;
281
253
  rows.push( /*#__PURE__*/_jsx(rootProps.slots.row, _extends({
282
254
  row: model,
283
255
  rowId: id,
284
- index: index,
256
+ index: rowIndex,
257
+ selected: isSelected,
258
+ offsetTop: params.rows ? undefined : rowsMeta.positions[rowIndexInPage],
259
+ offsetLeft: offsetLeft,
260
+ dimensions: dimensions,
285
261
  rowHeight: baseRowHeight,
286
- focusedCell: focusedCell,
287
262
  tabbableCell: tabbableCell,
288
- focusedCellColumnIndexNotInRange: focusedCellColumnIndexNotInRange,
289
- renderedColumns: renderedColumnsWithFocusedCell,
290
- visibleColumns: visibleColumns,
291
263
  pinnedColumns: pinnedColumns,
292
- firstColumnToRender: firstColumnToRender,
293
- lastColumnToRender: lastColumnToRender,
294
- selected: isSelected,
295
- offsets: offsets,
296
- dimensions: dimensions,
264
+ visibleColumns: visibleColumns,
265
+ renderContext: currentRenderContext,
266
+ focusedColumnIndex: hasFocus ? focusedCell.columnIndex : undefined,
297
267
  isFirstVisible: isFirstVisible,
298
268
  isLastVisible: isLastVisible,
299
- isNotVisible: isRowNotVisible
269
+ isNotVisible: isNotVisible
300
270
  }, rowProps), id));
301
271
  const panel = panels.get(id);
302
272
  if (panel) {
303
273
  rows.push(panel);
304
274
  }
305
- }
275
+ if (isLastVisible) {
276
+ rows.push(apiRef.current.getInfiniteLoadingTriggerElement?.({
277
+ lastRowId: id
278
+ }));
279
+ }
280
+ });
306
281
  return rows;
307
282
  };
308
283
  const needsHorizontalScrollbar = outerSize.width && columnsTotalWidth >= outerSize.width;
@@ -355,6 +330,9 @@ export const useGridVirtualScroller = () => {
355
330
  apiRef.current.register('private', {
356
331
  updateRenderContext: forceUpdateRenderContext
357
332
  });
333
+ useGridApiEventHandler(apiRef, 'columnsChange', forceUpdateRenderContext);
334
+ useGridApiEventHandler(apiRef, 'filteredRowsSet', forceUpdateRenderContext);
335
+ useGridApiEventHandler(apiRef, 'rowExpansionChange', forceUpdateRenderContext);
358
336
  return {
359
337
  renderContext,
360
338
  setPanels,
@@ -388,29 +366,6 @@ export const useGridVirtualScroller = () => {
388
366
  })
389
367
  };
390
368
  };
391
- function createGetRenderedColumns() {
392
- return defaultMemoize((columns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn, indexOfColumnWithFocusedCell) => {
393
- // If the selected column is not within the current range of columns being displayed,
394
- // we need to render it at either the left or right of the columns,
395
- // depending on whether it is above or below the range.
396
- let focusedCellColumnIndexNotInRange;
397
- const renderedColumns = columns.slice(firstColumnToRender, lastColumnToRender);
398
- if (indexOfColumnWithFocusedCell > -1) {
399
- // check if it is not on the left pinned column.
400
- if (firstColumnToRender > indexOfColumnWithFocusedCell && indexOfColumnWithFocusedCell >= minFirstColumn) {
401
- focusedCellColumnIndexNotInRange = indexOfColumnWithFocusedCell;
402
- }
403
- // check if it is not on the right pinned column.
404
- else if (lastColumnToRender < indexOfColumnWithFocusedCell && indexOfColumnWithFocusedCell < maxLastColumn) {
405
- focusedCellColumnIndexNotInRange = indexOfColumnWithFocusedCell;
406
- }
407
- }
408
- return {
409
- focusedCellColumnIndexNotInRange,
410
- renderedColumns
411
- };
412
- });
413
- }
414
369
  function inputsSelector(apiRef, rootProps, enabled, enabledForColumns) {
415
370
  const dimensions = gridDimensionsSelector(apiRef.current.state);
416
371
  const currentPage = getVisibleRows(apiRef, rootProps);
@@ -588,14 +543,8 @@ export function areRenderContextsEqual(context1, context2) {
588
543
  }
589
544
  return context1.firstRowIndex === context2.firstRowIndex && context1.lastRowIndex === context2.lastRowIndex && context1.firstColumnIndex === context2.firstColumnIndex && context1.lastColumnIndex === context2.lastColumnIndex;
590
545
  }
591
- function computeOffsets(apiRef, renderContext, direction, pinnedLeftLength) {
546
+ export function computeOffsetLeft(columnPositions, renderContext, direction, pinnedLeftLength) {
592
547
  const factor = direction === 'ltr' ? 1 : -1;
593
- const rowPositions = gridRowsMetaSelector(apiRef.current.state).positions;
594
- const columnPositions = gridColumnPositionsSelector(apiRef);
595
- const top = rowPositions[renderContext.firstRowIndex] ?? 0;
596
548
  const left = factor * (columnPositions[renderContext.firstColumnIndex] ?? 0) - (columnPositions[pinnedLeftLength] ?? 0);
597
- return {
598
- top,
599
- left
600
- };
549
+ return left;
601
550
  }