@mui/x-data-grid 6.9.1 → 6.10.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 (100) hide show
  1. package/CHANGELOG.md +287 -107
  2. package/DataGrid/DataGrid.js +5 -1
  3. package/README.md +1 -1
  4. package/components/GridPagination.js +16 -3
  5. package/components/cell/GridEditDateCell.js +1 -1
  6. package/components/cell/GridEditInputCell.js +3 -3
  7. package/components/toolbar/GridToolbarQuickFilter.d.ts +1 -1
  8. package/components/toolbar/GridToolbarQuickFilter.js +12 -9
  9. package/hooks/core/pipeProcessing/useGridPipeProcessing.js +4 -1
  10. package/hooks/core/useGridApiInitialization.d.ts +1 -0
  11. package/hooks/core/useGridApiInitialization.js +59 -36
  12. package/hooks/features/columns/gridColumnsUtils.js +2 -1
  13. package/hooks/features/dimensions/useGridDimensions.js +3 -3
  14. package/hooks/features/editing/useGridEditing.js +2 -1
  15. package/hooks/features/export/serializers/csvSerializer.d.ts +9 -6
  16. package/hooks/features/export/serializers/csvSerializer.js +78 -15
  17. package/hooks/features/export/useGridCsvExport.js +4 -3
  18. package/hooks/features/filter/gridFilterUtils.js +6 -5
  19. package/hooks/features/filter/useGridFilter.js +14 -2
  20. package/hooks/features/rows/gridRowsUtils.js +1 -1
  21. package/hooks/features/rows/useGridParamsApi.js +4 -4
  22. package/hooks/utils/useGridApiMethod.js +7 -23
  23. package/index.js +1 -1
  24. package/internals/index.d.ts +1 -0
  25. package/internals/index.js +1 -0
  26. package/joy/joySlots.js +17 -5
  27. package/legacy/DataGrid/DataGrid.js +5 -1
  28. package/legacy/components/GridPagination.js +16 -3
  29. package/legacy/components/cell/GridEditDateCell.js +1 -1
  30. package/legacy/components/cell/GridEditInputCell.js +3 -3
  31. package/legacy/components/toolbar/GridToolbarQuickFilter.js +12 -12
  32. package/legacy/hooks/core/pipeProcessing/useGridPipeProcessing.js +4 -1
  33. package/legacy/hooks/core/useGridApiInitialization.js +62 -39
  34. package/legacy/hooks/features/columns/gridColumnsUtils.js +4 -1
  35. package/legacy/hooks/features/dimensions/useGridDimensions.js +3 -3
  36. package/legacy/hooks/features/editing/useGridEditing.js +2 -1
  37. package/legacy/hooks/features/export/serializers/csvSerializer.js +84 -11
  38. package/legacy/hooks/features/export/useGridCsvExport.js +4 -3
  39. package/legacy/hooks/features/filter/gridFilterUtils.js +6 -5
  40. package/legacy/hooks/features/filter/useGridFilter.js +12 -2
  41. package/legacy/hooks/features/rows/gridRowsUtils.js +1 -1
  42. package/legacy/hooks/features/rows/useGridParamsApi.js +4 -4
  43. package/legacy/hooks/utils/useGridApiMethod.js +7 -25
  44. package/legacy/index.js +1 -1
  45. package/legacy/internals/index.js +1 -0
  46. package/legacy/joy/joySlots.js +17 -5
  47. package/legacy/locales/esES.js +3 -3
  48. package/legacy/locales/plPL.js +7 -7
  49. package/locales/esES.js +3 -3
  50. package/locales/plPL.js +7 -7
  51. package/models/api/gridEditingApi.d.ts +1 -1
  52. package/models/events/gridEventLookup.d.ts +0 -1
  53. package/models/gridExport.d.ts +8 -1
  54. package/models/gridFilterModel.d.ts +5 -0
  55. package/models/props/DataGridProps.d.ts +4 -1
  56. package/modern/DataGrid/DataGrid.js +5 -1
  57. package/modern/components/GridPagination.js +16 -2
  58. package/modern/components/cell/GridEditDateCell.js +1 -1
  59. package/modern/components/cell/GridEditInputCell.js +3 -3
  60. package/modern/components/toolbar/GridToolbarQuickFilter.js +12 -9
  61. package/modern/hooks/core/pipeProcessing/useGridPipeProcessing.js +4 -1
  62. package/modern/hooks/core/useGridApiInitialization.js +58 -36
  63. package/modern/hooks/features/columns/gridColumnsUtils.js +2 -1
  64. package/modern/hooks/features/dimensions/useGridDimensions.js +3 -3
  65. package/modern/hooks/features/editing/useGridEditing.js +1 -1
  66. package/modern/hooks/features/export/serializers/csvSerializer.js +78 -15
  67. package/modern/hooks/features/export/useGridCsvExport.js +3 -2
  68. package/modern/hooks/features/filter/gridFilterUtils.js +3 -2
  69. package/modern/hooks/features/filter/useGridFilter.js +14 -2
  70. package/modern/hooks/features/rows/gridRowsUtils.js +1 -1
  71. package/modern/hooks/features/rows/useGridParamsApi.js +1 -1
  72. package/modern/hooks/utils/useGridApiMethod.js +7 -23
  73. package/modern/index.js +1 -1
  74. package/modern/internals/index.js +1 -0
  75. package/modern/joy/joySlots.js +17 -4
  76. package/modern/locales/esES.js +3 -3
  77. package/modern/locales/plPL.js +7 -7
  78. package/node/DataGrid/DataGrid.js +5 -1
  79. package/node/components/GridPagination.js +16 -2
  80. package/node/components/cell/GridEditDateCell.js +1 -1
  81. package/node/components/cell/GridEditInputCell.js +3 -3
  82. package/node/components/toolbar/GridToolbarQuickFilter.js +12 -9
  83. package/node/hooks/core/pipeProcessing/useGridPipeProcessing.js +4 -1
  84. package/node/hooks/core/useGridApiInitialization.js +59 -36
  85. package/node/hooks/features/columns/gridColumnsUtils.js +2 -1
  86. package/node/hooks/features/dimensions/useGridDimensions.js +3 -3
  87. package/node/hooks/features/editing/useGridEditing.js +1 -1
  88. package/node/hooks/features/export/serializers/csvSerializer.js +78 -15
  89. package/node/hooks/features/export/useGridCsvExport.js +3 -2
  90. package/node/hooks/features/filter/gridFilterUtils.js +2 -1
  91. package/node/hooks/features/filter/useGridFilter.js +14 -2
  92. package/node/hooks/features/rows/gridRowsUtils.js +1 -1
  93. package/node/hooks/features/rows/useGridParamsApi.js +1 -1
  94. package/node/hooks/utils/useGridApiMethod.js +7 -23
  95. package/node/index.js +1 -1
  96. package/node/internals/index.js +8 -0
  97. package/node/joy/joySlots.js +17 -4
  98. package/node/locales/esES.js +3 -3
  99. package/node/locales/plPL.js +7 -7
  100. package/package.json +1 -1
