@mui/x-data-grid 7.1.1 → 7.3.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 (145) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/DataGrid/DataGrid.js +19 -0
  3. package/components/GridPagination.d.ts +4 -1
  4. package/components/GridPagination.js +60 -9
  5. package/components/GridRow.js +4 -1
  6. package/components/GridScrollArea.js +1 -1
  7. package/components/cell/GridCell.d.ts +1 -0
  8. package/components/cell/GridCell.js +4 -2
  9. package/components/columnHeaders/GridColumnGroupHeader.d.ts +1 -0
  10. package/components/columnHeaders/GridColumnGroupHeader.js +3 -2
  11. package/components/columnHeaders/GridColumnHeaderItem.d.ts +2 -0
  12. package/components/columnHeaders/GridColumnHeaderItem.js +9 -4
  13. package/components/containers/GridRootStyles.js +8 -4
  14. package/components/index.d.ts +1 -1
  15. package/components/index.js +1 -1
  16. package/components/panel/GridPanel.js +3 -1
  17. package/components/panel/filterPanel/GridFilterInputDate.js +5 -1
  18. package/constants/gridClasses.d.ts +11 -2
  19. package/constants/gridClasses.js +1 -1
  20. package/hooks/core/useGridInitialization.js +1 -1
  21. package/hooks/core/useGridStateInitialization.d.ts +1 -2
  22. package/hooks/core/useGridStateInitialization.js +6 -10
  23. package/hooks/features/columnHeaders/useGridColumnHeaders.js +9 -5
  24. package/hooks/features/columnResize/useGridColumnResize.js +75 -61
  25. package/hooks/features/columns/gridColumnsUtils.d.ts +2 -1
  26. package/hooks/features/columns/gridColumnsUtils.js +4 -3
  27. package/hooks/features/density/useGridDensity.d.ts +1 -1
  28. package/hooks/features/density/useGridDensity.js +30 -4
  29. package/hooks/features/dimensions/gridDimensionsApi.d.ts +5 -1
  30. package/hooks/features/dimensions/useGridDimensions.d.ts +1 -1
  31. package/hooks/features/dimensions/useGridDimensions.js +5 -2
  32. package/hooks/features/editing/useGridCellEditing.js +17 -2
  33. package/hooks/features/editing/useGridRowEditing.js +3 -1
  34. package/hooks/features/export/serializers/csvSerializer.js +6 -5
  35. package/hooks/features/export/useGridPrintExport.d.ts +1 -1
  36. package/hooks/features/export/useGridPrintExport.js +2 -2
  37. package/hooks/features/headerFiltering/useGridHeaderFiltering.d.ts +2 -2
  38. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.d.ts +2 -2
  39. package/hooks/features/pagination/gridPaginationInterfaces.d.ts +14 -2
  40. package/hooks/features/pagination/gridPaginationSelector.d.ts +5 -0
  41. package/hooks/features/pagination/gridPaginationSelector.js +7 -1
  42. package/hooks/features/pagination/gridPaginationUtils.d.ts +1 -2
  43. package/hooks/features/pagination/gridPaginationUtils.js +5 -3
  44. package/hooks/features/pagination/useGridPagination.d.ts +1 -1
  45. package/hooks/features/pagination/useGridPagination.js +5 -1
  46. package/hooks/features/pagination/useGridPaginationMeta.d.ts +4 -0
  47. package/hooks/features/pagination/useGridPaginationMeta.js +77 -0
  48. package/hooks/features/pagination/useGridPaginationModel.js +2 -1
  49. package/hooks/features/pagination/useGridRowCount.d.ts +0 -4
  50. package/hooks/features/pagination/useGridRowCount.js +32 -18
  51. package/hooks/utils/useGridSelector.d.ts +1 -1
  52. package/index.js +1 -1
  53. package/internals/index.d.ts +1 -1
  54. package/internals/utils/propValidation.js +1 -1
  55. package/locales/daDK.js +3 -4
  56. package/locales/deDE.js +3 -4
  57. package/locales/frFR.js +5 -6
  58. package/locales/svSE.js +3 -4
  59. package/models/api/gridApiCommon.d.ts +1 -1
  60. package/models/api/gridCallbackDetails.d.ts +5 -0
  61. package/models/api/gridLocaleTextApi.d.ts +7 -3
  62. package/models/api/index.d.ts +1 -1
  63. package/models/api/index.js +0 -1
  64. package/models/events/gridEventLookup.d.ts +7 -1
  65. package/models/gridPaginationProps.d.ts +3 -0
  66. package/models/props/DataGridProps.d.ts +26 -8
  67. package/modern/DataGrid/DataGrid.js +19 -0
  68. package/modern/components/GridPagination.js +60 -9
  69. package/modern/components/GridRow.js +4 -1
  70. package/modern/components/GridScrollArea.js +1 -1
  71. package/modern/components/cell/GridCell.js +4 -2
  72. package/modern/components/columnHeaders/GridColumnGroupHeader.js +3 -2
  73. package/modern/components/columnHeaders/GridColumnHeaderItem.js +9 -4
  74. package/modern/components/containers/GridRootStyles.js +8 -4
  75. package/modern/components/index.js +1 -1
  76. package/modern/components/panel/GridPanel.js +3 -1
  77. package/modern/components/panel/filterPanel/GridFilterInputDate.js +5 -1
  78. package/modern/constants/gridClasses.js +1 -1
  79. package/modern/hooks/core/useGridInitialization.js +1 -1
  80. package/modern/hooks/core/useGridStateInitialization.js +6 -10
  81. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +9 -5
  82. package/modern/hooks/features/columnResize/useGridColumnResize.js +75 -61
  83. package/modern/hooks/features/columns/gridColumnsUtils.js +4 -3
  84. package/modern/hooks/features/density/useGridDensity.js +30 -4
  85. package/modern/hooks/features/dimensions/useGridDimensions.js +5 -2
  86. package/modern/hooks/features/editing/useGridCellEditing.js +17 -2
  87. package/modern/hooks/features/editing/useGridRowEditing.js +3 -1
  88. package/modern/hooks/features/export/serializers/csvSerializer.js +6 -5
  89. package/modern/hooks/features/export/useGridPrintExport.js +2 -2
  90. package/modern/hooks/features/pagination/gridPaginationSelector.js +7 -1
  91. package/modern/hooks/features/pagination/gridPaginationUtils.js +5 -3
  92. package/modern/hooks/features/pagination/useGridPagination.js +5 -1
  93. package/modern/hooks/features/pagination/useGridPaginationMeta.js +77 -0
  94. package/modern/hooks/features/pagination/useGridPaginationModel.js +2 -1
  95. package/modern/hooks/features/pagination/useGridRowCount.js +32 -18
  96. package/modern/index.js +1 -1
  97. package/modern/internals/utils/propValidation.js +1 -1
  98. package/modern/locales/daDK.js +3 -4
  99. package/modern/locales/deDE.js +3 -4
  100. package/modern/locales/frFR.js +5 -6
  101. package/modern/locales/svSE.js +3 -4
  102. package/modern/models/api/index.js +0 -1
  103. package/modern/utils/cellBorderUtils.js +15 -2
  104. package/node/DataGrid/DataGrid.js +19 -0
  105. package/node/components/GridPagination.js +59 -8
  106. package/node/components/GridRow.js +4 -1
  107. package/node/components/GridScrollArea.js +1 -1
  108. package/node/components/cell/GridCell.js +4 -2
  109. package/node/components/columnHeaders/GridColumnGroupHeader.js +3 -2
  110. package/node/components/columnHeaders/GridColumnHeaderItem.js +8 -3
  111. package/node/components/containers/GridRootStyles.js +8 -4
  112. package/node/components/index.js +27 -10
  113. package/node/components/panel/GridPanel.js +3 -1
  114. package/node/components/panel/filterPanel/GridFilterInputDate.js +5 -1
  115. package/node/constants/gridClasses.js +1 -1
  116. package/node/hooks/core/useGridInitialization.js +1 -1
  117. package/node/hooks/core/useGridStateInitialization.js +6 -10
  118. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +9 -5
  119. package/node/hooks/features/columnResize/useGridColumnResize.js +76 -62
  120. package/node/hooks/features/columns/gridColumnsUtils.js +4 -3
  121. package/node/hooks/features/density/useGridDensity.js +30 -4
  122. package/node/hooks/features/dimensions/useGridDimensions.js +5 -2
  123. package/node/hooks/features/editing/useGridCellEditing.js +17 -2
  124. package/node/hooks/features/editing/useGridRowEditing.js +3 -1
  125. package/node/hooks/features/export/serializers/csvSerializer.js +6 -5
  126. package/node/hooks/features/export/useGridPrintExport.js +2 -2
  127. package/node/hooks/features/pagination/gridPaginationSelector.js +8 -2
  128. package/node/hooks/features/pagination/gridPaginationUtils.js +6 -4
  129. package/node/hooks/features/pagination/useGridPagination.js +5 -1
  130. package/node/hooks/features/pagination/useGridPaginationMeta.js +87 -0
  131. package/node/hooks/features/pagination/useGridPaginationModel.js +2 -1
  132. package/node/hooks/features/pagination/useGridRowCount.js +30 -15
  133. package/node/index.js +1 -1
  134. package/node/internals/utils/propValidation.js +1 -1
  135. package/node/locales/daDK.js +3 -4
  136. package/node/locales/deDE.js +3 -4
  137. package/node/locales/frFR.js +5 -6
  138. package/node/locales/svSE.js +3 -4
  139. package/node/models/api/index.js +0 -11
  140. package/node/utils/cellBorderUtils.js +15 -2
  141. package/package.json +1 -1
  142. package/themeAugmentation/overrides.d.ts +6 -0
  143. package/utils/cellBorderUtils.d.ts +1 -1
  144. package/utils/cellBorderUtils.js +15 -2
  145. package/utils/getGridLocalization.d.ts +1 -1
