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

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 (90) hide show
  1. package/CHANGELOG.md +138 -14
  2. package/DataGrid/DataGrid.js +2 -0
  3. package/colDef/gridBooleanOperators.js +1 -1
  4. package/components/GridRow.d.ts +7 -9
  5. package/components/GridRow.js +36 -47
  6. package/components/cell/GridCell.d.ts +2 -1
  7. package/components/cell/GridCell.js +7 -3
  8. package/components/cell/GridSkeletonCell.d.ts +3 -2
  9. package/components/cell/GridSkeletonCell.js +14 -6
  10. package/components/columnSelection/GridCellCheckboxRenderer.js +6 -4
  11. package/components/columnsManagement/GridColumnsManagement.js +1 -1
  12. package/components/containers/GridRootStyles.js +9 -2
  13. package/components/virtualization/GridBottomContainer.js +1 -1
  14. package/components/virtualization/GridTopContainer.js +1 -1
  15. package/components/virtualization/GridVirtualScroller.js +2 -2
  16. package/components/virtualization/GridVirtualScrollerRenderZone.js +9 -3
  17. package/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -8
  18. package/hooks/features/columns/gridColumnsSelector.d.ts +6 -0
  19. package/hooks/features/columns/gridColumnsSelector.js +8 -1
  20. package/hooks/features/columns/useGridColumns.js +4 -0
  21. package/hooks/features/editing/useGridRowEditing.js +1 -2
  22. package/hooks/features/filter/useGridFilter.js +2 -2
  23. package/hooks/features/rows/useGridRows.js +8 -4
  24. package/hooks/features/rows/useGridRowsMeta.js +5 -13
  25. package/hooks/features/sorting/gridSortingUtils.js +9 -1
  26. package/hooks/features/virtualization/gridVirtualizationSelectors.d.ts +0 -9
  27. package/hooks/features/virtualization/gridVirtualizationSelectors.js +0 -7
  28. package/hooks/features/virtualization/useGridVirtualScroller.d.ts +3 -0
  29. package/hooks/features/virtualization/useGridVirtualScroller.js +82 -138
  30. package/hooks/features/virtualization/useGridVirtualization.d.ts +0 -8
  31. package/hooks/features/virtualization/useGridVirtualization.js +1 -6
  32. package/hooks/utils/useTimeout.d.ts +5 -3
  33. package/hooks/utils/useTimeout.js +13 -5
  34. package/index.js +1 -1
  35. package/models/colDef/gridColDef.d.ts +7 -0
  36. package/modern/DataGrid/DataGrid.js +2 -0
  37. package/modern/colDef/gridBooleanOperators.js +1 -1
  38. package/modern/components/GridRow.js +35 -46
  39. package/modern/components/cell/GridCell.js +7 -3
  40. package/modern/components/cell/GridSkeletonCell.js +14 -6
  41. package/modern/components/columnSelection/GridCellCheckboxRenderer.js +6 -4
  42. package/modern/components/columnsManagement/GridColumnsManagement.js +1 -1
  43. package/modern/components/containers/GridRootStyles.js +9 -2
  44. package/modern/components/virtualization/GridBottomContainer.js +1 -1
  45. package/modern/components/virtualization/GridTopContainer.js +1 -1
  46. package/modern/components/virtualization/GridVirtualScroller.js +2 -2
  47. package/modern/components/virtualization/GridVirtualScrollerRenderZone.js +8 -3
  48. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -8
  49. package/modern/hooks/features/columns/gridColumnsSelector.js +8 -1
  50. package/modern/hooks/features/columns/useGridColumns.js +2 -0
  51. package/modern/hooks/features/editing/useGridRowEditing.js +1 -2
  52. package/modern/hooks/features/filter/useGridFilter.js +2 -2
  53. package/modern/hooks/features/rows/useGridRows.js +8 -4
  54. package/modern/hooks/features/rows/useGridRowsMeta.js +5 -13
  55. package/modern/hooks/features/sorting/gridSortingUtils.js +9 -1
  56. package/modern/hooks/features/virtualization/gridVirtualizationSelectors.js +0 -7
  57. package/modern/hooks/features/virtualization/useGridVirtualScroller.js +80 -136
  58. package/modern/hooks/features/virtualization/useGridVirtualization.js +1 -6
  59. package/modern/hooks/utils/useTimeout.js +13 -5
  60. package/modern/index.js +1 -1
  61. package/modern/utils/utils.js +9 -0
  62. package/node/DataGrid/DataGrid.js +1 -0
  63. package/node/colDef/gridBooleanOperators.js +1 -1
  64. package/node/components/GridRow.js +35 -46
  65. package/node/components/cell/GridCell.js +7 -3
  66. package/node/components/cell/GridSkeletonCell.js +15 -7
  67. package/node/components/columnSelection/GridCellCheckboxRenderer.js +6 -4
  68. package/node/components/columnsManagement/GridColumnsManagement.js +1 -1
  69. package/node/components/containers/GridRootStyles.js +9 -2
  70. package/node/components/virtualization/GridBottomContainer.js +1 -1
  71. package/node/components/virtualization/GridTopContainer.js +1 -1
  72. package/node/components/virtualization/GridVirtualScroller.js +2 -2
  73. package/node/components/virtualization/GridVirtualScrollerRenderZone.js +7 -2
  74. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +8 -5
  75. package/node/hooks/features/columns/gridColumnsSelector.js +9 -2
  76. package/node/hooks/features/columns/useGridColumns.js +2 -0
  77. package/node/hooks/features/editing/useGridRowEditing.js +1 -2
  78. package/node/hooks/features/filter/useGridFilter.js +2 -2
  79. package/node/hooks/features/rows/useGridRows.js +8 -4
  80. package/node/hooks/features/rows/useGridRowsMeta.js +5 -13
  81. package/node/hooks/features/sorting/gridSortingUtils.js +9 -1
  82. package/node/hooks/features/virtualization/gridVirtualizationSelectors.js +1 -8
  83. package/node/hooks/features/virtualization/useGridVirtualScroller.js +81 -136
  84. package/node/hooks/features/virtualization/useGridVirtualization.js +2 -7
  85. package/node/hooks/utils/useTimeout.js +13 -4
  86. package/node/index.js +1 -1
  87. package/node/utils/utils.js +12 -1
  88. package/package.json +1 -1
  89. package/utils/utils.d.ts +4 -0
  90. package/utils/utils.js +9 -0
