@mui/x-data-grid-pro 6.14.0 → 6.15.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 (65) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/DataGridPro/DataGridPro.js +20 -0
  3. package/DataGridPro/useDataGridProComponent.js +3 -1
  4. package/DataGridPro/useDataGridProProps.js +2 -0
  5. package/components/headerFiltering/GridHeaderFilterCell.js +16 -2
  6. package/components/headerFiltering/GridHeaderFilterMenu.js +1 -1
  7. package/hooks/features/columnHeaders/useGridColumnHeaders.js +19 -2
  8. package/hooks/features/columnPinning/useGridColumnPinning.js +1 -44
  9. package/hooks/features/columnResize/gridColumnResizeApi.d.ts +44 -0
  10. package/hooks/features/columnResize/gridColumnResizeApi.js +10 -0
  11. package/hooks/features/columnResize/index.d.ts +1 -0
  12. package/hooks/features/columnResize/index.js +2 -1
  13. package/hooks/features/columnResize/useGridColumnResize.d.ts +1 -1
  14. package/hooks/features/columnResize/useGridColumnResize.js +236 -56
  15. package/index.js +1 -1
  16. package/legacy/DataGridPro/DataGridPro.js +20 -0
  17. package/legacy/DataGridPro/useDataGridProComponent.js +3 -1
  18. package/legacy/DataGridPro/useDataGridProProps.js +2 -0
  19. package/legacy/components/headerFiltering/GridHeaderFilterCell.js +17 -3
  20. package/legacy/components/headerFiltering/GridHeaderFilterMenu.js +1 -1
  21. package/legacy/hooks/features/columnHeaders/useGridColumnHeaders.js +22 -8
  22. package/legacy/hooks/features/columnPinning/useGridColumnPinning.js +1 -44
  23. package/legacy/hooks/features/columnResize/gridColumnResizeApi.js +10 -0
  24. package/legacy/hooks/features/columnResize/index.js +2 -1
  25. package/legacy/hooks/features/columnResize/useGridColumnResize.js +279 -57
  26. package/legacy/index.js +1 -1
  27. package/legacy/models/index.js +1 -0
  28. package/legacy/utils/domUtils.js +12 -0
  29. package/legacy/utils/releaseInfo.js +1 -1
  30. package/models/dataGridProProps.d.ts +15 -0
  31. package/models/gridApiPro.d.ts +2 -2
  32. package/models/index.d.ts +1 -0
  33. package/models/index.js +1 -0
  34. package/modern/DataGridPro/DataGridPro.js +20 -0
  35. package/modern/DataGridPro/useDataGridProComponent.js +3 -1
  36. package/modern/DataGridPro/useDataGridProProps.js +2 -0
  37. package/modern/components/headerFiltering/GridHeaderFilterCell.js +15 -2
  38. package/modern/components/headerFiltering/GridHeaderFilterMenu.js +1 -1
  39. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +18 -1
  40. package/modern/hooks/features/columnPinning/useGridColumnPinning.js +1 -43
  41. package/modern/hooks/features/columnResize/gridColumnResizeApi.js +10 -0
  42. package/modern/hooks/features/columnResize/index.js +2 -1
  43. package/modern/hooks/features/columnResize/useGridColumnResize.js +225 -52
  44. package/modern/index.js +1 -1
  45. package/modern/models/index.js +1 -0
  46. package/modern/utils/domUtils.js +10 -0
  47. package/modern/utils/releaseInfo.js +1 -1
  48. package/node/DataGridPro/DataGridPro.js +20 -0
  49. package/node/DataGridPro/useDataGridProComponent.js +2 -0
  50. package/node/DataGridPro/useDataGridProProps.js +2 -0
  51. package/node/components/headerFiltering/GridHeaderFilterCell.js +15 -2
  52. package/node/components/headerFiltering/GridHeaderFilterMenu.js +1 -1
  53. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +18 -1
  54. package/node/hooks/features/columnPinning/useGridColumnPinning.js +0 -42
  55. package/node/hooks/features/columnResize/gridColumnResizeApi.js +17 -0
  56. package/node/hooks/features/columnResize/index.js +11 -0
  57. package/node/hooks/features/columnResize/useGridColumnResize.js +222 -49
  58. package/node/index.js +1 -1
  59. package/node/models/index.js +11 -0
  60. package/node/utils/domUtils.js +12 -0
  61. package/node/utils/releaseInfo.js +1 -1
  62. package/package.json +2 -2
  63. package/utils/domUtils.d.ts +2 -0
  64. package/utils/domUtils.js +10 -0
  65. package/utils/releaseInfo.js +1 -1
@@ -1,10 +1,11 @@
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 { gridClasses, useGridApiEventHandler, useGridApiOptionHandler, useGridNativeEventListener, useGridLogger } from '@mui/x-data-grid';
5
- import { clamp, findParentElementFromClassName } from '@mui/x-data-grid/internals';
4
+ import { gridClasses, useGridApiEventHandler, useGridApiOptionHandler, useGridApiMethod, useGridNativeEventListener, useGridLogger, useGridSelector, gridVirtualizationColumnEnabledSelector } from '@mui/x-data-grid';
5
+ import { clamp, findParentElementFromClassName, gridColumnsStateSelector, useOnMount, useTimeout, createControllablePromise } from '@mui/x-data-grid/internals';
6
6
  import { useTheme } from '@mui/material/styles';