@@ -1,6 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import { unstable_ownerDocument as ownerDocument, unstable_useEventCallback as useEventCallback } from '@mui/utils';
4
+ import useLazyRef from '@mui/utils/useLazyRef';
4
5
  import { useTheme } from '@mui/material/styles';
5
6
  import { findGridCellElementsFromCol, findGridElement, findLeftPinnedCellsAfterCol, findRightPinnedCellsBeforeCol, getFieldFromHeaderElem, findHeaderElementFromField, findGroupHeaderElementsFromField, findGridHeader, findGridCells, findParentElementFromClassName, findLeftPinnedHeadersAfterCol, findRightPinnedHeadersBeforeCol } from '../../../utils/domUtils';
6
7
  import { DEFAULT_GRID_AUTOSIZE_OPTIONS } from './gridColumnResizeApi';
@@ -173,6 +174,25 @@ export const columnResizeStateInitializer = state => _extends({}, state, {
173
174
  resizingColumnField: ''
174
175
  }
175
176
  });
177
+ function createResizeRefs() {
178
+ return {
179
+ colDef: undefined,
180
+ initialColWidth: 0,
181
+ initialTotalWidth: 0,
182
+ previousMouseClickEvent: undefined,
183
+ columnHeaderElement: undefined,
184
+ headerFilterElement: undefined,
185
+ groupHeaderElements: [],
186
+ cellElements: [],
187
+ leftPinnedCellsAfter: [],
188
+ rightPinnedCellsBefore: [],
189
+ fillerLeft: undefined,
190
+ fillerRight: undefined,
191
+ leftPinnedHeadersAfter: [],
192
+ rightPinnedHeadersBefore: []
193
+ };
194
+ }
195
+
176
196
  /**
177
197
  * @requires useGridColumns (method, event)
178
198
  * TODO: improve experience for last column
@@ -180,18 +200,7 @@ export const columnResizeStateInitializer = state => _extends({}, state, {
180
200
  export const useGridColumnResize = (apiRef, props) => {
181
201
  const theme = useTheme();
182
202
  const logger = useGridLogger(apiRef, 'useGridColumnResize');
183
- const colDefRef = React.useRef();
184
- const previousMouseClickEvent = React.useRef();
185
- const columnHeaderElementRef = React.useRef();
186
- const headerFilterElementRef = React.useRef();
187
- const groupHeaderElementsRef = React.useRef([]);
188
- const cellElementsRef = React.useRef([]);
189
- const leftPinnedCellsAfterRef = React.useRef([]);
190
- const rightPinnedCellsBeforeRef = React.useRef([]);
191
- const fillerLeftRef = React.useRef();
192
- const fillerRightRef = React.useRef();
193
- const leftPinnedHeadersAfterRef = React.useRef([]);
194
- const rightPinnedHeadersBeforeRef = React.useRef([]);
203
+ const refs = useLazyRef(createResizeRefs).current;
195
204
 
196
205
  // To improve accessibility, the separator has padding on both sides.
197
206
  // Clicking inside the padding area should be treated as a click in the separator.
@@ -201,22 +210,25 @@ export const useGridColumnResize = (apiRef, props) => {
201
210
  const stopResizeEventTimeout = useTimeout();
202
211
  const touchId = React.useRef();
203
212
  const updateWidth = newWidth => {
204
- logger.debug(`Updating width to ${newWidth} for col ${colDefRef.current.field}`);
205
- const prevWidth = columnHeaderElementRef.current.offsetWidth;
213
+ logger.debug(`Updating width to ${newWidth} for col ${refs.colDef.field}`);
214
+ const prevWidth = refs.columnHeaderElement.offsetWidth;
206
215
  const widthDiff = newWidth - prevWidth;
207
- colDefRef.current.computedWidth = newWidth;
208
- colDefRef.current.width = newWidth;
209
- colDefRef.current.flex = 0;
210
- columnHeaderElementRef.current.style.width = `${newWidth}px`;
211
- columnHeaderElementRef.current.style.minWidth = `${newWidth}px`;
212
- columnHeaderElementRef.current.style.maxWidth = `${newWidth}px`;
213
- const headerFilterElement = headerFilterElementRef.current;
216
+ const columnWidthDiff = newWidth - refs.initialColWidth;
217
+ const newTotalWidth = refs.initialTotalWidth + columnWidthDiff;
218
+ apiRef.current.rootElementRef?.current?.style.setProperty('--DataGrid-rowWidth', `${newTotalWidth}px`);
219
+ refs.colDef.computedWidth = newWidth;
220
+ refs.colDef.width = newWidth;
221
+ refs.colDef.flex = 0;
222
+ refs.columnHeaderElement.style.width = `${newWidth}px`;
223
+ refs.columnHeaderElement.style.minWidth = `${newWidth}px`;
224
+ refs.columnHeaderElement.style.maxWidth = `${newWidth}px`;
225
+ const headerFilterElement = refs.headerFilterElement;
214
226
  if (headerFilterElement) {
215
227
  headerFilterElement.style.width = `${newWidth}px`;
216
228
  headerFilterElement.style.minWidth = `${newWidth}px`;
217
229
  headerFilterElement.style.maxWidth = `${newWidth}px`;
218
230
  }
219
- groupHeaderElementsRef.current.forEach(element => {
231
+ refs.groupHeaderElements.forEach(element => {
220
232
  const div = element;
221
233
  let finalWidth;
222
234
  if (div.getAttribute('aria-colspan') === '1') {
@@ -230,7 +242,7 @@ export const useGridColumnResize = (apiRef, props) => {
230
242
  div.style.minWidth = finalWidth;
231
243
  div.style.maxWidth = finalWidth;
232
244
  });
233
- cellElementsRef.current.forEach(element => {
245
+ refs.cellElements.forEach(element => {
234
246
  const div = element;
235
247
  let finalWidth;
236
248
  if (div.getAttribute('aria-colspan') === '1') {
@@ -242,22 +254,22 @@ export const useGridColumnResize = (apiRef, props) => {
242
254
  }
243
255
  div.style.setProperty('--width', finalWidth);
244
256
  });
245
- const pinnedPosition = apiRef.current.unstable_applyPipeProcessors('isColumnPinned', false, colDefRef.current.field);
257
+ const pinnedPosition = apiRef.current.unstable_applyPipeProcessors('isColumnPinned', false, refs.colDef.field);
246
258
  if (pinnedPosition === GridPinnedColumnPosition.LEFT) {
247
- updateProperty(fillerLeftRef.current, 'width', widthDiff);
248
- leftPinnedCellsAfterRef.current.forEach(cell => {
259
+ updateProperty(refs.fillerLeft, 'width', widthDiff);
260
+ refs.leftPinnedCellsAfter.forEach(cell => {
249
261
  updateProperty(cell, 'left', widthDiff);
250
262
  });
251
- leftPinnedHeadersAfterRef.current.forEach(header => {
263
+ refs.leftPinnedHeadersAfter.forEach(header => {
252
264
  updateProperty(header, 'left', widthDiff);
253
265
  });
254
266
  }
255
267
  if (pinnedPosition === GridPinnedColumnPosition.RIGHT) {
256
- updateProperty(fillerRightRef.current, 'width', widthDiff);
257
- rightPinnedCellsBeforeRef.current.forEach(cell => {
268
+ updateProperty(refs.fillerRight, 'width', widthDiff);
269
+ refs.rightPinnedCellsBefore.forEach(cell => {
258
270
  updateProperty(cell, 'right', widthDiff);
259
271
  });
260
- rightPinnedHeadersBeforeRef.current.forEach(header => {
272
+ refs.rightPinnedHeadersBefore.forEach(header => {
261
273
  updateProperty(header, 'right', widthDiff);
262
274
  });
263
275
  }
@@ -267,21 +279,21 @@ export const useGridColumnResize = (apiRef, props) => {
267
279
  stopListening();
268
280
 
269
281
  // Prevent double-clicks from being interpreted as two separate clicks
270
- if (previousMouseClickEvent.current) {
271
- const prevEvent = previousMouseClickEvent.current;
282
+ if (refs.previousMouseClickEvent) {
283
+ const prevEvent = refs.previousMouseClickEvent;
272
284
  const prevTimeStamp = prevEvent.timeStamp;
273
285
  const prevClientX = prevEvent.clientX;
274
286
  const prevClientY = prevEvent.clientY;
275
287
 
276
288
  // Check if the current event is part of a double-click
277
289
  if (nativeEvent.timeStamp - prevTimeStamp < 300 && nativeEvent.clientX === prevClientX && nativeEvent.clientY === prevClientY) {
278
- previousMouseClickEvent.current = undefined;
290
+ refs.previousMouseClickEvent = undefined;
279
291
  return;
280
292
  }
281
293
  }
282
- if (colDefRef.current) {
283
- apiRef.current.setColumnWidth(colDefRef.current.field, colDefRef.current.width);
284
- logger.debug(`Updating col ${colDefRef.current.field} with new width: ${colDefRef.current.width}`);
294
+ if (refs.colDef) {
295
+ apiRef.current.setColumnWidth(refs.colDef.field, refs.colDef.width);
296
+ logger.debug(`Updating col ${refs.colDef.field} with new width: ${refs.colDef.width}`);
285
297
  }
286
298
  stopResizeEventTimeout.start(0, () => {
287
299
  apiRef.current.publishEvent('columnResizeStop', null, nativeEvent);
@@ -289,23 +301,25 @@ export const useGridColumnResize = (apiRef, props) => {
289
301
  };
290
302
  const storeReferences = (colDef, separator, xStart) => {
291
303
  const root = apiRef.current.rootElementRef.current;
292
- colDefRef.current = colDef;
293
- columnHeaderElementRef.current = findHeaderElementFromField(apiRef.current.columnHeadersContainerRef.current, colDef.field);
304
+ refs.initialColWidth = colDef.computedWidth;
305
+ refs.initialTotalWidth = apiRef.current.getRootDimensions().rowWidth;
306
+ refs.colDef = colDef;
307
+ refs.columnHeaderElement = findHeaderElementFromField(apiRef.current.columnHeadersContainerRef.current, colDef.field);
294
308
  const headerFilterElement = root.querySelector(`.${gridClasses.headerFilterRow} [data-field="${colDef.field}"]`);
295
309
  if (headerFilterElement) {
296
- headerFilterElementRef.current = headerFilterElement;
310
+ refs.headerFilterElement = headerFilterElement;
297
311
  }
298
- groupHeaderElementsRef.current = findGroupHeaderElementsFromField(apiRef.current.columnHeadersContainerRef?.current, colDef.field);
299
- cellElementsRef.current = findGridCellElementsFromCol(columnHeaderElementRef.current, apiRef.current);
300
- fillerLeftRef.current = findGridElement(apiRef.current, 'filler--pinnedLeft');
301
- fillerRightRef.current = findGridElement(apiRef.current, 'filler--pinnedRight');
302
- const pinnedPosition = apiRef.current.unstable_applyPipeProcessors('isColumnPinned', false, colDefRef.current.field);
303
- leftPinnedCellsAfterRef.current = pinnedPosition !== GridPinnedColumnPosition.LEFT ? [] : findLeftPinnedCellsAfterCol(apiRef.current, columnHeaderElementRef.current);
304
- rightPinnedCellsBeforeRef.current = pinnedPosition !== GridPinnedColumnPosition.RIGHT ? [] : findRightPinnedCellsBeforeCol(apiRef.current, columnHeaderElementRef.current);
305
- leftPinnedHeadersAfterRef.current = pinnedPosition !== GridPinnedColumnPosition.LEFT ? [] : findLeftPinnedHeadersAfterCol(apiRef.current, columnHeaderElementRef.current);
306
- rightPinnedHeadersBeforeRef.current = pinnedPosition !== GridPinnedColumnPosition.RIGHT ? [] : findRightPinnedHeadersBeforeCol(apiRef.current, columnHeaderElementRef.current);
312
+ refs.groupHeaderElements = findGroupHeaderElementsFromField(apiRef.current.columnHeadersContainerRef?.current, colDef.field);
313
+ refs.cellElements = findGridCellElementsFromCol(refs.columnHeaderElement, apiRef.current);
314
+ refs.fillerLeft = findGridElement(apiRef.current, 'filler--pinnedLeft');
315
+ refs.fillerRight = findGridElement(apiRef.current, 'filler--pinnedRight');
316
+ const pinnedPosition = apiRef.current.unstable_applyPipeProcessors('isColumnPinned', false, refs.colDef.field);
317
+ refs.leftPinnedCellsAfter = pinnedPosition !== GridPinnedColumnPosition.LEFT ? [] : findLeftPinnedCellsAfterCol(apiRef.current, refs.columnHeaderElement);
318
+ refs.rightPinnedCellsBefore = pinnedPosition !== GridPinnedColumnPosition.RIGHT ? [] : findRightPinnedCellsBeforeCol(apiRef.current, refs.columnHeaderElement);
319
+ refs.leftPinnedHeadersAfter = pinnedPosition !== GridPinnedColumnPosition.LEFT ? [] : findLeftPinnedHeadersAfterCol(apiRef.current, refs.columnHeaderElement);
320
+ refs.rightPinnedHeadersBefore = pinnedPosition !== GridPinnedColumnPosition.RIGHT ? [] : findRightPinnedHeadersBeforeCol(apiRef.current, refs.columnHeaderElement);
307
321
  resizeDirection.current = getResizeDirection(separator, theme.direction);
308
- initialOffsetToSeparator.current = computeOffsetToSeparator(xStart, columnHeaderElementRef.current.getBoundingClientRect(), resizeDirection.current);
322
+ initialOffsetToSeparator.current = computeOffsetToSeparator(xStart, refs.columnHeaderElement.getBoundingClientRect(), resizeDirection.current);
309
323
  };
310
324
  const handleResizeMouseUp = useEventCallback(finishResize);
311
325
  const handleResizeMouseMove = useEventCallback(nativeEvent => {
@@ -314,12 +328,12 @@ export const useGridColumnResize = (apiRef, props) => {
314
328
  handleResizeMouseUp(nativeEvent);
315
329
  return;
316
330
  }
317
- let newWidth = computeNewWidth(initialOffsetToSeparator.current, nativeEvent.clientX, columnHeaderElementRef.current.getBoundingClientRect(), resizeDirection.current);
318
- newWidth = clamp(newWidth, colDefRef.current.minWidth, colDefRef.current.maxWidth);
331
+ let newWidth = computeNewWidth(initialOffsetToSeparator.current, nativeEvent.clientX, refs.columnHeaderElement.getBoundingClientRect(), resizeDirection.current);
332
+ newWidth = clamp(newWidth, refs.colDef.minWidth, refs.colDef.maxWidth);
319
333
  updateWidth(newWidth);
320
334
  const params = {
321
- element: columnHeaderElementRef.current,
322
- colDef: colDefRef.current,
335
+ element: refs.columnHeaderElement,
336
+ colDef: refs.colDef,
323
337
  width: newWidth
324
338
  };
325
339
  apiRef.current.publishEvent('columnResize', params, nativeEvent);
@@ -342,12 +356,12 @@ export const useGridColumnResize = (apiRef, props) => {
342
356
  handleTouchEnd(nativeEvent);
343
357
  return;
344
358
  }
345
- let newWidth = computeNewWidth(initialOffsetToSeparator.current, finger.x, columnHeaderElementRef.current.getBoundingClientRect(), resizeDirection.current);
346
- newWidth = clamp(newWidth, colDefRef.current.minWidth, colDefRef.current.maxWidth);
359
+ let newWidth = computeNewWidth(initialOffsetToSeparator.current, finger.x, refs.columnHeaderElement.getBoundingClientRect(), resizeDirection.current);
360
+ newWidth = clamp(newWidth, refs.colDef.minWidth, refs.colDef.maxWidth);
347
361
  updateWidth(newWidth);
348
362
  const params = {
349
- element: columnHeaderElementRef.current,
350
- colDef: colDefRef.current,
363
+ element: refs.columnHeaderElement,
364
+ colDef: refs.colDef,
351
365
  width: newWidth
352
366
  };
353
367
  apiRef.current.publishEvent('columnResize', params, nativeEvent);
@@ -391,10 +405,10 @@ export const useGridColumnResize = (apiRef, props) => {
391
405
  setTimeout(() => {
392
406
  doc.removeEventListener('click', preventClick, true);
393
407
  }, 100);
394
- if (columnHeaderElementRef.current) {
395
- columnHeaderElementRef.current.style.pointerEvents = 'unset';
408
+ if (refs.columnHeaderElement) {
409
+ refs.columnHeaderElement.style.pointerEvents = 'unset';
396
410
  }
397
- }, [apiRef, columnHeaderElementRef, handleResizeMouseMove, handleResizeMouseUp, handleTouchMove, handleTouchEnd]);
411
+ }, [apiRef, refs, handleResizeMouseMove, handleResizeMouseUp, handleTouchMove, handleTouchEnd]);
398
412
  const handleResizeStart = React.useCallback(({
399
413
  field
400
414
  }) => {
@@ -435,7 +449,7 @@ export const useGridColumnResize = (apiRef, props) => {
435
449
  storeReferences(colDef, event.currentTarget, event.clientX);
436
450
  const doc = ownerDocument(apiRef.current.rootElementRef.current);
437
451
  doc.body.style.cursor = 'col-resize';
438
- previousMouseClickEvent.current = event.nativeEvent;
452
+ refs.previousMouseClickEvent = event.nativeEvent;
439
453
  doc.addEventListener('mousemove', handleResizeMouseMove);
440
454
  doc.addEventListener('mouseup', handleResizeMouseUp);
441
455
 
@@ -304,10 +304,11 @@ export function getFirstNonSpannedColumnToRender({
304
304
  }
305
305
  return firstNonSpannedColumnToRender;
306
306
  }
307
- export function getTotalHeaderHeight(apiRef, headerHeight) {
307
+ export function getTotalHeaderHeight(apiRef, props) {
308
308
  const densityFactor = gridDensityFactorSelector(apiRef);
309
309
  const maxDepth = gridColumnGroupsHeaderMaxDepthSelector(apiRef);
310
310
  const isHeaderFilteringEnabled = gridHeaderFilteringEnabledSelector(apiRef);
311
- const multiplicationFactor = isHeaderFilteringEnabled ? 2 : 1;
312
- return Math.floor(headerHeight * densityFactor) * ((maxDepth ?? 0) + multiplicationFactor);
311
+ const columnHeadersHeight = Math.floor(props.columnHeaderHeight * densityFactor);
312
+ const filterHeadersHeight = isHeaderFilteringEnabled ? Math.floor((props.headerFilterHeight ?? props.columnHeaderHeight) * densityFactor) : 0;
313
+ return columnHeadersHeight * (1 + (maxDepth ?? 0)) + filterHeadersHeight;
313
314
  }
@@ -4,6 +4,7 @@ import useEventCallback from '@mui/utils/useEventCallback';
4
4
  import { useGridLogger } from '../../utils/useGridLogger';
5
5
  import { useGridApiMethod } from '../../utils/useGridApiMethod';
6
6
  import { gridDensitySelector } from './densitySelector';
7
+ import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
7
8
  export const densityStateInitializer = (state, props) => _extends({}, state, {
8
9
  density: props.initialState?.density ?? props.density ?? 'standard'
9
10
  });
@@ -26,13 +27,38 @@ export const useGridDensity = (apiRef, props) => {
26
27
  density: newDensity
27
28
  }));
28
29
  });
30
+ const densityApi = {
31
+ setDensity
32
+ };
33
+ useGridApiMethod(apiRef, densityApi, 'public');
34
+ const stateExportPreProcessing = React.useCallback((prevState, context) => {
35
+ const exportedDensity = gridDensitySelector(apiRef.current.state);
36
+ const shouldExportRowCount =
37
+ // Always export if the `exportOnlyDirtyModels` property is not activated
38
+ !context.exportOnlyDirtyModels ||
39
+ // Always export if the `density` is controlled
40
+ props.density != null ||
41
+ // Always export if the `density` has been initialized
42
+ props.initialState?.density != null;
43
+ if (!shouldExportRowCount) {
44
+ return prevState;
45
+ }
46
+ return _extends({}, prevState, {
47
+ density: exportedDensity
48
+ });
49
+ }, [apiRef, props.density, props.initialState?.density]);
50
+ const stateRestorePreProcessing = React.useCallback((params, context) => {
51
+ const restoredDensity = context.stateToRestore?.density ? context.stateToRestore.density : gridDensitySelector(apiRef.current.state);
52
+ apiRef.current.setState(state => _extends({}, state, {
53
+ density: restoredDensity
54
+ }));
55
+ return params;
56
+ }, [apiRef]);
57
+ useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
58
+ useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
29
59
  React.useEffect(() => {
30
60
  if (props.density) {
31
61
  apiRef.current.setDensity(props.density);
32
62
  }
33
63
  }, [apiRef, props.density]);
34
- const densityApi = {
35
- setDensity
36
- };
37
- useGridApiMethod(apiRef, densityApi, 'public');
38
64
  };
@@ -29,6 +29,7 @@ const EMPTY_DIMENSIONS = {
29
29
  hasScrollY: false,
30
30
  scrollbarSize: 0,
31
31
  headerHeight: 0,
32
+ headerFilterHeight: 0,
32
33
  rowWidth: 0,
33
34
  rowHeight: 0,
34
35
  columnsTotalWidth: 0,
@@ -53,8 +54,9 @@ export function useGridDimensions(apiRef, props) {
53
54
  const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
54
55
  const rowHeight = Math.floor(props.rowHeight * densityFactor);
55
56
  const headerHeight = Math.floor(props.columnHeaderHeight * densityFactor);
57
+ const headerFilterHeight = Math.floor((props.headerFilterHeight ?? props.columnHeaderHeight) * densityFactor);
56
58
  const columnsTotalWidth = roundToDecimalPlaces(gridColumnsTotalWidthSelector(apiRef), 6);
57
- const headersTotalHeight = getTotalHeaderHeight(apiRef, props.columnHeaderHeight);
59
+ const headersTotalHeight = getTotalHeaderHeight(apiRef, props);
58
60
  const leftPinnedWidth = pinnedColumns.left.reduce((w, col) => w + col.computedWidth, 0);
59
61
  const rightPinnedWidth = pinnedColumns.right.reduce((w, col) => w + col.computedWidth, 0);
60
62
  const [savedSize, setSavedSize] = React.useState();
@@ -176,6 +178,7 @@ export function useGridDimensions(apiRef, props) {
176
178
  hasScrollY,
177
179
  scrollbarSize,
178
180
  headerHeight,
181
+ headerFilterHeight,
179
182
  rowWidth,
180
183
  rowHeight,
181
184
  columnsTotalWidth,
@@ -191,7 +194,7 @@ export function useGridDimensions(apiRef, props) {
191
194
  apiRef.current.publishEvent('viewportInnerSizeChange', newDimensions.viewportInnerSize);
192
195
  }
193
196
  apiRef.current.updateRenderContext?.();
194
- }, [apiRef, setDimensions, props.scrollbarSize, props.autoHeight, rowsMeta.currentPageTotalHeight, rowHeight, headerHeight, columnsTotalWidth, headersTotalHeight, leftPinnedWidth, rightPinnedWidth]);
197
+ }, [apiRef, setDimensions, props.scrollbarSize, props.autoHeight, rowsMeta.currentPageTotalHeight, rowHeight, headerHeight, headerFilterHeight, columnsTotalWidth, headersTotalHeight, leftPinnedWidth, rightPinnedWidth]);
195
198
  const apiPublic = {
196
199
  resize,
197
200
  getRootDimensions
@@ -153,13 +153,26 @@ export const useGridCellEditing = (apiRef, props) => {
153
153
  cellToFocusAfter
154
154
  });
155
155
  }, [apiRef]);
156
+ const runIfNoFieldErrors = callback => async (...args) => {
157
+ if (callback) {
158
+ const {
159
+ id,
160
+ field
161
+ } = args[0];
162
+ const editRowsState = apiRef.current.state.editRows;
163
+ const hasFieldErrors = editRowsState[id][field]?.error;
164
+ if (!hasFieldErrors) {
165
+ callback(...args);
166
+ }
167
+ }
168
+ };
156
169
  useGridApiEventHandler(apiRef, 'cellDoubleClick', runIfEditModeIsCell(handleCellDoubleClick));
157
170
  useGridApiEventHandler(apiRef, 'cellFocusOut', runIfEditModeIsCell(handleCellFocusOut));
158
171
  useGridApiEventHandler(apiRef, 'cellKeyDown', runIfEditModeIsCell(handleCellKeyDown));
159
172
  useGridApiEventHandler(apiRef, 'cellEditStart', runIfEditModeIsCell(handleCellEditStart));
160
173
  useGridApiEventHandler(apiRef, 'cellEditStop', runIfEditModeIsCell(handleCellEditStop));
161
174
  useGridApiOptionHandler(apiRef, 'cellEditStart', props.onCellEditStart);
162
- useGridApiOptionHandler(apiRef, 'cellEditStop', props.onCellEditStop);
175
+ useGridApiOptionHandler(apiRef, 'cellEditStop', runIfNoFieldErrors(props.onCellEditStop));
163
176
  const getCellMode = React.useCallback((id, field) => {
164
177
  const editingState = gridEditRowsStateSelector(apiRef.current.state);
165
178
  const isEditing = editingState[id] && editingState[id][field];
@@ -168,7 +181,9 @@ export const useGridCellEditing = (apiRef, props) => {
168
181
  const updateCellModesModel = useEventCallback(newModel => {
169
182
  const isNewModelDifferentFromProp = newModel !== props.cellModesModel;
170
183
  if (onCellModesModelChange && isNewModelDifferentFromProp) {
171
- onCellModesModelChange(newModel, {});
184
+ onCellModesModelChange(newModel, {
185
+ api: apiRef.current
186
+ });
172
187
  }
173
188
  if (props.cellModesModel && isNewModelDifferentFromProp) {
174
189
  return; // The prop always win
@@ -242,7 +242,9 @@ export const useGridRowEditing = (apiRef, props) => {
242
242
  const updateRowModesModel = useEventCallback(newModel => {
243
243
  const isNewModelDifferentFromProp = newModel !== props.rowModesModel;
244
244
  if (onRowModesModelChange && isNewModelDifferentFromProp) {
245
- onRowModesModelChange(newModel, {});
245
+ onRowModesModelChange(newModel, {
246
+ api: apiRef.current
247
+ });
246
248
  }
247
249
  if (props.rowModesModel && isNewModelDifferentFromProp) {
248
250
  return; // The prop always win
@@ -2,12 +2,13 @@ import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../../colDef';
2
2
  import { buildWarning } from '../../../../utils/warning';
3
3
  function sanitizeCellValue(value, delimiterCharacter, shouldAppendQuotes) {
4
4
  if (typeof value === 'string') {
5
- // Make sure value containing delimiter or line break won't be split into multiple rows
6
- if ([delimiterCharacter, '\n', '\r', '"'].some(delimiter => value.includes(delimiter))) {
7
- if (shouldAppendQuotes) {
8
- return `"${value.replace(/"/g, '""')}"`;
5
+ if (shouldAppendQuotes) {
6
+ const escapedValue = value.replace(/"/g, '""');
7
+ // Make sure value containing delimiter or line break won't be split into multiple rows
8
+ if ([delimiterCharacter, '\n', '\r', '"'].some(delimiter => value.includes(delimiter))) {
9
+ return `"${escapedValue}"`;
9
10
  }
10
- return `${value.replace(/"/g, '""')}`;
11
+ return escapedValue;
11
12
  }
12
13
  return value;
13
14
  }
@@ -107,7 +107,7 @@ export const useGridPrintExport = (apiRef, props) => {
107
107
  }
108
108
 
109
109
  // Expand container height to accommodate all rows
110
- const computedTotalHeight = rowsMeta.currentPageTotalHeight + getTotalHeaderHeight(apiRef, props.columnHeaderHeight) + gridToolbarElementHeight + gridFooterElementHeight;
110
+ const computedTotalHeight = rowsMeta.currentPageTotalHeight + getTotalHeaderHeight(apiRef, props) + gridToolbarElementHeight + gridFooterElementHeight;
111
111
  gridClone.style.height = `${computedTotalHeight}px`;
112
112
  // The height above does not include grid border width, so we need to exclude it
113
113
  gridClone.style.boxSizing = 'content-box';
@@ -181,7 +181,7 @@ export const useGridPrintExport = (apiRef, props) => {
181
181
  printWindow.contentWindow.print();
182
182
  });
183
183
  }
184
- }, [apiRef, doc, props.columnHeaderHeight]);
184
+ }, [apiRef, doc, props]);
185
185
  const handlePrintWindowAfterPrint = React.useCallback(printWindow => {
186
186
  // Remove the print iframe
187
187
  doc.current.body.removeChild(printWindow);
@@ -21,6 +21,12 @@ export const gridPaginationModelSelector = createSelector(gridPaginationSelector
21
21
  */