@@ -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,10 +145,13 @@ 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 = {}) => {
163
- var _params$rows, _rootProps$slotProps;
149
+ var _params$renderContext, _params$rows, _rootProps$slotProps;
150
+ if (!params.rows && !currentPage.range) {
151
+ return [];
152
+ }
153
+ const columnPositions = gridColumnPositionsSelector(apiRef);
154
+ const currentRenderContext = (_params$renderContext = params.renderContext) != null ? _params$renderContext : renderContext;
164
155
  const isLastSection = !hasBottomPinnedRows && params.position === undefined || hasBottomPinnedRows && params.position === 'bottom';
165
156
  const isPinnedSection = params.position !== undefined;
166
157
  let rowIndexOffset;
@@ -177,78 +168,58 @@ export const useGridVirtualScroller = () => {
177
168
  rowIndexOffset = pinnedRows.top.length;
178
169
  break;
179
170
  }
180
- const firstRowToRender = renderContext.firstRowIndex;
181
- const lastRowToRender = renderContext.lastRowIndex;
182
- const firstColumnToRender = renderContext.firstColumnIndex;
183
- const lastColumnToRender = renderContext.lastColumnIndex;
184
- if (!params.rows && !currentPage.range) {
185
- return [];
186
- }
187
- const renderedRows = (_params$rows = params.rows) != null ? _params$rows : currentPage.rows.slice(firstRowToRender, lastRowToRender);
188
-
189
- // If the selected row is not within the current range of rows being displayed,
190
- // we need to render it at either the top or bottom of the rows,
191
- // depending on whether it is above or below the range.
192
- let isRowWithFocusedCellNotInRange = false;
193
- if (!isPinnedSection && indexOfRowWithFocusedCell > -1 && (firstRowToRender > indexOfRowWithFocusedCell || lastRowToRender < indexOfRowWithFocusedCell)) {
194
- isRowWithFocusedCellNotInRange = true;
195
- const rowWithFocusedCell = currentPage.rows[indexOfRowWithFocusedCell];
196
- if (indexOfRowWithFocusedCell > firstRowToRender) {
197
- renderedRows.push(rowWithFocusedCell);
198
- } else {
199
- renderedRows.unshift(rowWithFocusedCell);
200
- }
201
- }
202
- let isColumnWihFocusedCellNotInRange = false;
203
- if (!isPinnedSection && (firstColumnToRender > indexOfColumnWithFocusedCell || lastColumnToRender < indexOfColumnWithFocusedCell)) {
204
- isColumnWihFocusedCellNotInRange = true;
205
- }
206
- const {
207
- focusedCellColumnIndexNotInRange,
208
- renderedColumns
209
- } = getRenderedColumns(visibleColumns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn, isColumnWihFocusedCellNotInRange ? indexOfColumnWithFocusedCell : -1);
210
- renderedRows.forEach(row => {
211
- apiRef.current.calculateColSpan({
212
- rowId: row.id,
213
- minFirstColumn,
214
- maxLastColumn,
215
- columns: visibleColumns
216
- });
217
- if (pinnedColumns.left.length > 0) {
218
- apiRef.current.calculateColSpan({
219
- rowId: row.id,
220
- minFirstColumn: 0,
221
- maxLastColumn: pinnedColumns.left.length,
222
- columns: visibleColumns
223
- });
171
+ const rowModels = (_params$rows = params.rows) != null ? _params$rows : currentPage.rows;
172
+ const firstRowToRender = currentRenderContext.firstRowIndex;
173
+ const lastRowToRender = Math.min(currentRenderContext.lastRowIndex, rowModels.length);
174
+ const rowIndexes = params.rows ? range(0, params.rows.length) : range(firstRowToRender, lastRowToRender);
175
+ let virtualRowIndex = -1;
176
+ if (!isPinnedSection && focusedCell.rowIndex !== -1) {
177
+ if (focusedCell.rowIndex < firstRowToRender) {
178
+ virtualRowIndex = focusedCell.rowIndex;
179
+ rowIndexes.unshift(virtualRowIndex);
224
180
  }
225
- if (pinnedColumns.right.length > 0) {
226
- apiRef.current.calculateColSpan({
227
- rowId: row.id,
228
- minFirstColumn: visibleColumns.length - pinnedColumns.right.length,
229
- maxLastColumn: visibleColumns.length,
230
- columns: visibleColumns
231
- });
181
+ if (focusedCell.rowIndex >= lastRowToRender) {
182
+ virtualRowIndex = focusedCell.rowIndex;
183
+ rowIndexes.push(virtualRowIndex);
232
184
  }
233
- });
185
+ }
234
186
  const rows = [];
235
187
  const rowProps = (_rootProps$slotProps = rootProps.slotProps) == null ? void 0 : _rootProps$slotProps.row;
236
- let isRowWithFocusedCellRendered = false;
237
- for (let i = 0; i < renderedRows.length; i += 1) {
188
+ rowIndexes.forEach(rowIndexInPage => {
238
189
  var _currentPage$range;
239
190
  const {
240
191
  id,
241
192
  model
242
- } = renderedRows[i];
243
- const rowIndexInPage = ((currentPage == null || (_currentPage$range = currentPage.range) == null ? void 0 : _currentPage$range.firstRowIndex) || 0) + firstRowToRender + i;
244
- let index = rowIndexOffset + rowIndexInPage;
245
- if (isRowWithFocusedCellNotInRange && (cellFocus == null ? void 0 : cellFocus.id) === id) {
246
- index = indexOfRowWithFocusedCell;
247
- isRowWithFocusedCellRendered = true;
248
- } else if (isRowWithFocusedCellRendered) {
249
- index -= 1;
193
+ } = rowModels[rowIndexInPage];
194
+
195
+ // NOTE: This is an expensive feature, the colSpan code could be optimized.
196
+ if (hasColSpan) {
197
+ const minFirstColumn = pinnedColumns.left.length;
198
+ const maxLastColumn = visibleColumns.length - pinnedColumns.right.length;
199
+ apiRef.current.calculateColSpan({
200
+ rowId: id,
201
+ minFirstColumn,
202
+ maxLastColumn,
203
+ columns: visibleColumns
204
+ });
205
+ if (pinnedColumns.left.length > 0) {
206
+ apiRef.current.calculateColSpan({
207
+ rowId: id,
208
+ minFirstColumn: 0,
209
+ maxLastColumn: pinnedColumns.left.length,
210
+ columns: visibleColumns
211
+ });
212
+ }
213
+ if (pinnedColumns.right.length > 0) {
214
+ apiRef.current.calculateColSpan({
215
+ rowId: id,
216
+ minFirstColumn: visibleColumns.length - pinnedColumns.right.length,
217
+ maxLastColumn: visibleColumns.length,
218
+ columns: visibleColumns
219
+ });
220
+ }
250
221
  }
251
- const isRowNotVisible = isRowWithFocusedCellNotInRange && cellFocus.id === id;
222
+ const hasFocus = (cellFocus == null ? void 0 : cellFocus.id) === id;
252
223
  const baseRowHeight = !apiRef.current.rowHasAutoHeight(id) ? apiRef.current.unstable_getRowHeight(id) : 'auto';
253
224
  let isSelected;
254
225
  if (selectedRowsLookup[id] == null) {
@@ -264,47 +235,46 @@ export const useGridVirtualScroller = () => {
264
235
  if (isLastSection) {
265
236
  if (!isPinnedSection) {
266
237
  const lastIndex = currentPage.rows.length - 1;
267
- const isLastVisibleRowIndex = isRowWithFocusedCellNotInRange ? firstRowToRender + i === lastIndex + 1 : firstRowToRender + i === lastIndex;
238
+ const isLastVisibleRowIndex = rowIndexInPage === lastIndex;
268
239
  if (isLastVisibleRowIndex) {
269
240
  isLastVisible = true;
270
241
  }
271
242
  } else {
272
- isLastVisible = i === renderedRows.length - 1;
243
+ isLastVisible = rowIndexInPage === rowModels.length - 1;
273
244
  }
274
245
  }
275
- const focusedCell = cellFocus !== null && cellFocus.id === id ? cellFocus.field : null;
276
- const columnWithFocusedCellNotInRange = focusedCellColumnIndexNotInRange !== undefined && visibleColumns[focusedCellColumnIndexNotInRange];
277
- const renderedColumnsWithFocusedCell = columnWithFocusedCellNotInRange && focusedCell ? [columnWithFocusedCellNotInRange, ...renderedColumns] : renderedColumns;
246
+ const isVirtualRow = rowIndexInPage === virtualRowIndex;
247
+ const isNotVisible = isVirtualRow;
278
248
  let tabbableCell = null;
279
249
  if (cellTabIndex !== null && cellTabIndex.id === id) {
280
250
  const cellParams = apiRef.current.getCellParams(id, cellTabIndex.field);
281
251
  tabbableCell = cellParams.cellMode === 'view' ? cellTabIndex.field : null;
282
252
  }
253
+ const offsetLeft = computeOffsetLeft(columnPositions, currentRenderContext, theme.direction, pinnedColumns.left.length);
254
+ const rowIndex = ((currentPage == null || (_currentPage$range = currentPage.range) == null ? void 0 : _currentPage$range.firstRowIndex) || 0) + rowIndexOffset + rowIndexInPage;
283
255
  rows.push( /*#__PURE__*/_jsx(rootProps.slots.row, _extends({
284
256
  row: model,
285
257
  rowId: id,
286
- index: index,
258
+ index: rowIndex,
259
+ selected: isSelected,
260
+ offsetTop: params.rows ? undefined : rowsMeta.positions[rowIndexInPage],
261
+ offsetLeft: offsetLeft,
262
+ dimensions: dimensions,
287
263
  rowHeight: baseRowHeight,
288
- focusedCell: focusedCell,
289
264
  tabbableCell: tabbableCell,
290
- focusedCellColumnIndexNotInRange: focusedCellColumnIndexNotInRange,
291
- renderedColumns: renderedColumnsWithFocusedCell,
292
- visibleColumns: visibleColumns,
293
265
  pinnedColumns: pinnedColumns,
294
- firstColumnToRender: firstColumnToRender,
295
- lastColumnToRender: lastColumnToRender,
296
- selected: isSelected,
297
- offsets: offsets,
298
- dimensions: dimensions,
266
+ visibleColumns: visibleColumns,
267
+ renderContext: currentRenderContext,
268
+ focusedColumnIndex: hasFocus ? focusedCell.columnIndex : undefined,
299
269
  isFirstVisible: isFirstVisible,
300
270
  isLastVisible: isLastVisible,
301
- isNotVisible: isRowNotVisible
271
+ isNotVisible: isNotVisible
302
272
  }, rowProps), id));
303
273
  const panel = panels.get(id);
304
274
  if (panel) {
305
275
  rows.push(panel);
306
276
  }
307
- }
277
+ });
308
278
  return rows;
309
279
  };
310
280
  const needsHorizontalScrollbar = outerSize.width && columnsTotalWidth >= outerSize.width;
@@ -357,6 +327,9 @@ export const useGridVirtualScroller = () => {
357
327
  apiRef.current.register('private', {
358
328
  updateRenderContext: forceUpdateRenderContext
359
329
  });
330
+ useGridApiEventHandler(apiRef, 'columnsChange', forceUpdateRenderContext);
331
+ useGridApiEventHandler(apiRef, 'filteredRowsSet', forceUpdateRenderContext);
332
+ useGridApiEventHandler(apiRef, 'rowExpansionChange', forceUpdateRenderContext);
360
333
  return {
361
334
  renderContext,
362
335
  setPanels,
@@ -390,29 +363,6 @@ export const useGridVirtualScroller = () => {
390
363
  })
391
364
  };
392
365
  };
393
- function createGetRenderedColumns() {
394
- return defaultMemoize((columns, firstColumnToRender, lastColumnToRender, minFirstColumn, maxLastColumn, indexOfColumnWithFocusedCell) => {
395
- // If the selected column is not within the current range of columns being displayed,
396
- // we need to render it at either the left or right of the columns,
397
- // depending on whether it is above or below the range.
398
- let focusedCellColumnIndexNotInRange;
399
- const renderedColumns = columns.slice(firstColumnToRender, lastColumnToRender);
400
- if (indexOfColumnWithFocusedCell > -1) {
401
- // check if it is not on the left pinned column.
402
- if (firstColumnToRender > indexOfColumnWithFocusedCell && indexOfColumnWithFocusedCell >= minFirstColumn) {
403
- focusedCellColumnIndexNotInRange = indexOfColumnWithFocusedCell;
404
- }
405
- // check if it is not on the right pinned column.
406
- else if (lastColumnToRender < indexOfColumnWithFocusedCell && indexOfColumnWithFocusedCell < maxLastColumn) {
407
- focusedCellColumnIndexNotInRange = indexOfColumnWithFocusedCell;
408
- }
409
- }
410
- return {
411
- focusedCellColumnIndexNotInRange,
412
- renderedColumns
413
- };
414
- });
415
- }
416
366
  function inputsSelector(apiRef, rootProps, enabled, enabledForColumns) {
417
367
  const dimensions = gridDimensionsSelector(apiRef.current.state);
418
368
  const currentPage = getVisibleRows(apiRef, rootProps);
@@ -591,15 +541,9 @@ export function areRenderContextsEqual(context1, context2) {
591
541
  }
592
542
  return context1.firstRowIndex === context2.firstRowIndex && context1.lastRowIndex === context2.lastRowIndex && context1.firstColumnIndex === context2.firstColumnIndex && context1.lastColumnIndex === context2.lastColumnIndex;
593
543
  }
594
- function computeOffsets(apiRef, renderContext, direction, pinnedLeftLength) {
595
- var _rowPositions$renderC, _columnPositions$rend, _columnPositions$pinn;
544
+ export function computeOffsetLeft(columnPositions, renderContext, direction, pinnedLeftLength) {
545
+ var _columnPositions$rend, _columnPositions$pinn;
596
546
  const factor = direction === 'ltr' ? 1 : -1;
597
- const rowPositions = gridRowsMetaSelector(apiRef.current.state).positions;
598
- const columnPositions = gridColumnPositionsSelector(apiRef);
599
- const top = (_rowPositions$renderC = rowPositions[renderContext.firstRowIndex]) != null ? _rowPositions$renderC : 0;
600
547
  const left = factor * ((_columnPositions$rend = columnPositions[renderContext.firstColumnIndex]) != null ? _columnPositions$rend : 0) - ((_columnPositions$pinn = columnPositions[pinnedLeftLength]) != null ? _columnPositions$pinn : 0);
601
- return {
602
- top,
603
- left
604
- };
548
+ return left;
605
549
  }
@@ -8,14 +8,6 @@ export type GridVirtualizationState = {
8
8
  enabled: boolean;
9
9
  enabledForColumns: boolean;
10
10
  renderContext: GridRenderContext;
11
- offsets: {
12
- top: number;
13
- left: number;
14
- };
15
- };
16
- export declare const EMPTY_OFFSETS: {
17
- top: number;
18
- left: number;
19
11
  };
20
12
  export declare const EMPTY_RENDER_CONTEXT: {
21
13
  firstRowIndex: number;
@@ -1,10 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import { useGridApiMethod } from '../../utils/useGridApiMethod';
4
- export const EMPTY_OFFSETS = {
5
- top: 0,
6
- left: 0
7
- };
8
4
  export const EMPTY_RENDER_CONTEXT = {
9
5
  firstRowIndex: 0,
10
6
  lastRowIndex: 0,
@@ -15,8 +11,7 @@ export const virtualizationStateInitializer = (state, props) => {
15
11
  const virtualization = {
16
12
  enabled: !props.disableVirtualization,
17
13
  enabledForColumns: true,
18
- renderContext: EMPTY_RENDER_CONTEXT,
19
- offsets: EMPTY_OFFSETS
14
+ renderContext: EMPTY_RENDER_CONTEXT
20
15
  };
21
16
  return _extends({}, state, {
22
17
  virtualization
@@ -1,9 +1,11 @@
1
- declare class Timeout {
1
+ export declare class Timeout {
2
2
  static create(): Timeout;
3
- currentId: number;
3
+ currentId: ReturnType<typeof setTimeout> | null;
4
+ /**
5
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
6
+ */
4
7
  start(delay: number, fn: Function): void;
5
8
  clear: () => void;
6
9
  disposeEffect: () => () => void;
7
10
  }
8
11
  export declare function useTimeout(): Timeout;
9
- export {};
@@ -1,12 +1,14 @@
1
+ 'use client';
2
+
1
3
  import { useLazyRef } from './useLazyRef';
2
4
  import { useOnMount } from './useOnMount';
3
- class Timeout {
5
+ export class Timeout {
4
6
  constructor() {
5
- this.currentId = 0;
7
+ this.currentId = null;
6
8
  this.clear = () => {
7
- if (this.currentId !== 0) {
9
+ if (this.currentId !== null) {
8
10
  clearTimeout(this.currentId);
9
- this.currentId = 0;
11
+ this.currentId = null;
10
12
  }
11
13
  };
12
14
  this.disposeEffect = () => {
@@ -16,9 +18,15 @@ class Timeout {
16
18
  static create() {
17
19
  return new Timeout();
18
20
  }
21
+ /**
22
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
23
+ */
19
24
  start(delay, fn) {
20
25
  this.clear();
21
- this.currentId = setTimeout(fn, delay);
26
+ this.currentId = setTimeout(() => {
27
+ this.currentId = null;
28
+ fn();
29
+ }, delay);
22
30
  }
23
31
  }
24
32
  export function useTimeout() {
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v7.0.0-beta.4
2
+ * @mui/x-data-grid v7.0.0-beta.5
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -108,6 +108,13 @@ export interface GridBaseColDef<R extends GridValidRowModel = GridValidRowModel,
108
108
  * A comparator function used to sort rows.
109
109
  */
110
110
  sortComparator?: GridComparatorFn<V>;
111
+ /**
112
+ * Allows to use a different comparator function depending on the sort direction.
113
+ * Takes precedence over `sortComparator`.
114
+ * @param {GridSortDirection} sortDirection The direction of the sort.
115
+ * @returns {GridComparatorFn<V>} The comparator function to use.
116
+ */
117
+ getSortComparator?: (sortDirection: GridSortDirection) => GridComparatorFn<V> | undefined;
111
118
  /**
112
119
  * The type of the column.
113
120
  * @default 'string'
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import _extends from "@babel/runtime/helpers/esm/extends";
2
4
  import * as React from 'react';
3
5
  import PropTypes from 'prop-types';
@@ -5,7 +5,7 @@ export const getGridBooleanOperators = () => [{
5
5
  if (!filterItem.value) {
6
6
  return null;
7
7
  }
8
- const valueAsBoolean = filterItem.value === 'true';
8
+ const valueAsBoolean = String(filterItem.value) === 'true';
9
9
  return value => {
10
10
  return Boolean(value) === valueAsBoolean;
11
11
  };
@@ -1,6 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3
- const _excluded = ["selected", "rowId", "row", "index", "style", "rowHeight", "className", "visibleColumns", "renderedColumns", "pinnedColumns", "offsets", "dimensions", "firstColumnToRender", "lastColumnToRender", "isFirstVisible", "isLastVisible", "focusedCellColumnIndexNotInRange", "isNotVisible", "focusedCell", "tabbableCell", "onClick", "onDoubleClick", "onMouseEnter", "onMouseLeave", "onMouseOut", "onMouseOver"];
3
+ const _excluded = ["selected", "rowId", "row", "index", "style", "rowHeight", "className", "visibleColumns", "pinnedColumns", "offsetTop", "offsetLeft", "dimensions", "renderContext", "focusedColumnIndex", "isFirstVisible", "isLastVisible", "isNotVisible", "focusedCell", "tabbableCell", "onClick", "onDoubleClick", "onMouseEnter", "onMouseLeave", "onMouseOut", "onMouseOver"];
4
4
  import * as React from 'react';
5
5
  import PropTypes from 'prop-types';
6
6
  import clsx from 'clsx';
@@ -21,7 +21,6 @@ import { gridSortModelSelector } from '../hooks/features/sorting/gridSortingSele
21
21
  import { gridRowMaximumTreeDepthSelector } from '../hooks/features/rows/gridRowsSelector';
22
22
  import { gridColumnGroupsHeaderMaxDepthSelector } from '../hooks/features/columnGrouping/gridColumnGroupsSelector';
23
23
  import { gridEditRowsStateSelector } from '../hooks/features/editing/gridEditingSelectors';
24
- import { randomNumberBetween } from '../utils/utils';
25
24
  import { PinnedPosition } from './cell/GridCell';
26
25
  import { GridScrollbarFillerCell as ScrollbarFiller } from './GridScrollbarFillerCell';
27
26
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -65,16 +64,14 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
65
64
  rowHeight,
66
65
  className,
67
66
  visibleColumns,
68
- renderedColumns,
69
67
  pinnedColumns,
70
- offsets,
68
+ offsetLeft,
71
69
  dimensions,
72
- firstColumnToRender,
70
+ renderContext,
71
+ focusedColumnIndex,
73
72
  isFirstVisible,
74
73
  isLastVisible,
75
- focusedCellColumnIndexNotInRange,
76
74
  isNotVisible,
77
- focusedCell,
78
75
  onClick,
79
76
  onDoubleClick,
80
77
  onMouseEnter,
@@ -95,6 +92,9 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
95
92
  const handleRef = useForkRef(ref, refProp);
96
93
  const rowNode = apiRef.current.getRowNode(rowId);
97
94
  const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0;
95
+ const hasFocusCell = focusedColumnIndex !== undefined;
96
+ const hasVirtualFocusCellLeft = hasFocusCell && focusedColumnIndex >= pinnedColumns.left.length && focusedColumnIndex < renderContext.firstColumnIndex;
97
+ const hasVirtualFocusCellRight = hasFocusCell && focusedColumnIndex < visibleColumns.length - pinnedColumns.right.length && focusedColumnIndex >= renderContext.lastColumnIndex;
98
98
  const ariaRowIndex = index + headerGroupingMaxDepth + 2; // 1 for the header row and 1 as it's 1-based
99
99
 
100
100
  const ownerState = {
@@ -243,12 +243,13 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
243
243
  });
244
244
  rowClassNames.push(rootProps.getRowClassName(rowParams));
245
245
  }
246
- const randomNumber = randomNumberBetween(10000, 20, 80);
247
246
  const getCell = (column, indexInSection, indexRelativeToAllColumns, sectionLength, pinnedPosition = PinnedPosition.NONE) => {
248
247
  const cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, indexRelativeToAllColumns);
249
- if (!cellColSpanInfo || cellColSpanInfo.spannedByColSpan) {
248
+ if (cellColSpanInfo?.spannedByColSpan) {
250
249
  return null;
251
250
  }
251
+ const width = cellColSpanInfo?.cellProps.width ?? column.computedWidth;
252
+ const colSpan = cellColSpanInfo?.cellProps.colSpan ?? 1;
252
253
  let pinnedOffset;
253
254
  // FIXME: Why is the switch check exhaustiveness not validated with typescript-eslint?
254
255
  // eslint-disable-next-line default-case
@@ -260,25 +261,18 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
260
261
  pinnedOffset = dimensions.columnsTotalWidth - columnPositions[indexRelativeToAllColumns] - column.computedWidth + scrollbarWidth;
261
262
  break;
262
263
  case PinnedPosition.NONE:
264
+ case PinnedPosition.VIRTUAL:
263
265
  pinnedOffset = 0;
264
266
  break;
265
267
  }
266
268
  if (rowNode?.type === 'skeletonRow') {
267
- const {
268
- width
269
- } = cellColSpanInfo.cellProps;
270
- const contentWidth = Math.round(randomNumber());
271
269
  return /*#__PURE__*/_jsx(slots.skeletonCell, {
272
270
  width: width,
273
- contentWidth: contentWidth,
271
+ height: rowHeight,
274
272
  field: column.field,
275
273
  align: column.align ?? 'left'
276
274
  }, column.field);
277
275
  }
278
- const {
279
- colSpan,
280
- width
281
- } = cellColSpanInfo.cellProps;
282
276
  const editCellState = editRowsState[rowId]?.[column.field] ?? null;
283
277
 
284
278
  // when the cell is a reorder cell we are not allowing to reorder the col
@@ -288,10 +282,7 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
288
282
  const canReorderColumn = !(disableColumnReorder || column.disableReorder);
289
283
  const canReorderRow = rowReordering && !sortModel.length && treeDepth <= 1 && !isEditingRows;
290
284
  const disableDragEvents = !(canReorderColumn || isReorderCell && canReorderRow);
291
- let cellIsNotVisible = false;
292
- if (focusedCellColumnIndexNotInRange !== undefined && visibleColumns[focusedCellColumnIndexNotInRange].field === column.field) {
293
- cellIsNotVisible = true;
294
- }
285
+ const cellIsNotVisible = pinnedPosition === PinnedPosition.VIRTUAL;
295
286
  return /*#__PURE__*/_jsx(slots.cell, _extends({
296
287
  column: column,
297
288
  width: width,
@@ -327,18 +318,16 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
327
318
  });
328
319
  const middleColumnsLength = visibleColumns.length - pinnedColumns.left.length - pinnedColumns.right.length;
329
320
  const cells = [];
330
- for (let i = 0; i < renderedColumns.length; i += 1) {
331
- const column = renderedColumns[i];
332
- let indexRelativeToAllColumns = firstColumnToRender + i;
333
- if (focusedCellColumnIndexNotInRange !== undefined && focusedCell) {
334
- if (visibleColumns[focusedCellColumnIndexNotInRange].field === column.field) {
335
- indexRelativeToAllColumns = focusedCellColumnIndexNotInRange;
336
- } else {
337
- indexRelativeToAllColumns -= 1;
338
- }
339
- }
340
- const indexInSection = indexRelativeToAllColumns - pinnedColumns.left.length;
341
- cells.push(getCell(column, indexInSection, indexRelativeToAllColumns, middleColumnsLength));
321
+ if (hasVirtualFocusCellLeft) {
322
+ cells.push(getCell(visibleColumns[focusedColumnIndex], focusedColumnIndex - pinnedColumns.left.length, focusedColumnIndex, middleColumnsLength, PinnedPosition.VIRTUAL));
323
+ }
324
+ for (let i = renderContext.firstColumnIndex; i < renderContext.lastColumnIndex; i += 1) {
325
+ const column = visibleColumns[i];
326
+ const indexInSection = i - pinnedColumns.left.length;
327
+ cells.push(getCell(column, indexInSection, i, middleColumnsLength));
328
+ }
329
+ if (hasVirtualFocusCellRight) {
330
+ cells.push(getCell(visibleColumns[focusedColumnIndex], focusedColumnIndex - pinnedColumns.left.length, focusedColumnIndex, middleColumnsLength, PinnedPosition.VIRTUAL));
342
331
  }
343
332
  const eventHandlers = row ? {
344
333
  onClick: publishClick,
@@ -364,7 +353,7 @@ const GridRow = /*#__PURE__*/React.forwardRef(function GridRow(props, refProp) {
364
353
  role: "presentation",
365
354
  className: gridClasses.cellOffsetLeft,
366
355
  style: {
367
- width: offsets.left
356
+ width: offsetLeft
368
357
  }
369
358
  }), cells, emptyCellWidth > 0 && /*#__PURE__*/_jsx(EmptyCell, {
370
359
  width: emptyCellWidth
@@ -418,13 +407,11 @@ process.env.NODE_ENV !== "production" ? GridRow.propTypes = {
418
407
  width: PropTypes.number.isRequired
419
408
  }).isRequired
420
409
  }).isRequired,
421
- firstColumnToRender: PropTypes.number.isRequired,
422
410
  /**
423
411
  * Determines which cell has focus.
424
412
  * If `null`, no cell in this row has focus.
425
413
  */
426
- focusedCell: PropTypes.string,
427
- focusedCellColumnIndexNotInRange: PropTypes.number,
414
+ focusedColumnIndex: PropTypes.number,
428
415
  /**
429
416
  * Index of the row in the whole sorted and filtered dataset.
430
417
  * If some rows above have expanded children, this index also take those children into account.
@@ -432,19 +419,21 @@ process.env.NODE_ENV !== "production" ? GridRow.propTypes = {
432
419
  index: PropTypes.number.isRequired,
433
420
  isFirstVisible: PropTypes.bool.isRequired,
434
421
  isLastVisible: PropTypes.bool.isRequired,
435
- isNotVisible: PropTypes.bool,
436
- lastColumnToRender: PropTypes.number.isRequired,
437
- offsets: PropTypes.shape({
438
- left: PropTypes.number.isRequired,
439
- top: PropTypes.number.isRequired
440
- }).isRequired,
422
+ isNotVisible: PropTypes.bool.isRequired,
423
+ offsetLeft: PropTypes.number.isRequired,
424
+ offsetTop: PropTypes.number,
441
425
  onClick: PropTypes.func,
442
426
  onDoubleClick: PropTypes.func,
443
427
  onMouseEnter: PropTypes.func,
444
428
  onMouseLeave: PropTypes.func,
445
429
  pinnedColumns: PropTypes.object.isRequired,
446
- renderedColumns: PropTypes.arrayOf(PropTypes.object).isRequired,
447
- row: PropTypes.object,
430
+ renderContext: PropTypes.shape({
431
+ firstColumnIndex: PropTypes.number.isRequired,
432
+ firstRowIndex: PropTypes.number.isRequired,
433
+ lastColumnIndex: PropTypes.number.isRequired,
434
+ lastRowIndex: PropTypes.number.isRequired
435
+ }).isRequired,
436
+ row: PropTypes.object.isRequired,
448
437
  rowHeight: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]).isRequired,
449
438
  rowId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
450
439
  selected: PropTypes.bool.isRequired,