7
- import { findGridCellElementsFromCol, getFieldFromHeaderElem, findHeaderElementFromField, findGroupHeaderElementsFromField } from '../../../utils/domUtils';
7
+ import { findGridCellElementsFromCol, getFieldFromHeaderElem, findHeaderElementFromField, findGroupHeaderElementsFromField, findGridHeader, findGridCells } from '../../../utils/domUtils';
8
+ import { DEFAULT_GRID_AUTOSIZE_OPTIONS } from './gridColumnResizeApi';
8
9
  // TODO: remove support for Safari < 13.
9
10
  // https://caniuse.com/#search=touch-action
10
11
  //
@@ -73,12 +74,104 @@ function getResizeDirection(element, direction) {
73
74
  }
74
75
  return side;
75
76
  }
77
+ function preventClick(event) {
78
+ event.preventDefault();
79
+ event.stopImmediatePropagation();
80
+ }
81
+
82
+ /**
83
+ * Checker that returns a promise that resolves when the column virtualization
84
+ * is disabled.
85
+ */
86
+ function useColumnVirtualizationDisabled(apiRef) {
87
+ const promise = React.useRef();
88
+ const selector = () => gridVirtualizationColumnEnabledSelector(apiRef);
89
+ const value = useGridSelector(apiRef, selector);
90
+ React.useEffect(() => {
91
+ if (promise.current && value === false) {
92
+ promise.current.resolve();
93
+ promise.current = undefined;
94
+ }
95
+ });
96
+ const asyncCheck = () => {
97
+ if (!promise.current) {
98
+ if (selector() === false) {
99
+ return Promise.resolve();
100
+ }
101
+ promise.current = createControllablePromise();
102
+ }
103
+ return promise.current;
104
+ };
105
+ return asyncCheck;
106
+ }
107
+
108
+ /**
109
+ * Basic statistical outlier detection, checks if the value is `F * IQR` away from
110
+ * the Q1 and Q3 boundaries. IQR: interquartile range.
111
+ */
112
+ function excludeOutliers(inputValues, factor) {
113
+ if (inputValues.length < 4) {
114
+ return inputValues;
115
+ }
116
+ const values = inputValues.slice();
117
+ values.sort((a, b) => a - b);
118
+ const q1 = values[Math.floor(values.length * 0.25)];
119
+ const q3 = values[Math.floor(values.length * 0.75) - 1];
120
+ const iqr = q3 - q1;
121
+
122
+ // We make a small adjustment if `iqr < 5` for the cases where the IQR is
123
+ // very small (e.g. zero) due to very close by values in the input data.
124
+ // Otherwise, with an IQR of `0`, anything outside that would be considered
125
+ // an outlier, but it makes more sense visually to allow for this 5px variance
126
+ // rather than showing a cropped cell.
127
+ const deviation = iqr < 5 ? 5 : iqr * factor;
128
+ return values.filter(v => v > q1 - deviation && v < q3 + deviation);
129
+ }
130
+ function extractColumnWidths(apiRef, options, columns) {
131
+ const widthByField = {};
132
+ columns.forEach(column => {
133
+ const cells = findGridCells(apiRef.current, column.field);
134
+ const widths = cells.map(cell => {
135
+ var _getAttribute, _cell$firstElementChi, _cell$firstElementChi2;
136
+ const id = (_getAttribute = cell.parentElement.getAttribute('data-id')) != null ? _getAttribute : '';
137
+ const hasAutoHeight = apiRef.current.rowHasAutoHeight(id);
138
+ if (hasAutoHeight) {
139
+ var _computedWidth;
140
+ return (_computedWidth = column.computedWidth) != null ? _computedWidth : column.width;
141
+ }
142
+ const style = window.getComputedStyle(cell, null);
143
+ const paddingWidth = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10);
144
+ const contentWidth = ((_cell$firstElementChi = (_cell$firstElementChi2 = cell.firstElementChild) == null ? void 0 : _cell$firstElementChi2.scrollWidth) != null ? _cell$firstElementChi : -1) + 1;
145
+ return paddingWidth + contentWidth;
146
+ });
147
+ const filteredWidths = options.includeOutliers ? widths : excludeOutliers(widths, options.outliersFactor);
148
+ if (options.includeHeaders) {
149
+ const header = findGridHeader(apiRef.current, column.field);
150
+ if (header) {
151
+ const title = header.querySelector(`.${gridClasses.columnHeaderTitle}`);
152
+ const content = header.querySelector(`.${gridClasses.columnHeaderTitleContainerContent}`);
153
+ const element = title != null ? title : content;
154
+ const style = window.getComputedStyle(header, null);
155
+ const paddingWidth = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10);
156
+ const contentWidth = element.scrollWidth + 1;
157
+ const width = paddingWidth + contentWidth;
158
+ filteredWidths.push(width);
159
+ }
160
+ }
161
+ const hasColumnMin = column.minWidth !== -Infinity && column.minWidth !== undefined;
162
+ const hasColumnMax = column.maxWidth !== Infinity && column.maxWidth !== undefined;
163
+ const min = hasColumnMin ? column.minWidth : 0;
164
+ const max = hasColumnMax ? column.maxWidth : Infinity;
165
+ const maxContent = filteredWidths.length === 0 ? 0 : Math.max(...filteredWidths);
166
+ widthByField[column.field] = clamp(maxContent, min, max);
167
+ });
168
+ return widthByField;
169
+ }
76
170
  export const columnResizeStateInitializer = state => _extends({}, state, {
77
171
  columnResize: {
78
172
  resizingColumnField: ''
79
173
  }
80
174
  });