22
22
  export const gridPaginationRowCountSelector = createSelector(gridPaginationSelector, pagination => pagination.rowCount);
23
23
 
24
+ /**
25
+ * Get the pagination meta
26
+ * @category Pagination
27
+ */
28
+ export const gridPaginationMetaSelector = createSelector(gridPaginationSelector, pagination => pagination.meta);
29
+
24
30
  /**
25
31
  * Get the index of the page to render if the pagination is enabled
26
32
  * @category Pagination
@@ -37,7 +43,7 @@ export const gridPageSizeSelector = createSelector(gridPaginationModelSelector,
37
43
  * Get the amount of pages needed to display all the rows if the pagination is enabled
38
44
  * @category Pagination
39
45
  */
40
- export const gridPageCountSelector = createSelector(gridPageSizeSelector, gridPaginationRowCountSelector, (pageSize, rowCount) => getPageCount(rowCount, pageSize));
46
+ export const gridPageCountSelector = createSelector(gridPaginationModelSelector, gridPaginationRowCountSelector, (paginationModel, rowCount) => getPageCount(rowCount, paginationModel.pageSize, paginationModel.page));
41
47
 
42
48
  /**
43
49
  * Get the index of the first and the last row to include in the current page if the pagination is enabled.
@@ -1,14 +1,16 @@
1
- import { buildWarning } from '../../../utils/warning';
2
1
  import { GridSignature } from '../../utils';
3
2
  const MAX_PAGE_SIZE = 100;
4
3
  export const defaultPageSize = autoPageSize => autoPageSize ? 0 : 100;
5
- export const getPageCount = (rowCount, pageSize) => {
4
+ export const getPageCount = (rowCount, pageSize, page) => {
6
5
  if (pageSize > 0 && rowCount > 0) {
7
6
  return Math.ceil(rowCount / pageSize);
8
7
  }
8
+ if (rowCount === -1) {
9
+ // With unknown row-count, we can assume a page after the current one
10
+ return page + 2;
11
+ }
9
12
  return 0;
10
13
  };
11
- export const noRowCountInServerMode = buildWarning(["MUI X: the 'rowCount' prop is undefined while using paginationMode='server'", 'For more detail, see http://mui.com/components/data-grid/pagination/#basic-implementation'], 'error');
12
14
  export const getDefaultGridPaginationModel = autoPageSize => ({
13
15
  page: 0,
14
16
  pageSize: autoPageSize ? 0 : 100
@@ -2,14 +2,17 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { throwIfPageSizeExceedsTheLimit, getDefaultGridPaginationModel } from './gridPaginationUtils';
3
3
  import { useGridPaginationModel } from './useGridPaginationModel';
4
4
  import { useGridRowCount } from './useGridRowCount';
5
+ import { useGridPaginationMeta } from './useGridPaginationMeta';
5
6
  export const paginationStateInitializer = (state, props) => {
6
7
  const paginationModel = _extends({}, getDefaultGridPaginationModel(props.autoPageSize), props.paginationModel ?? props.initialState?.pagination?.paginationModel);
7
8
  throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, props.signature);
8
9
  const rowCount = props.rowCount ?? props.initialState?.pagination?.rowCount;
10
+ const meta = props.paginationMeta ?? props.initialState?.pagination?.meta ?? {};
9
11
  return _extends({}, state, {
10
12
  pagination: {
11
13
  paginationModel,
12
- rowCount
14
+ rowCount,
15
+ meta
13
16
  }
14
17
  });
15
18
  };
@@ -19,6 +22,7 @@ export const paginationStateInitializer = (state, props) => {
19
22
  * @requires useGridDimensions (event) - can be after
20
23
  */