@@ -181,6 +181,7 @@ DataGridRaw.propTypes = {
181
181
  value: PropTypes.any
182
182
  })).isRequired,
183
183
  logicOperator: PropTypes.oneOf(['and', 'or']),
184
+ quickFilterExcludeHiddenColumns: PropTypes.bool,
184
185
  quickFilterLogicOperator: PropTypes.oneOf(['and', 'or']),
185
186
  quickFilterValues: PropTypes.array
186
187
  }),
@@ -513,7 +514,10 @@ DataGridRaw.propTypes = {
513
514
  * Select the pageSize dynamically using the component UI.
514
515
  * @default [25, 50, 100]
515
516
  */
516
- pageSizeOptions: PropTypes.arrayOf(PropTypes.number),
517
+ pageSizeOptions: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.shape({
518
+ label: PropTypes.string.isRequired,
519
+ value: PropTypes.number.isRequired
520
+ })]).isRequired),
517
521
  pagination: props => {
518
522
  if (props.pagination === false) {
519
523
  return new Error(['MUI: `<DataGrid pagination={false} />` is not a valid prop.', 'Infinite scrolling is not available in the MIT version.', '', 'You need to upgrade to DataGridPro or DataGridPremium component to disable the pagination.'].join('\n'));
package/README.md CHANGED
@@ -7,7 +7,7 @@ It's part of MUI X, an open core extension of MUI, with advanced components.
7
7
 
8
8
  Install the package in your project directory with:
9
9
 
10
- ```sh
10
+ ```bash
11
11
  // with npm
12
12
  npm install @mui/x-data-grid
13
13
 
@@ -25,7 +25,6 @@ const GridPaginationRoot = styled(TablePagination)(({
25
25
  }
26
26
  }));
27
27
  export const GridPagination = /*#__PURE__*/React.forwardRef(function GridPagination(props, ref) {
28
- var _rootProps$pageSizeOp;
29
28
  const apiRef = useGridApiContext();
30
29
  const rootProps = useGridRootProps();
31
30
  const paginationModel = useGridSelector(apiRef, gridPaginationModelSelector);
@@ -42,22 +41,36 @@ export const GridPagination = /*#__PURE__*/React.forwardRef(function GridPaginat
42
41
  const handlePageChange = React.useCallback((_, page) => {
43
42
  apiRef.current.setPage(page);
44
43
  }, [apiRef]);
44
+ const isPageSizeIncludedInPageSizeOptions = pageSize => {
45
+ for (let i = 0; i < rootProps.pageSizeOptions.length; i += 1) {
46
+ const option = rootProps.pageSizeOptions[i];
47
+ if (typeof option === 'number') {
48
+ if (option === pageSize) {
49
+ return true;
50
+ }
51
+ } else if (option.value === pageSize) {
52
+ return true;
53
+ }
54
+ }
55
+ return false;
56
+ };
45
57
  if (process.env.NODE_ENV !== 'production') {
46
58
  var _rootProps$pagination, _rootProps$pagination2;
47
59
  // eslint-disable-next-line react-hooks/rules-of-hooks
48
60
  const warnedOnceMissingInPageSizeOptions = React.useRef(false);
49
61
  const pageSize = (_rootProps$pagination = (_rootProps$pagination2 = rootProps.paginationModel) == null ? void 0 : _rootProps$pagination2.pageSize) != null ? _rootProps$pagination : paginationModel.pageSize;
50
- if (!warnedOnceMissingInPageSizeOptions.current && !rootProps.autoPageSize && !rootProps.pageSizeOptions.includes(pageSize)) {
62
+ if (!warnedOnceMissingInPageSizeOptions.current && !rootProps.autoPageSize && !isPageSizeIncludedInPageSizeOptions(pageSize)) {
51
63
  console.warn([`MUI: The page size \`${paginationModel.pageSize}\` is not preset in the \`pageSizeOptions\``, `Add it to show the pagination select.`].join('\n'));
52
64
  warnedOnceMissingInPageSizeOptions.current = true;
53
65
  }
54
66
  }
67
+ const pageSizeOptions = isPageSizeIncludedInPageSizeOptions(paginationModel.pageSize) ? rootProps.pageSizeOptions : [];
55
68
  return /*#__PURE__*/_jsx(GridPaginationRoot, _extends({
56
69
  ref: ref,
57
70
  component: "div",
58
71
  count: rowCount,
59
72
  page: paginationModel.page <= lastPage ? paginationModel.page : lastPage,
60
- rowsPerPageOptions: (_rootProps$pageSizeOp = rootProps.pageSizeOptions) != null && _rootProps$pageSizeOp.includes(paginationModel.pageSize) ? rootProps.pageSizeOptions : [],
73
+ rowsPerPageOptions: pageSizeOptions,
61
74
  rowsPerPage: paginationModel.pageSize,
62
75
  onPageChange: handlePageChange,
63
76
  onRowsPerPageChange: handlePageSizeChange
@@ -112,7 +112,7 @@ function GridEditDateCell(props) {
112
112
  const meta = apiRef.current.unstable_getEditCellMeta(id, field);
113
113
  const handleInputRef = el => {
114
114
  inputRef.current = el;
115
- if (meta.unstable_updateValueOnRender && !hasUpdatedEditValueOnMount.current) {
115
+ if (meta != null && meta.unstable_updateValueOnRender && !hasUpdatedEditValueOnMount.current) {
116
116
  const inputValue = inputRef.current.value;
117
117
  const parsedDate = parseValueToDate(inputValue);
118
118
  setValueState({
@@ -68,12 +68,12 @@ const GridEditInputCell = /*#__PURE__*/React.forwardRef((props, ref) => {
68
68
  unstable_skipValueParser: true
69
69
  }, event);
70
70
  }, [apiRef, debounceMs, field, id, onValueChange]);
71
- const meta = apiRef.current.unstable_getEditCellMeta ? apiRef.current.unstable_getEditCellMeta(id, field) : {};
71
+ const meta = apiRef.current.unstable_getEditCellMeta(id, field);
72
72
  React.useEffect(() => {
73
- if (meta.changeReason !== 'debouncedSetEditCellValue') {
73
+ if ((meta == null ? void 0 : meta.changeReason) !== 'debouncedSetEditCellValue') {
74
74
  setValueState(value);
75
75
  }
76
- }, [meta.changeReason, value]);
76
+ }, [meta, value]);
77
77
  useEnhancedEffect(() => {
78
78
  if (hasFocus) {
79
79
  inputRef.current.focus();
@@ -13,7 +13,7 @@ export type GridToolbarQuickFilterProps = TextFieldProps & {
13
13
  * @param {any[]} values The new values passed to the quick filter model
14
14
  * @returns {string} The string to display in the text field
15
15
  */
16
- quickFilterFormatter?: (values: GridFilterModel['quickFilterValues']) => string;
16
+ quickFilterFormatter?: (values: NonNullable<GridFilterModel['quickFilterValues']>) => string;
17
17
  /**
18
18
  * The debounce time in milliseconds.
19
19
  * @default 500
@@ -56,18 +56,20 @@ function GridToolbarQuickFilter(props) {
56
56
  const rootProps = useGridRootProps();
57
57
  const quickFilterValues = useGridSelector(apiRef, gridQuickFilterValuesSelector);
58
58
  const [searchValue, setSearchValue] = React.useState(() => quickFilterFormatter(quickFilterValues != null ? quickFilterValues : []));
59
- const [prevQuickFilterValues, setPrevQuickFilterValues] = React.useState(quickFilterValues);
59
+ const prevQuickFilterValuesRef = React.useRef(quickFilterValues);
60
60
  React.useEffect(() => {
61
- if (!isDeepEqual(prevQuickFilterValues, quickFilterValues)) {
61
+ if (!isDeepEqual(prevQuickFilterValuesRef.current, quickFilterValues)) {
62
62
  // The model of quick filter value has been updated
63
- setPrevQuickFilterValues(quickFilterValues);
63
+ prevQuickFilterValuesRef.current = quickFilterValues;
64
64
 
65
65
  // Update the input value if needed to match the new model
66
66
  setSearchValue(prevSearchValue => isDeepEqual(quickFilterParser(prevSearchValue), quickFilterValues) ? prevSearchValue : quickFilterFormatter(quickFilterValues != null ? quickFilterValues : []));
67
67
  }
68
- }, [prevQuickFilterValues, quickFilterValues, quickFilterFormatter, quickFilterParser]);
68
+ }, [quickFilterValues, quickFilterFormatter, quickFilterParser]);
69
69
  const updateSearchValue = React.useCallback(newSearchValue => {
70
- apiRef.current.setQuickFilterValues(quickFilterParser(newSearchValue));
70
+ const newQuickFilterValues = quickFilterParser(newSearchValue);
71
+ prevQuickFilterValuesRef.current = newQuickFilterValues;
72
+ apiRef.current.setQuickFilterValues(newQuickFilterValues);
71
73
  }, [apiRef, quickFilterParser]);
72
74
  const debouncedUpdateSearchValue = React.useMemo(() => debounce(updateSearchValue, debounceMs), [updateSearchValue, debounceMs]);
73
75
  const handleSearchValueChange = React.useCallback(event => {
@@ -87,8 +89,9 @@ function GridToolbarQuickFilter(props) {
87
89
  onChange: handleSearchValueChange,
88
90
  placeholder: apiRef.current.getLocaleText('toolbarQuickFilterPlaceholder'),
89
91
  "aria-label": apiRef.current.getLocaleText('toolbarQuickFilterLabel'),
90
- type: "search",
91
- InputProps: {
92
+ type: "search"
93
+ }, other, {
94
+ InputProps: _extends({
92
95
  startAdornment: /*#__PURE__*/_jsx(rootProps.slots.quickFilterIcon, {
93
96
  fontSize: "small"
94
97
  }),
@@ -104,8 +107,8 @@ function GridToolbarQuickFilter(props) {
104
107
  fontSize: "small"
105
108
  })
106
109
  }))
107
- }
108
- }, other, (_rootProps$slotProps2 = rootProps.slotProps) == null ? void 0 : _rootProps$slotProps2.baseTextField));
110
+ }, other.InputProps)
111
+ }, (_rootProps$slotProps2 = rootProps.slotProps) == null ? void 0 : _rootProps$slotProps2.baseTextField));
109
112
  }
110
113
  process.env.NODE_ENV !== "production" ? GridToolbarQuickFilter.propTypes = {
111
114
  // ----------------------------- Warning --------------------------------
@@ -33,13 +33,16 @@ import { useGridApiMethod } from '../../utils/useGridApiMethod';
33
33
  */
34
34
  export const useGridPipeProcessing = apiRef => {
35
35
  const processorsCache = React.useRef({});
36
+ const isRunning = React.useRef(false);
36
37
  const runAppliers = React.useCallback(groupCache => {
37
- if (!groupCache) {
38
+ if (isRunning.current || !groupCache) {
38
39
  return;
39
40
  }
41
+ isRunning.current = true;
40
42
  Object.values(groupCache.appliers).forEach(callback => {
41
43
  callback();
42
44
  });
45
+ isRunning.current = false;
43
46
  }, []);
44
47
  const registerPipeProcessor = React.useCallback((group, id, processor) => {
45
48
  if (!processorsCache.current[group]) {
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
2
  import { DataGridProcessedProps } from '../../models/props/DataGridProps';
3
3
  import type { GridApiCommon, GridPrivateApiCommon } from '../../models/api/gridApiCommon';
4
+ export declare function unwrapPrivateAPI<PrivateApi extends GridPrivateApiCommon, Api extends GridApiCommon>(publicApi: Api): PrivateApi;
4
5
  export declare function useGridApiInitialization<PrivateApi extends GridPrivateApiCommon, Api extends GridApiCommon>(inputApiRef: React.MutableRefObject<Api> | undefined, props: Pick<DataGridProcessedProps, 'signature'>): React.MutableRefObject<PrivateApi>;
@@ -3,58 +3,80 @@ import { Store } from '../../utils/Store';
3
3
  import { useGridApiMethod } from '../utils/useGridApiMethod';
4
4
  import { GridSignature } from '../utils/useGridApiEventHandler';
5
5
  import { EventManager } from '../../utils/EventManager';
6
+ const SYMBOL_API_PRIVATE = Symbol('mui.api_private');
6
7
  const isSyntheticEvent = event => {
7
8
  return event.isPropagationStopped !== undefined;
8
9
  };
10
+ export function unwrapPrivateAPI(publicApi) {
11
+ return publicApi[SYMBOL_API_PRIVATE];
12
+ }
9
13
  let globalId = 0;
10
- const wrapPublicApi = publicApi => {
11
- const privateOnlyApi = {};
12
- privateOnlyApi.getPublicApi = () => publicApi;
13
- privateOnlyApi.register = (visibility, methods) => {
14
+ function createPrivateAPI(publicApiRef) {
15
+ var _publicApiRef$current;
16
+ const existingPrivateApi = (_publicApiRef$current = publicApiRef.current) == null ? void 0 : _publicApiRef$current[SYMBOL_API_PRIVATE];
17
+ if (existingPrivateApi) {
18
+ return existingPrivateApi;
19
+ }
20
+ const state = {};
21
+ const privateApi = {
22
+ state,
23
+ store: Store.create(state),
24
+ instanceId: {
25
+ id: globalId
26
+ }
27
+ };
28
+ globalId += 1;
29
+ privateApi.getPublicApi = () => publicApiRef.current;
30
+ privateApi.register = (visibility, methods) => {
14
31
  Object.keys(methods).forEach(methodName => {
15
- if (visibility === 'public') {
16
- publicApi[methodName] = methods[methodName];
32
+ const method = methods[methodName];
33
+ const currentPrivateMethod = privateApi[methodName];
34
+ if ((currentPrivateMethod == null ? void 0 : currentPrivateMethod.spying) === true) {
35
+ currentPrivateMethod.target = method;
17
36
  } else {
18
- privateOnlyApi[methodName] = methods[methodName];
37
+ privateApi[methodName] = method;
38
+ }
39
+ if (visibility === 'public') {
40
+ const publicApi = publicApiRef.current;
41
+ const currentPublicMethod = publicApi[methodName];
42
+ if ((currentPublicMethod == null ? void 0 : currentPublicMethod.spying) === true) {
43
+ currentPublicMethod.target = method;
44
+ } else {
45
+ publicApi[methodName] = method;
46
+ }
19
47
  }
20
48
  });
21
49
  };
22
- const handler = {
23
- get: (obj, prop) => {
24
- if (prop in obj) {
25
- return obj[prop];
26
- }
27
- return privateOnlyApi[prop];
50
+ privateApi.register('private', {
51
+ caches: {},
52
+ eventManager: new EventManager()
53
+ });
54
+ return privateApi;
55
+ }
56
+ function createPublicAPI(privateApiRef) {
57
+ const publicApi = {
58
+ get state() {
59
+ return privateApiRef.current.state;
28
60
  },
29
- set: (obj, prop, value) => {
30
- obj[prop] = value;
31
- return true;
32
- }
61
+ get store() {
62
+ return privateApiRef.current.store;
63
+ },
64
+ get instanceId() {
65
+ return privateApiRef.current.instanceId;
66
+ },
67
+ [SYMBOL_API_PRIVATE]: privateApiRef.current
33
68
  };
34
- return new Proxy(publicApi, handler);
35
- };
69
+ return publicApi;
70
+ }
36
71
  export function useGridApiInitialization(inputApiRef, props) {
37
72
  const publicApiRef = React.useRef();
38
- if (!publicApiRef.current) {
39
- const state = {};
40
- publicApiRef.current = {
41
- state,
42
- store: Store.create(state),
43
- instanceId: {
44
- id: globalId
45
- }
46
- };
47
- globalId += 1;
48
- }
49
73
  const privateApiRef = React.useRef();
50
74
  if (!privateApiRef.current) {
51
- privateApiRef.current = wrapPublicApi(publicApiRef.current);
52
- privateApiRef.current.register('private', {
53
- caches: {},
54
- eventManager: new EventManager()
55
- });
75
+ privateApiRef.current = createPrivateAPI(publicApiRef);
76
+ }
77
+ if (!publicApiRef.current) {
78
+ publicApiRef.current = createPublicAPI(privateApiRef);
56
79
  }
57
- React.useImperativeHandle(inputApiRef, () => publicApiRef.current, [publicApiRef]);
58
80
  const publishEvent = React.useCallback((...args) => {
59
81
  const [name, params, event = {}] = args;
60
82
  event.defaultMuiPrevented = false;
@@ -77,6 +99,7 @@ export function useGridApiInitialization(inputApiRef, props) {
77
99
  subscribeEvent,
78
100
  publishEvent
79
101
  }, 'public');
102
+ React.useImperativeHandle(inputApiRef, () => publicApiRef.current, [publicApiRef]);
80
103
  React.useEffect(() => {
81
104
  const api = privateApiRef.current;
82
105
  return () => {
@@ -15,6 +15,7 @@ export function computeFlexColumnsWidth({
15
15
  totalFlexUnits,
16
16
  flexColumns
17
17
  }) {
18
+ const uniqueFlexColumns = new Set(flexColumns.map(col => col.field));
18
19
  const flexColumnsLookup = {
19
20
  all: {},
20
21
  frozenFields: [],
@@ -30,7 +31,7 @@ export function computeFlexColumnsWidth({
30
31
  // Step 5 of https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
31
32
  function loopOverFlexItems() {
32
33
  // 5a: If all the flex items on the line are frozen, free space has been distributed.
33
- if (flexColumnsLookup.frozenFields.length === flexColumns.length) {
34
+ if (flexColumnsLookup.frozenFields.length === uniqueFlexColumns.size) {
34
35
  return;
35
36
  }
36
37
  const violationsLookup = {
@@ -73,7 +73,7 @@ export function useGridDimensions(apiRef, props) {
73
73
  let hasScrollY;
74
74
  if (props.autoHeight) {
75
75
  hasScrollY = false;
76
- hasScrollX = columnsTotalWidth > rootDimensionsRef.current.width;
76
+ hasScrollX = Math.round(columnsTotalWidth) > Math.round(rootDimensionsRef.current.width);
77
77
  viewportOuterSize = {
78
78
  width: rootDimensionsRef.current.width,
79
79
  height: rowsMeta.currentPageTotalHeight + (hasScrollX ? scrollBarSize : 0)
@@ -85,11 +85,11 @@ export function useGridDimensions(apiRef, props) {
85
85
  };
86
86
  const scrollInformation = hasScroll({
87
87
  content: {
88
- width: columnsTotalWidth,
88
+ width: Math.round(columnsTotalWidth),
89
89
  height: rowsMeta.currentPageTotalHeight
90
90
  },
91
91
  container: {
92
- width: viewportOuterSize.width,
92
+ width: Math.round(viewportOuterSize.width),
93
93
  height: viewportOuterSize.height - pinnedRowsHeight.top - pinnedRowsHeight.bottom
94
94
  },
95
95
  scrollBarSize
@@ -106,8 +106,9 @@ export const useGridEditing = (apiRef, props) => {
106
106
  return props.editMode === GridEditModes.Cell ? apiRef.current.getRowWithUpdatedValuesFromCellEditing(id, field) : apiRef.current.getRowWithUpdatedValuesFromRowEditing(id);
107
107
  }, [apiRef, props.editMode]);
108
108
  const getEditCellMeta = React.useCallback((id, field) => {
109
+ var _editingState$id$fiel, _editingState$id;
109
110
  const editingState = gridEditRowsStateSelector(apiRef.current.state);
110
- return editingState[id][field];
111
+ return (_editingState$id$fiel = (_editingState$id = editingState[id]) == null ? void 0 : _editingState$id[field]) != null ? _editingState$id$fiel : null;
111
112
  }, [apiRef]);
112
113
  const editingSharedApi = {
113
114
  isCellEditable,
@@ -1,6 +1,8 @@
1
- import { GridRowId } from '../../../../models';
2
- import { GridCellParams } from '../../../../models/params/gridCellParams';
3
- import { GridStateColDef } from '../../../../models/colDef/gridColDef';
1
+ /// <reference types="react" />
2
+ import type { GridCsvExportOptions, GridRowId } from '../../../../models';
3
+ import type { GridCellParams } from '../../../../models/params/gridCellParams';
4
+ import type { GridStateColDef } from '../../../../models/colDef/gridColDef';
5
+ import type { GridApiCommunity } from '../../../../models/api/gridApiCommunity';
4
6
  export declare const serializeCellValue: (cellParams: GridCellParams, options: {
5
7
  delimiterCharacter: string;
6
8
  ignoreValueFormatter: boolean;
@@ -8,10 +10,11 @@ export declare const serializeCellValue: (cellParams: GridCellParams, options: {
8
10
  interface BuildCSVOptions {
9
11
  columns: GridStateColDef[];
10
12
  rowIds: GridRowId[];
11
- getCellParams: (id: GridRowId, field: string) => GridCellParams;
12
- delimiterCharacter: string;
13
- includeHeaders: boolean;
13
+ delimiterCharacter: NonNullable<GridCsvExportOptions['delimiter']>;
14
+ includeHeaders: NonNullable<GridCsvExportOptions['includeHeaders']>;
15
+ includeColumnGroupsHeaders: NonNullable<GridCsvExportOptions['includeColumnGroupsHeaders']>;
14
16
  ignoreValueFormatter: boolean;
17
+ apiRef: React.MutableRefObject<GridApiCommunity>;
15
18
  }
16
19
  export declare function buildCSV(options: BuildCSVOptions): string;
17
20
  export {};
@@ -37,43 +37,106 @@ export const serializeCellValue = (cellParams, options) => {
37
37
  return sanitizeCellValue(value, delimiterCharacter);
38
38
  };
39
39
  const objectFormattedValueWarning = buildWarning(['MUI: When the value of a field is an object or a `renderCell` is provided, the CSV export might not display the value correctly.', 'You can provide a `valueFormatter` with a string representation to be used.']);
40
+ class CSVRow {
41
+ constructor(options) {
42
+ this.options = void 0;
43
+ this.rowString = '';
44
+ this.isEmpty = true;
45
+ this.options = options;
46
+ }
47
+ addValue(value) {
48
+ if (!this.isEmpty) {
49
+ this.rowString += this.options.delimiterCharacter;
50
+ }
51
+ if (value === null || value === undefined) {
52
+ this.rowString += '';
53
+ } else if (typeof this.options.sanitizeCellValue === 'function') {
54
+ this.rowString += this.options.sanitizeCellValue(value, this.options.delimiterCharacter);
55
+ } else {
56
+ this.rowString += value;
57
+ }
58
+ this.isEmpty = false;
59
+ }
60
+ getRowString() {
61
+ return this.rowString;
62
+ }
63
+ }
40
64
  const serializeRow = ({
41
65
  id,
42
66
  columns,
43
67
  getCellParams,
44
68
  delimiterCharacter,
45
69
  ignoreValueFormatter
46
- }) => columns.map(column => {
47
- const cellParams = getCellParams(id, column.field);
48
- if (process.env.NODE_ENV !== 'production') {
49
- if (String(cellParams.formattedValue) === '[object Object]') {
50
- objectFormattedValueWarning();
70
+ }) => {
71
+ const row = new CSVRow({
72
+ delimiterCharacter
73
+ });
74
+ columns.forEach(column => {
75
+ const cellParams = getCellParams(id, column.field);
76
+ if (process.env.NODE_ENV !== 'production') {
77
+ if (String(cellParams.formattedValue) === '[object Object]') {
78
+ objectFormattedValueWarning();
79
+ }
51
80
  }
52
- }
53
- return serializeCellValue(cellParams, {
54
- delimiterCharacter,
55
- ignoreValueFormatter
81
+ row.addValue(serializeCellValue(cellParams, {
82
+ delimiterCharacter,
83
+ ignoreValueFormatter
84
+ }));
56
85
  });
57
- });
86
+ return row.getRowString();
87
+ };
58
88
  export function buildCSV(options) {
59
89
  const {
60
90
  columns,
61
91
  rowIds,
62
- getCellParams,
63
92
  delimiterCharacter,
64
93
  includeHeaders,
65
- ignoreValueFormatter
94
+ includeColumnGroupsHeaders,
95
+ ignoreValueFormatter,
96
+ apiRef
66
97
  } = options;
67
98
  const CSVBody = rowIds.reduce((acc, id) => `${acc}${serializeRow({
68
99
  id,
69
100
  columns,
70
- getCellParams,
101
+ getCellParams: apiRef.current.getCellParams,
71
102
  delimiterCharacter,
72
103
  ignoreValueFormatter
73
- }).join(delimiterCharacter)}\r\n`, '').trim();
104
+ })}\r\n`, '').trim();
74
105
  if (!includeHeaders) {
75
106
  return CSVBody;
76
107
  }
77
- const CSVHead = `${columns.filter(column => column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field).map(column => sanitizeCellValue(column.headerName || column.field, delimiterCharacter)).join(delimiterCharacter)}\r\n`;
108
+ const filteredColumns = columns.filter(column => column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field);
109
+ const headerRows = [];
110
+ if (includeColumnGroupsHeaders) {
111
+ const columnGroupLookup = apiRef.current.unstable_getAllGroupDetails();
112
+ let maxColumnGroupsDepth = 0;
113
+ const columnGroupPathsLookup = filteredColumns.reduce((acc, column) => {
114
+ const columnGroupPath = apiRef.current.unstable_getColumnGroupPath(column.field);
115
+ acc[column.field] = columnGroupPath;
116
+ maxColumnGroupsDepth = Math.max(maxColumnGroupsDepth, columnGroupPath.length);
117
+ return acc;
118
+ }, {});
119
+ for (let i = 0; i < maxColumnGroupsDepth; i += 1) {
120
+ const headerGroupRow = new CSVRow({
121
+ delimiterCharacter,
122
+ sanitizeCellValue
123
+ });
124
+ headerRows.push(headerGroupRow);
125
+ filteredColumns.forEach(column => {
126
+ const columnGroupId = (columnGroupPathsLookup[column.field] || [])[i];
127
+ const columnGroup = columnGroupLookup[columnGroupId];
128
+ headerGroupRow.addValue(columnGroup ? columnGroup.headerName || columnGroup.groupId : '');
129
+ });
130
+ }
131
+ }
132
+ const mainHeaderRow = new CSVRow({
133
+ delimiterCharacter,
134
+ sanitizeCellValue
135
+ });
136
+ filteredColumns.forEach(column => {
137
+ mainHeaderRow.addValue(column.headerName || column.field);
138
+ });
139
+ headerRows.push(mainHeaderRow);
140
+ const CSVHead = `${headerRows.map(row => row.getRowString()).join('\r\n')}\r\n`;
78
141
  return `${CSVHead}${CSVBody}`.trim();
79
142
  }
@@ -19,7 +19,7 @@ export const useGridCsvExport = (apiRef, props) => {
19
19
  const ignoreValueFormatterProp = props.unstable_ignoreValueFormatterDuringExport;
20
20
  const ignoreValueFormatter = (typeof ignoreValueFormatterProp === 'object' ? ignoreValueFormatterProp == null ? void 0 : ignoreValueFormatterProp.csvExport : ignoreValueFormatterProp) || false;
21
21
  const getDataAsCsv = React.useCallback((options = {}) => {
22
- var _options$getRowsToExp, _options$includeHeade;
22
+ var _options$getRowsToExp, _options$includeHeade, _options$includeColum;
23
23
  logger.debug(`Get data as CSV`);
24
24
  const exportedColumns = getColumnsToExport({
25
25
  apiRef,
@@ -32,10 +32,11 @@ export const useGridCsvExport = (apiRef, props) => {
32
32
  return buildCSV({
33
33
  columns: exportedColumns,
34
34
  rowIds: exportedRowIds,
35
- getCellParams: apiRef.current.getCellParams,
36
35
  delimiterCharacter: options.delimiter || ',',
37
36
  includeHeaders: (_options$includeHeade = options.includeHeaders) != null ? _options$includeHeade : true,
38
- ignoreValueFormatter
37
+ includeColumnGroupsHeaders: (_options$includeColum = options.includeColumnGroupsHeaders) != null ? _options$includeColum : true,
38
+ ignoreValueFormatter,
39
+ apiRef
39
40
  });
40
41
  }, [logger, apiRef, ignoreValueFormatter]);
41
42
  const exportDataAsCsv = React.useCallback(options => {
@@ -3,7 +3,7 @@ import { GridLogicOperator } from '../../../models';
3
3
  import { GLOBAL_API_REF, isInternalFilter } from '../../../colDef/utils';
4
4
  import { getDefaultGridFilterModel } from './gridFilterState';
5
5
  import { buildWarning } from '../../../utils/warning';
6
- import { gridColumnFieldsSelector, gridColumnLookupSelector } from '../columns';
6
+ import { gridColumnFieldsSelector, gridColumnLookupSelector, gridVisibleColumnFieldsSelector } from '../columns';
7
7
  /**
8
8
  * Adds default values to the optional fields of a filter items.
9
9
  * @param {GridFilterItem} item The raw filter item.
@@ -154,12 +154,13 @@ export const buildAggregatedFilterItemsApplier = (getRowId, filterModel, apiRef)
154
154
  * @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters.
155
155
  */
156
156
  export const buildAggregatedQuickFilterApplier = (getRowId, filterModel, apiRef) => {
157
- var _filterModel$quickFil, _filterModel$quickFil2;
157
+ var _filterModel$quickFil, _filterModel$quickFil2, _filterModel$quickFil3;
158
158
  const quickFilterValues = (_filterModel$quickFil = (_filterModel$quickFil2 = filterModel.quickFilterValues) == null ? void 0 : _filterModel$quickFil2.filter(Boolean)) != null ? _filterModel$quickFil : [];
159
159
  if (quickFilterValues.length === 0) {
160
160
  return null;
161
161
  }
162
- const columnFields = gridColumnFieldsSelector(apiRef);
162
+ const quickFilterExcludeHiddenColumns = (_filterModel$quickFil3 = filterModel.quickFilterExcludeHiddenColumns) != null ? _filterModel$quickFil3 : false;
163
+ const columnFields = quickFilterExcludeHiddenColumns ? gridVisibleColumnFieldsSelector(apiRef) : gridColumnFieldsSelector(apiRef);
163
164
  const appliersPerField = [];
164
165
  columnFields.forEach(field => {
165
166
  const column = apiRef.current.getColumn(field);
@@ -276,12 +277,12 @@ export const passFilterLogic = (allFilterItemResults, allQuickFilterResults, fil
276
277
 
277
278
  // get result for quick filter model
278
279
  if (cleanedQuickFilterResults.length > 0 && filterModel.quickFilterValues != null) {
279
- var _filterModel$quickFil3;
280
+ var _filterModel$quickFil4;
280
281
  // Return true if the item pass with one of the rows
281
282
  const quickFilterValuePredicate = value => {
282
283
  return cleanedQuickFilterResults.some(quickFilterValueResult => quickFilterValueResult[value]);
283
284
  };
284
- const quickFilterLogicOperator = (_filterModel$quickFil3 = filterModel.quickFilterLogicOperator) != null ? _filterModel$quickFil3 : getDefaultGridFilterModel().quickFilterLogicOperator;
285
+ const quickFilterLogicOperator = (_filterModel$quickFil4 = filterModel.quickFilterLogicOperator) != null ? _filterModel$quickFil4 : getDefaultGridFilterModel().quickFilterLogicOperator;
285
286
  if (quickFilterLogicOperator === GridLogicOperator.And) {
286
287
  const passesAllQuickFilterValues = filterModel.quickFilterValues.every(quickFilterValuePredicate);
287
288
  if (!passesAllQuickFilterValues) {
@@ -21,6 +21,7 @@ export const filterStateInitializer = (state, props, apiRef) => {
21
21
  return _extends({}, state, {
22
22
  filter: {
23
23
  filterModel: sanitizeFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef),
24
+ filteredRowsLookup: {},
24
25
  filteredDescendantCountLookup: {}
25
26
  },
26
27
  visibleRowsLookup: {}
@@ -258,6 +259,9 @@ export const useGridFilter = (apiRef, props) => {
258
259
  }, [props.slots.filterPanel, (_props$slotProps2 = props.slotProps) == null ? void 0 : _props$slotProps2.filterPanel]);
259
260
  const dataRowIdToIdLookup = apiRef.current.state.rows.dataRowIdToModelLookup;
260
261
  const rows = React.useMemo(() => Object.values(dataRowIdToIdLookup), [dataRowIdToIdLookup]);
262
+ const {
263
+ getRowId
264
+ } = props;
261
265
  const flatFilteringMethod = React.useCallback(params => {
262
266
  if (props.filterMode !== 'client' || !params.isRowMatchingFilters) {
263
267
  return {
@@ -277,9 +281,10 @@ export const useGridFilter = (apiRef, props) => {
277
281
  };
278
282
  for (let i = 0; i < rows.length; i += 1) {
279
283
  const row = rows[i];
284
+ const id = getRowId ? getRowId(row) : row.id;
280
285
  isRowMatchingFilters(row, undefined, result);
281
286
  const isRowPassing = passFilterLogic([result.passingFilterItems], [result.passingQuickFilterValues], params.filterModel, apiRef, filterCache);
282
- filteredRowsLookup[row.id] = isRowPassing;
287
+ filteredRowsLookup[id] = isRowPassing;
283
288
  }
284
289
  const footerId = 'auto-generated-group-footer-root';
285
290
  const footer = dataRowIdToModelLookup[footerId];
@@ -290,7 +295,7 @@ export const useGridFilter = (apiRef, props) => {
290
295
  filteredRowsLookup,
291
296
  filteredDescendantCountLookup: {}
292
297
  };
293
- }, [apiRef, props.filterMode, rows]);
298
+ }, [apiRef, rows, props.filterMode, getRowId]);
294
299
  useGridRegisterPipeProcessor(apiRef, 'columnMenu', addColumnMenuItem);
295
300
  useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
296
301
  useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
@@ -332,6 +337,13 @@ export const useGridFilter = (apiRef, props) => {
332
337
  useGridApiEventHandler(apiRef, 'columnsChange', handleColumnsChange);
333
338
  useGridApiEventHandler(apiRef, 'activeStrategyProcessorChange', handleStrategyProcessorChange);
334
339
  useGridApiEventHandler(apiRef, 'rowExpansionChange', updateVisibleRowsLookupState);
340
+ useGridApiEventHandler(apiRef, 'columnVisibilityModelChange', () => {
341
+ const filterModel = gridFilterModelSelector(apiRef);
342
+ if (filterModel.quickFilterValues && filterModel.quickFilterExcludeHiddenColumns) {
343
+ // re-apply filters because the quick filter results may have changed
344
+ apiRef.current.unstable_applyFilters();
345
+ }
346
+ });
335
347
 
336
348
  /**
337
349
  * 1ST RENDER
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { gridPinnedRowsSelector } from './gridRowsSelector';
3
3
  import { gridDensityFactorSelector } from '../density/densitySelector';
4
4
  export const GRID_ROOT_GROUP_ID = `auto-generated-group-node-root`;
5
- export const GRID_ID_AUTOGENERATED = Symbol('mui-autogenerated-id');
5
+ export const GRID_ID_AUTOGENERATED = Symbol('mui.id_autogenerated');
6
6
  export const buildRootGroup = () => ({
7
7
  type: 'group',
8
8
  id: GRID_ROOT_GROUP_ID,