81
-
82
175
  /**
83
176
  * @requires useGridColumns (method, event)
84
177
  * TODO: improve experience for last column
@@ -97,7 +190,7 @@ export const useGridColumnResize = (apiRef, props) => {
97
190
  // This ref stores the offset between the click and the separator.
98
191
  const initialOffsetToSeparator = React.useRef();
99
192
  const resizeDirection = React.useRef();
100
- const stopResizeEventTimeout = React.useRef();
193
+ const stopResizeEventTimeout = useTimeout();
101
194
  const touchId = React.useRef();
102
195
  const updateWidth = newWidth => {
103
196
  logger.debug(`Updating width to ${newWidth} for col ${colDefRef.current.field}`);
@@ -137,8 +230,7 @@ export const useGridColumnResize = (apiRef, props) => {
137
230
  apiRef.current.setColumnWidth(colDefRef.current.field, colDefRef.current.width);
138
231
  logger.debug(`Updating col ${colDefRef.current.field} with new width: ${colDefRef.current.width}`);
139
232
  }
140
- clearTimeout(stopResizeEventTimeout.current);
141
- stopResizeEventTimeout.current = setTimeout(() => {
233
+ stopResizeEventTimeout.start(0, () => {
142
234
  apiRef.current.publishEvent('columnResizeStop', null, nativeEvent);
143
235
  });
144
236
  };
@@ -159,44 +251,6 @@ export const useGridColumnResize = (apiRef, props) => {
159
251
  };
160
252
  apiRef.current.publishEvent('columnResize', params, nativeEvent);
161
253
  });
162
- const handleColumnResizeMouseDown = useEventCallback(({
163
- colDef
164
- }, event) => {
165
- var _apiRef$current$colum, _apiRef$current$heade, _apiRef$current$colum2;
166
- // Only handle left clicks
167
- if (event.button !== 0) {
168
- return;
169
- }
170
-
171
- // Skip if the column isn't resizable
172
- if (!event.currentTarget.classList.contains(gridClasses['columnSeparator--resizable'])) {
173
- return;
174
- }
175
-
176
- // Avoid text selection
177
- event.preventDefault();
178
- logger.debug(`Start Resize on col ${colDef.field}`);
179
- apiRef.current.publishEvent('columnResizeStart', {
180
- field: colDef.field
181
- }, event);
182
- colDefRef.current = colDef;
183
- colElementRef.current = (_apiRef$current$colum = apiRef.current.columnHeadersContainerElementRef) == null ? void 0 : _apiRef$current$colum.current.querySelector(`[data-field="${colDef.field}"]`);
184
- const headerFilterRowElement = (_apiRef$current$heade = apiRef.current.headerFiltersElementRef) == null ? void 0 : _apiRef$current$heade.current;
185
- if (headerFilterRowElement) {
186
- headerFilterElementRef.current = headerFilterRowElement.querySelector(`[data-field="${colDef.field}"]`);
187
- }
188
- colGroupingElementRef.current = findGroupHeaderElementsFromField((_apiRef$current$colum2 = apiRef.current.columnHeadersContainerElementRef) == null ? void 0 : _apiRef$current$colum2.current, colDef.field);
189
- colCellElementsRef.current = findGridCellElementsFromCol(colElementRef.current, apiRef.current);
190
- const doc = ownerDocument(apiRef.current.rootElementRef.current);
191
- doc.body.style.cursor = 'col-resize';
192
- resizeDirection.current = getResizeDirection(event.currentTarget, theme.direction);
193
- initialOffsetToSeparator.current = computeOffsetToSeparator(event.clientX, colElementRef.current.getBoundingClientRect(), resizeDirection.current);
194
- doc.addEventListener('mousemove', handleResizeMouseMove);
195
- doc.addEventListener('mouseup', handleResizeMouseUp);
196
-
197
- // Fixes https://github.com/mui/mui-x/issues/4777
198
- colElementRef.current.style.pointerEvents = 'none';
199
- });
200
254
  const handleTouchEnd = useEventCallback(nativeEvent => {
201
255
  const finger = trackFinger(nativeEvent, touchId.current);
202
256
  if (!finger) {
@@ -226,7 +280,7 @@ export const useGridColumnResize = (apiRef, props) => {
226
280
  apiRef.current.publishEvent('columnResize', params, nativeEvent);
227
281
  });
228
282
  const handleTouchStart = useEventCallback(event => {
229
- var _apiRef$current$colum3, _apiRef$current$colum4;
283
+ var _apiRef$current$colum, _apiRef$current$colum2;
230
284
  const cellSeparator = findParentElementFromClassName(event.target, gridClasses['columnSeparator--resizable']);
231
285
  // Let the event bubble if the target is not a col separator
232
286
  if (!cellSeparator) {
@@ -244,13 +298,13 @@ export const useGridColumnResize = (apiRef, props) => {
244
298
  colElementRef.current = findParentElementFromClassName(event.target, gridClasses.columnHeader);
245
299
  const field = getFieldFromHeaderElem(colElementRef.current);
246
300
  const colDef = apiRef.current.getColumn(field);
247
- colGroupingElementRef.current = findGroupHeaderElementsFromField((_apiRef$current$colum3 = apiRef.current.columnHeadersContainerElementRef) == null ? void 0 : _apiRef$current$colum3.current, field);
301
+ colGroupingElementRef.current = findGroupHeaderElementsFromField((_apiRef$current$colum = apiRef.current.columnHeadersContainerElementRef) == null ? void 0 : _apiRef$current$colum.current, field);
248
302
  logger.debug(`Start Resize on col ${colDef.field}`);
249
303
  apiRef.current.publishEvent('columnResizeStart', {
250
304
  field
251
305
  }, event);
252
306
  colDefRef.current = colDef;
253
- colElementRef.current = findHeaderElementFromField((_apiRef$current$colum4 = apiRef.current.columnHeadersElementRef) == null ? void 0 : _apiRef$current$colum4.current, colDef.field);
307
+ colElementRef.current = findHeaderElementFromField((_apiRef$current$colum2 = apiRef.current.columnHeadersElementRef) == null ? void 0 : _apiRef$current$colum2.current, colDef.field);
254
308
  colCellElementsRef.current = findGridCellElementsFromCol(colElementRef.current, apiRef.current);
255
309
  resizeDirection.current = getResizeDirection(event.target, theme.direction);
256
310
  initialOffsetToSeparator.current = computeOffsetToSeparator(touch.clientX, colElementRef.current.getBoundingClientRect(), resizeDirection.current);
@@ -265,6 +319,11 @@ export const useGridColumnResize = (apiRef, props) => {
265
319
  doc.removeEventListener('mouseup', handleResizeMouseUp);
266
320
  doc.removeEventListener('touchmove', handleTouchMove);
267
321
  doc.removeEventListener('touchend', handleTouchEnd);
322
+ // The click event runs right after the mouseup event, we want to wait until it
323
+ // has been canceled before removing our handler.
324
+ setTimeout(() => {
325
+ doc.removeEventListener('click', preventClick, true);
326
+ }, 100);
268
327
  if (colElementRef.current) {
269
328
  colElementRef.current.style.pointerEvents = 'unset';
270
329
  }
@@ -287,21 +346,142 @@ export const useGridColumnResize = (apiRef, props) => {
287
346
  }));
288
347
  apiRef.current.forceUpdate();
289
348
  }, [apiRef]);
290
- React.useEffect(() => {
291
- return () => {
292
- clearTimeout(stopResizeEventTimeout.current);
293
- stopListening();
294
- };
295
- }, [apiRef, handleTouchStart, stopListening]);
349
+ const handleColumnResizeMouseDown = useEventCallback(({
350
+ colDef
351
+ }, event) => {
352
+ var _apiRef$current$colum3, _apiRef$current$heade, _apiRef$current$colum4;
353
+ // Only handle left clicks
354
+ if (event.button !== 0) {
355
+ return;
356
+ }
357
+
358
+ // Skip if the column isn't resizable
359
+ if (!event.currentTarget.classList.contains(gridClasses['columnSeparator--resizable'])) {
360
+ return;
361
+ }
362
+
363
+ // Avoid text selection
364
+ event.preventDefault();
365
+ logger.debug(`Start Resize on col ${colDef.field}`);
366
+ apiRef.current.publishEvent('columnResizeStart', {
367
+ field: colDef.field
368
+ }, event);
369
+ colDefRef.current = colDef;
370
+ colElementRef.current = (_apiRef$current$colum3 = apiRef.current.columnHeadersContainerElementRef) == null ? void 0 : _apiRef$current$colum3.current.querySelector(`[data-field="${colDef.field}"]`);
371
+ const headerFilterRowElement = (_apiRef$current$heade = apiRef.current.headerFiltersElementRef) == null ? void 0 : _apiRef$current$heade.current;
372
+ if (headerFilterRowElement) {
373
+ headerFilterElementRef.current = headerFilterRowElement.querySelector(`[data-field="${colDef.field}"]`);
374
+ }
375
+ colGroupingElementRef.current = findGroupHeaderElementsFromField((_apiRef$current$colum4 = apiRef.current.columnHeadersContainerElementRef) == null ? void 0 : _apiRef$current$colum4.current, colDef.field);
376
+ colCellElementsRef.current = findGridCellElementsFromCol(colElementRef.current, apiRef.current);
377
+ const doc = ownerDocument(apiRef.current.rootElementRef.current);
378
+ doc.body.style.cursor = 'col-resize';
379
+ resizeDirection.current = getResizeDirection(event.currentTarget, theme.direction);
380
+ initialOffsetToSeparator.current = computeOffsetToSeparator(event.clientX, colElementRef.current.getBoundingClientRect(), resizeDirection.current);
381
+ doc.addEventListener('mousemove', handleResizeMouseMove);
382
+ doc.addEventListener('mouseup', handleResizeMouseUp);
383
+
384
+ // Prevent the click event if we have resized the column.
385
+ // Fixes https://github.com/mui/mui-x/issues/4777
386
+ doc.addEventListener('click', preventClick, true);
387
+ });
388
+ const handleColumnSeparatorDoubleClick = useEventCallback((params, event) => {
389
+ if (props.disableAutosize) {
390
+ return;
391
+ }
392
+
393
+ // Only handle left clicks
394
+ if (event.button !== 0) {
395
+ return;
396
+ }
397
+ const column = apiRef.current.state.columns.lookup[params.field];
398
+ if (column.resizable === false) {
399
+ return;
400
+ }
401
+ apiRef.current.autosizeColumns(_extends({}, props.autosizeOptions, {
402
+ columns: [column.field]
403
+ }));
404
+ });
405
+
406
+ /**
407
+ * API METHODS
408
+ */
409
+
410
+ const columnVirtualizationDisabled = useColumnVirtualizationDisabled(apiRef);
411
+ const isAutosizingRef = React.useRef(false);
412
+ const autosizeColumns = React.useCallback(async userOptions => {
413
+ var _apiRef$current$rootE, _userOptions$columns;
414
+ const root = (_apiRef$current$rootE = apiRef.current.rootElementRef) == null ? void 0 : _apiRef$current$rootE.current;
415
+ if (!root) {
416
+ return;
417
+ }
418
+ if (isAutosizingRef.current) {
419
+ return;
420
+ }
421
+ isAutosizingRef.current = true;
422
+ const state = gridColumnsStateSelector(apiRef.current.state);
423
+ const options = _extends({}, DEFAULT_GRID_AUTOSIZE_OPTIONS, userOptions, {
424
+ columns: (_userOptions$columns = userOptions == null ? void 0 : userOptions.columns) != null ? _userOptions$columns : state.orderedFields
425
+ });
426
+ options.columns = options.columns.filter(c => state.columnVisibilityModel[c] !== false);
427
+ const columns = options.columns.map(c => apiRef.current.state.columns.lookup[c]);
428
+ try {
429
+ apiRef.current.unstable_setColumnVirtualization(false);
430
+ await columnVirtualizationDisabled();
431
+ const widthByField = extractColumnWidths(apiRef, options, columns);
432
+ const newColumns = columns.map(column => _extends({}, column, {
433
+ width: widthByField[column.field],
434
+ computedWidth: widthByField[column.field]
435
+ }));
436
+ if (options.expand) {
437
+ var _apiRef$current$getRo, _apiRef$current$getRo2;
438
+ const visibleColumns = state.orderedFields.map(field => state.lookup[field]).filter(c => state.columnVisibilityModel[c.field] !== false);
439
+ const totalWidth = visibleColumns.reduce((total, column) => {
440
+ var _ref, _widthByField$column$;
441
+ return total + ((_ref = (_widthByField$column$ = widthByField[column.field]) != null ? _widthByField$column$ : column.computedWidth) != null ? _ref : column.width);
442
+ }, 0);
443
+ const availableWidth = (_apiRef$current$getRo = (_apiRef$current$getRo2 = apiRef.current.getRootDimensions()) == null ? void 0 : _apiRef$current$getRo2.viewportInnerSize.width) != null ? _apiRef$current$getRo : 0;
444
+ const remainingWidth = availableWidth - totalWidth;
445
+ if (remainingWidth > 0) {
446
+ const widthPerColumn = remainingWidth / (newColumns.length || 1);
447
+ newColumns.forEach(column => {
448
+ column.width += widthPerColumn;
449
+ column.computedWidth += widthPerColumn;
450
+ });
451
+ }
452
+ }
453
+ apiRef.current.updateColumns(newColumns);
454
+ } finally {
455
+ apiRef.current.unstable_setColumnVirtualization(true);
456
+ isAutosizingRef.current = false;
457
+ }
458
+ }, [apiRef, columnVirtualizationDisabled]);
459
+
460
+ /**
461
+ * EFFECTS
462
+ */
463
+
464
+ React.useEffect(() => stopListening, [stopListening]);
465
+ useOnMount(() => {
466
+ if (props.autosizeOnMount) {
467
+ Promise.resolve().then(() => {
468
+ apiRef.current.autosizeColumns(props.autosizeOptions);
469
+ });
470
+ }
471
+ });
296
472
  useGridNativeEventListener(apiRef, () => {
297
473
  var _apiRef$current$colum5;
298
474
  return (_apiRef$current$colum5 = apiRef.current.columnHeadersElementRef) == null ? void 0 : _apiRef$current$colum5.current;
299
475
  }, 'touchstart', handleTouchStart, {
300
476
  passive: doesSupportTouchActionNone()
301
477
  });