21
24
  export const useGridPagination = (apiRef, props) => {
25
+ useGridPaginationMeta(apiRef, props);
22
26
  useGridPaginationModel(apiRef, props);
23
27
  useGridRowCount(apiRef, props);
24
28
  };
@@ -0,0 +1,77 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import * as React from 'react';
3
+ import { useGridLogger, useGridSelector, useGridApiMethod } from '../../utils';
4
+ import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
5
+ import { gridPaginationMetaSelector } from './gridPaginationSelector';
6
+ export const useGridPaginationMeta = (apiRef, props) => {
7
+ const logger = useGridLogger(apiRef, 'useGridPaginationMeta');
8
+ const paginationMeta = useGridSelector(apiRef, gridPaginationMetaSelector);
9
+ apiRef.current.registerControlState({
10
+ stateId: 'paginationMeta',
11
+ propModel: props.paginationMeta,
12
+ propOnChange: props.onPaginationMetaChange,
13
+ stateSelector: gridPaginationMetaSelector,
14
+ changeEvent: 'paginationMetaChange'
15
+ });
16
+
17
+ /**
18
+ * API METHODS
19
+ */
20
+ const setPaginationMeta = React.useCallback(newPaginationMeta => {
21
+ if (paginationMeta === newPaginationMeta) {
22
+ return;
23
+ }
24
+ logger.debug("Setting 'paginationMeta' to", newPaginationMeta);
25
+ apiRef.current.setState(state => _extends({}, state, {
26
+ pagination: _extends({}, state.pagination, {
27
+ meta: newPaginationMeta
28
+ })
29
+ }));
30
+ }, [apiRef, logger, paginationMeta]);
31
+ const paginationMetaApi = {
32
+ setPaginationMeta
33
+ };
34
+ useGridApiMethod(apiRef, paginationMetaApi, 'public');
35
+
36
+ /**
37
+ * PRE-PROCESSING
38
+ */
39
+ const stateExportPreProcessing = React.useCallback((prevState, context) => {
40
+ const exportedPaginationMeta = gridPaginationMetaSelector(apiRef);
41
+ const shouldExportRowCount =
42
+ // Always export if the `exportOnlyDirtyModels` property is not activated
43
+ !context.exportOnlyDirtyModels ||
44
+ // Always export if the `paginationMeta` is controlled
45
+ props.paginationMeta != null ||
46
+ // Always export if the `paginationMeta` has been initialized
47
+ props.initialState?.pagination?.meta != null;
48
+ if (!shouldExportRowCount) {
49
+ return prevState;
50
+ }
51
+ return _extends({}, prevState, {
52
+ pagination: _extends({}, prevState.pagination, {
53
+ meta: exportedPaginationMeta
54
+ })
55
+ });
56
+ }, [apiRef, props.paginationMeta, props.initialState?.pagination?.meta]);
57
+ const stateRestorePreProcessing = React.useCallback((params, context) => {
58
+ const restoredPaginationMeta = context.stateToRestore.pagination?.meta ? context.stateToRestore.pagination.meta : gridPaginationMetaSelector(apiRef);
59
+ apiRef.current.setState(state => _extends({}, state, {
60
+ pagination: _extends({}, state.pagination, {
61
+ meta: restoredPaginationMeta
62
+ })
63
+ }));
64
+ return params;
65
+ }, [apiRef]);
66
+ useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
67
+ useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
68
+
69
+ /**
70
+ * EFFECTS
71
+ */
72
+ React.useEffect(() => {
73
+ if (props.paginationMeta) {
74
+ apiRef.current.setPaginationMeta(props.paginationMeta);
75
+ }
76
+ }, [apiRef, props.paginationMeta]);
77
+ };
@@ -9,7 +9,8 @@ export const getDerivedPaginationModel = (paginationState, signature, pagination
9
9
  let paginationModel = paginationState.paginationModel;
10
10
  const rowCount = paginationState.rowCount;
11
11
  const pageSize = paginationModelProp?.pageSize ?? paginationModel.pageSize;
12
- const pageCount = getPageCount(rowCount, pageSize);
12
+ const page = paginationModelProp?.page ?? paginationModel.page;
13
+ const pageCount = getPageCount(rowCount, pageSize, page);
13
14
  if (paginationModelProp && (paginationModelProp?.page !== paginationModel.page || paginationModelProp?.pageSize !== paginationModel.pageSize)) {
14
15
  paginationModel = paginationModelProp;
15
16
  }