302
- useGridApiEventHandler(apiRef, 'columnSeparatorMouseDown', handleColumnResizeMouseDown);
303
- useGridApiEventHandler(apiRef, 'columnResizeStart', handleResizeStart);
478
+ useGridApiMethod(apiRef, {
479
+ autosizeColumns
480
+ }, 'public');
304
481
  useGridApiEventHandler(apiRef, 'columnResizeStop', handleResizeStop);
482
+ useGridApiEventHandler(apiRef, 'columnResizeStart', handleResizeStart);
483
+ useGridApiEventHandler(apiRef, 'columnSeparatorMouseDown', handleColumnResizeMouseDown);
484
+ useGridApiEventHandler(apiRef, 'columnSeparatorDoubleClick', handleColumnSeparatorDoubleClick);
305
485
  useGridApiOptionHandler(apiRef, 'columnResize', props.onColumnResize);
306
486
  useGridApiOptionHandler(apiRef, 'columnWidthChange', props.onColumnWidthChange);
307
487
  };
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid-pro v6.14.0
2
+ * @mui/x-data-grid-pro v6.15.0
3
3
  *
4
4
  * @license MUI X Commercial
5
5
  * This source code is licensed under the commercial license found in the
@@ -69,6 +69,21 @@ DataGridProRaw.propTypes = {
69
69
  * @default false
70
70
  */
71
71
  autoPageSize: PropTypes.bool,
72
+ /**
73
+ * If `true`, columns are autosized after the datagrid is mounted.
74
+ * @default false
75
+ */
76
+ autosizeOnMount: PropTypes.bool,
77
+ /**
78
+ * The options for autosize when user-initiated.
79
+ */
80
+ autosizeOptions: PropTypes.shape({
81
+ columns: PropTypes.arrayOf(PropTypes.string),
82
+ expand: PropTypes.bool,
83
+ includeHeaders: PropTypes.bool,
84
+ includeOutliers: PropTypes.bool,
85
+ outliersFactor: PropTypes.number
86
+ }),
72
87
  /**
73
88
  * Controls the modes of the cells.
74
89
  */
@@ -148,6 +163,11 @@ DataGridProRaw.propTypes = {
148
163
  * The row ids to show the detail panel.
149
164
  */
150
165
  detailPanelExpandedRowIds: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired),
166
+ /**
167
+ * If `true`, column autosizing on header separator double-click is disabled.
168
+ * @default false
169
+ */
170
+ disableAutosize: PropTypes.bool,
151
171
  /**
152
172
  * If `true`, the filtering will only be applied to the top level rows when grouping rows with the `treeData` prop.
153
173
  * @default false
@@ -1,4 +1,4 @@
1
- import { useGridInitialization, useGridInitializeState, useGridClipboard, useGridColumnMenu, useGridColumns, columnsStateInitializer, useGridDensity, useGridCsvExport, useGridPrintExport, useGridFilter, filterStateInitializer, useGridFocus, useGridKeyboardNavigation, useGridPagination, paginationStateInitializer, useGridPreferencesPanel, useGridEditing, editingStateInitializer, useGridRows, useGridRowsPreProcessors, rowsStateInitializer, useGridRowsMeta, useGridParamsApi, useGridRowSelection, useGridSorting, sortingStateInitializer, useGridScroll, useGridEvents, useGridDimensions, useGridStatePersistence, useGridRowSelectionPreProcessors, useGridColumnSpanning, columnMenuStateInitializer, densityStateInitializer, focusStateInitializer, preferencePanelStateInitializer, rowsMetaStateInitializer, rowSelectionStateInitializer, useGridColumnGrouping, columnGroupsStateInitializer, headerFilteringStateInitializer, useGridHeaderFiltering } from '@mui/x-data-grid/internals';
1
+ import { useGridInitialization, useGridInitializeState, useGridClipboard, useGridColumnMenu, useGridColumns, columnsStateInitializer, useGridDensity, useGridCsvExport, useGridPrintExport, useGridFilter, filterStateInitializer, useGridFocus, useGridKeyboardNavigation, useGridPagination, paginationStateInitializer, useGridPreferencesPanel, useGridEditing, editingStateInitializer, useGridRows, useGridRowsPreProcessors, rowsStateInitializer, useGridRowsMeta, useGridParamsApi, useGridRowSelection, useGridSorting, sortingStateInitializer, useGridScroll, useGridEvents, useGridDimensions, useGridStatePersistence, useGridRowSelectionPreProcessors, useGridColumnSpanning, columnMenuStateInitializer, densityStateInitializer, focusStateInitializer, preferencePanelStateInitializer, rowsMetaStateInitializer, rowSelectionStateInitializer, useGridColumnGrouping, columnGroupsStateInitializer, headerFilteringStateInitializer, useGridHeaderFiltering, virtualizationStateInitializer, useGridVirtualization } from '@mui/x-data-grid/internals';
2
2
  // Pro-only features
3
3
  import { useGridInfiniteLoader } from '../hooks/features/infiniteLoader/useGridInfiniteLoader';
4
4
  import { useGridColumnReorder, columnReorderStateInitializer } from '../hooks/features/columnReorder/useGridColumnReorder';
@@ -54,6 +54,7 @@ export var useDataGridProComponent = function useDataGridProComponent(inputApiRe
54
54
  useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
55
55
  useGridInitializeState(columnMenuStateInitializer, apiRef, props);
56
56
  useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
57
+ useGridInitializeState(virtualizationStateInitializer, apiRef, props);
57
58
  useGridHeaderFiltering(apiRef, props);
58
59
  useGridTreeData(apiRef);
59
60
  useGridKeyboardNavigation(apiRef, props);
@@ -87,5 +88,6 @@ export var useDataGridProComponent = function useDataGridProComponent(inputApiRe
87
88
  useGridDimensions(apiRef, props);
88
89
  useGridEvents(apiRef, props);
89
90
  useGridStatePersistence(apiRef);
91
+ useGridVirtualization(apiRef, props);
90
92
  return apiRef;
91
93
  };
@@ -13,6 +13,8 @@ export var DATA_GRID_PRO_PROPS_DEFAULT_VALUES = _extends({}, DATA_GRID_PROPS_DEF
13
13
  scrollEndThreshold: 80,
14
14
  treeData: false,
15
15
  defaultGroupingExpansionDepth: 0,
16
+ autosizeOnMount: false,
17
+ disableAutosize: false,
16
18
  disableColumnPinning: false,
17
19
  keepColumnPositionIfDraggedOutside: false,
18
20
  disableChildrenFiltering: false,
@@ -128,7 +128,7 @@ var GridHeaderFilterCell = /*#__PURE__*/React.forwardRef(function (props, ref) {
128
128
  }, [apiRef, colDef.field]);
129
129
  var onMouseDown = React.useCallback(function (event) {
130
130
  if (!hasFocus) {
131
- if (inputRef.current) {
131
+ if (inputRef.current && inputRef.current.contains(event.target)) {
132
132
  inputRef.current.focus();
133
133
  }
134
134
  apiRef.current.setColumnHeaderFilterFocus(colDef.field, event);
@@ -175,8 +175,22 @@ var GridHeaderFilterCell = /*#__PURE__*/React.forwardRef(function (props, ref) {
175
175
  onFocus: function onFocus() {
176
176
  return apiRef.current.startHeaderFilterEditMode(colDef.field);
177
177
  },
178
- onBlur: function onBlur() {
179
- return apiRef.current.stopHeaderFilterEditMode();
178
+ onBlur: function onBlur(event) {
179
+ var _event$relatedTarget;
180
+ apiRef.current.stopHeaderFilterEditMode();
181
+ // Blurring an input element should reset focus state only if `relatedTarget` is not the header filter cell
182
+ if (!((_event$relatedTarget = event.relatedTarget) != null && _event$relatedTarget.className.includes('columnHeader'))) {
183
+ apiRef.current.setState(function (state) {
184
+ return _extends({}, state, {
185
+ focus: {
186
+ cell: null,
187
+ columnHeader: null,
188
+ columnHeaderFilter: null,
189
+ columnGroupHeader: null
190
+ }
191
+ });
192
+ });
193
+ }
180
194
  },
181
195
  label: capitalize(label),
182
196
  placeholder: "",
@@ -34,7 +34,7 @@ function GridHeaderFilterMenu(_ref) {
34
34
  placement: "bottom-end",
35
35
  open: open,
36
36
  target: target,
37
- onClickAway: hideMenu,
37
+ onClose: hideMenu,
38
38
  onExited: hideMenu,
39
39
  children: /*#__PURE__*/_jsx(MenuList, {
40
40
  "aria-labelledby": labelledBy,
@@ -28,6 +28,7 @@ var GridHeaderFilterRow = styled('div', {
28
28
  display: 'flex'
29
29
  };
30
30
  });
31
+ var filterItemsCache = Object.create(null);
31
32
  export var useGridColumnHeaders = function useGridColumnHeaders(props) {
32
33
  var apiRef = useGridPrivateApiContext();
33
34
  var headerGroupingMaxDepth = props.headerGroupingMaxDepth,
@@ -51,6 +52,24 @@ export var useGridColumnHeaders = function useGridColumnHeaders(props) {
51
52
  var filterModel = useGridSelector(apiRef, gridFilterModelSelector);
52
53
  var totalHeaderHeight = getTotalHeaderHeight(apiRef, rootProps.columnHeaderHeight) + (disableHeaderFiltering ? 0 : headerHeight);
53
54
  var columnHeaderFilterFocus = useGridSelector(apiRef, unstable_gridFocusColumnHeaderFilterSelector);
55
+ var getFilterItem = React.useCallback(function (colDef) {
56
+ var filterModelItem = filterModel == null ? void 0 : filterModel.items.find(function (it) {
57
+ return it.field === colDef.field && it.operator !== 'isAnyOf';
58
+ });
59
+ if (filterModelItem != null) {
60
+ // there's a valid `filterModelItem` for this column
61
+ return filterModelItem;
62
+ }
63
+ var defaultCachedItem = filterItemsCache[colDef.field];
64
+ if (defaultCachedItem != null) {
65
+ // there's a cached `defaultItem` for this column
66
+ return defaultCachedItem;
67
+ }
68
+ // there's no cached `defaultItem` for this column, let's generate one and cache it
69
+ var defaultItem = getGridFilter(colDef);
70
+ filterItemsCache[colDef.field] = defaultItem;
71
+ return defaultItem;
72
+ }, [filterModel]);
54
73
  var getColumnFilters = function getColumnFilters(params) {
55
74
  var other = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
56
75
  if (disableHeaderFiltering) {
@@ -63,8 +82,8 @@ export var useGridColumnHeaders = function useGridColumnHeaders(props) {
63
82
  var renderedColumns = columnsToRender.renderedColumns,
64
83
  firstColumnToRender = columnsToRender.firstColumnToRender;
65
84
  var filters = [];
66
- var _loop = function _loop() {
67
- var _colDef$filterOperato, _colDef$filterOperato2, _filterModel$items$fi, _rootProps$slotProps;
85
+ for (var i = 0; i < renderedColumns.length; i += 1) {
86
+ var _colDef$filterOperato, _colDef$filterOperato2, _rootProps$slotProps;
68
87
  var colDef = renderedColumns[i];
69
88
  var columnIndex = firstColumnToRender + i;
70
89
  var hasFocus = (columnHeaderFilterFocus == null ? void 0 : columnHeaderFilterFocus.field) === colDef.field;
@@ -80,9 +99,7 @@ export var useGridColumnHeaders = function useGridColumnHeaders(props) {
80
99
  var filterOperators = (_colDef$filterOperato = (_colDef$filterOperato2 = colDef.filterOperators) == null ? void 0 : _colDef$filterOperato2.filter(function (operator) {
81
100
  return operator.value !== 'isAnyOf';
82
101
  })) != null ? _colDef$filterOperato : [];
83
- var item = (_filterModel$items$fi = filterModel == null ? void 0 : filterModel.items.find(function (it) {
84
- return it.field === colDef.field && it.operator !== 'isAnyOf';
85
- })) != null ? _filterModel$items$fi : getGridFilter(colDef);
102
+ var item = getFilterItem(colDef);
86
103
  filters.push( /*#__PURE__*/_jsx(rootProps.slots.headerFilterCell, _extends({
87
104
  colIndex: columnIndex,
88
105
  height: headerHeight,
@@ -96,9 +113,6 @@ export var useGridColumnHeaders = function useGridColumnHeaders(props) {
96
113
  "data-field": colDef.field,
97
114
  item: item
98
115
  }, (_rootProps$slotProps = rootProps.slotProps) == null ? void 0 : _rootProps$slotProps.headerFilterCell, other), "".concat(colDef.field, "-filter")));
99
- };
100
- for (var i = 0; i < renderedColumns.length; i += 1) {
101
- _loop();
102
116
  }
103
117
  return /*#__PURE__*/_jsx(GridHeaderFilterRow, {
104
118
  ref: headerFiltersRef,
@@ -4,7 +4,7 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
4
4
  import _extends from "@babel/runtime/helpers/esm/extends";
5
5
  import * as React from 'react';
6
6
  import { useTheme } from '@mui/material/styles';
7
- import { useGridSelector, gridVisibleColumnDefinitionsSelector, gridColumnsTotalWidthSelector, gridColumnPositionsSelector, gridVisibleColumnFieldsSelector, gridClasses, useGridApiMethod, useGridApiEventHandler, gridColumnFieldsSelector } from '@mui/x-data-grid';
7
+ import { useGridSelector, gridVisibleColumnDefinitionsSelector, gridColumnsTotalWidthSelector, gridColumnPositionsSelector, gridVisibleColumnFieldsSelector, useGridApiMethod, useGridApiEventHandler, gridColumnFieldsSelector } from '@mui/x-data-grid';
8
8
  import { useGridRegisterPipeProcessor } from '@mui/x-data-grid/internals';
9
9
  import { GridPinnedPosition } from './gridColumnPinningInterface';
10
10
  import { gridPinnedColumnsSelector } from './gridColumnPinningSelector';
@@ -40,49 +40,6 @@ export var useGridColumnPinning = function useGridColumnPinning(apiRef, props) {
40
40
  var _props$initialState4;
41
41
  var pinnedColumns = useGridSelector(apiRef, gridPinnedColumnsSelector);
42
42
  var theme = useTheme();
43
- // Each visible row (not to be confused with a filter result) is composed of a central .MuiDataGrid-row element
44
- // and up to two additional .MuiDataGrid-row's, one for the columns pinned to the left and another
45
- // for those on the right side. When hovering any of these elements, the :hover styles are applied only to
46
- // the row element that was actually hovered, not its additional siblings. To make it look like a contiguous row,
47
- // this method adds/removes the .Mui-hovered class to all of the row elements inside one visible row.
48
- var updateHoveredClassOnSiblingRows = React.useCallback(function (event) {
49
- var _pinnedColumns$left$l, _pinnedColumns$left, _pinnedColumns$right$, _pinnedColumns$right;
50
- if (props.disableColumnPinning) {
51
- return;
52
- }
53
- if (!Array.isArray(pinnedColumns.left) && !Array.isArray(pinnedColumns.right)) {
54
- return;
55
- }
56
- var nbLeftPinnedColumns = (_pinnedColumns$left$l = (_pinnedColumns$left = pinnedColumns.left) == null ? void 0 : _pinnedColumns$left.length) != null ? _pinnedColumns$left$l : 0;
57
- var nbRightPinnedColumns = (_pinnedColumns$right$ = (_pinnedColumns$right = pinnedColumns.right) == null ? void 0 : _pinnedColumns$right.length) != null ? _pinnedColumns$right$ : 0;
58
- if (nbLeftPinnedColumns + nbRightPinnedColumns === 0) {
59
- return;
60
- }
61
- var rowContainer = apiRef.current.virtualScrollerRef.current;
62
- if (!rowContainer) {
63
- return;
64
- }
65
- var index = event.currentTarget.dataset.rowindex;
66
- var rowElements = rowContainer.querySelectorAll(".".concat(gridClasses.row, "[data-rowindex=\"").concat(index, "\"]"));
67
- rowElements.forEach(function (row) {
68
- // Ignore rows from other grid inside the hovered row
69
- if (row.closest(".".concat(gridClasses.virtualScroller)) === apiRef.current.virtualScrollerRef.current) {
70
- if (event.type === 'mouseenter') {
71
- row.classList.add('Mui-hovered');
72
- } else {
73
- row.classList.remove('Mui-hovered');
74
- }
75
- }
76
- });
77
- }, [apiRef, pinnedColumns.left, pinnedColumns.right, props.disableColumnPinning]);
78
- var handleMouseEnter = React.useCallback(function (params, event) {
79
- updateHoveredClassOnSiblingRows(event);
80
- }, [updateHoveredClassOnSiblingRows]);
81
- var handleMouseLeave = React.useCallback(function (params, event) {
82
- updateHoveredClassOnSiblingRows(event);
83
- }, [updateHoveredClassOnSiblingRows]);
84
- useGridApiEventHandler(apiRef, 'rowMouseEnter', handleMouseEnter);
85
- useGridApiEventHandler(apiRef, 'rowMouseLeave', handleMouseLeave);
86
43
 
87
44
  /**
88
45
  * PRE-PROCESSING
@@ -0,0 +1,10 @@
1
+ export var DEFAULT_GRID_AUTOSIZE_OPTIONS = {
2
+ includeHeaders: true,
3
+ includeOutliers: false,
4
+ outliersFactor: 1.5,
5
+ expand: false
6
+ };
7
+
8
+ /**
9
+ * The Resize API interface that is available in the grid `apiRef`.
10
+ */
@@ -1,2 +1,3 @@
1
1
  export * from './columnResizeSelector';
2
- export * from './columnResizeState';
2
+ export * from './columnResizeState';
3
+ export * from './gridColumnResizeApi';