@mui/x-data-grid-premium 7.26.0 → 7.27.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 (39) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/DataGridPremium/DataGridPremium.js +6 -0
  3. package/DataGridPremium/useDataGridPremiumComponent.js +1 -1
  4. package/esm/DataGridPremium/DataGridPremium.js +6 -0
  5. package/esm/DataGridPremium/useDataGridPremiumComponent.js +1 -1
  6. package/esm/hooks/features/clipboard/useGridClipboardImport.js +2 -2
  7. package/esm/hooks/features/export/index.js +1 -1
  8. package/esm/hooks/features/export/serializer/excelSerializer.js +69 -180
  9. package/esm/hooks/features/export/serializer/setupExcelExportWebWorker.js +53 -0
  10. package/esm/hooks/features/export/serializer/utils.js +93 -0
  11. package/esm/hooks/features/export/useGridExcelExport.js +11 -5
  12. package/esm/setupExcelExportWebWorker.js +1 -0
  13. package/esm/utils/releaseInfo.js +1 -1
  14. package/hooks/features/clipboard/useGridClipboardImport.js +2 -2
  15. package/hooks/features/export/index.d.ts +1 -1
  16. package/hooks/features/export/index.js +2 -2
  17. package/hooks/features/export/serializer/excelSerializer.d.ts +3 -31
  18. package/hooks/features/export/serializer/excelSerializer.js +74 -187
  19. package/hooks/features/export/serializer/setupExcelExportWebWorker.d.ts +2 -0
  20. package/hooks/features/export/serializer/setupExcelExportWebWorker.js +59 -0
  21. package/hooks/features/export/serializer/utils.d.ts +36 -0
  22. package/hooks/features/export/serializer/utils.js +106 -0
  23. package/hooks/features/export/useGridExcelExport.js +10 -3
  24. package/index.js +1 -1
  25. package/modern/DataGridPremium/DataGridPremium.js +6 -0
  26. package/modern/DataGridPremium/useDataGridPremiumComponent.js +1 -1
  27. package/modern/hooks/features/clipboard/useGridClipboardImport.js +2 -2
  28. package/modern/hooks/features/export/index.js +1 -1
  29. package/modern/hooks/features/export/serializer/excelSerializer.js +69 -180
  30. package/modern/hooks/features/export/serializer/setupExcelExportWebWorker.js +53 -0
  31. package/modern/hooks/features/export/serializer/utils.js +93 -0
  32. package/modern/hooks/features/export/useGridExcelExport.js +11 -5
  33. package/modern/index.js +1 -1
  34. package/modern/setupExcelExportWebWorker.js +1 -0
  35. package/modern/utils/releaseInfo.js +1 -1
  36. package/package.json +3 -3
  37. package/setupExcelExportWebWorker.d.ts +1 -0
  38. package/setupExcelExportWebWorker.js +12 -0
  39. package/utils/releaseInfo.js +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,68 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 7.27.0
7
+
8
+ _Feb 17, 2025_
9
+
10
+ We'd like to offer a big thanks to the 7 contributors who made this release possible. Here are some highlights ✨:
11
+
12
+ - ⚡ Improve Data Grid Excel export serialization performance
13
+ - 🐞 Bugfixes
14
+ - 🌍 Improve Polish (pl-PL) and Ukrainian (uk-UA) locale on the Data Grid
15
+
16
+ Special thanks go out to the community contributors who have helped make this release possible:
17
+ @pawelkula, @Neonin.
18
+ Following are all team members who have contributed to this release:
19
+ @cherniavskii, @JCQuintas, @oliviertassinari, @arminmeh and @LukasTy
20
+
21
+ ### Data Grid
22
+
23
+ #### `@mui/x-data-grid@7.27.0`
24
+
25
+ - [DataGrid] Add `resetPageOnSortFilter` prop that resets the page after sorting and filtering (#16580) @arminmeh
26
+ - [DataGrid] Avoid `undefined` value for pagination `rowCount` (#16558) @cherniavskii
27
+ - [l10n] Improve Polish (pl-PL) locale (#16594) @pawelkula
28
+ - [l10n] Improve Ukrainian (uk-UA) locale (#16593) @Neonin
29
+
30
+ #### `@mui/x-data-grid-pro@7.27.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
31
+
32
+ Same changes as in `@mui/x-data-grid@7.27.0`.
33
+
34
+ #### `@mui/x-data-grid-premium@7.27.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
35
+
36
+ Same changes as in `@mui/x-data-grid-pro@7.27.0`, plus:
37
+
38
+ - [DataGridPremium] Fix Excel export Web Worker demo not working in dev mode (#16532) @cherniavskii
39
+ - [DataGridPremium] Improve Excel export serialization performance (#16545) @cherniavskii
40
+ - [DataGridPremium] Namespace Excel export worker (#16539) @oliviertassinari
41
+
42
+ ### Date and Time Pickers
43
+
44
+ #### `@mui/x-date-pickers@7.27.0`
45
+
46
+ Internal changes.
47
+
48
+ #### `@mui/x-date-pickers-pro@7.27.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
49
+
50
+ Same changes as in `@mui/x-date-pickers@7.27.0`, plus:
51
+
52
+ - [DateRangePicker] Avoid unnecessary field section focusing (#16569) @LukasTy
53
+
54
+ ### Charts
55
+
56
+ #### `@mui/x-charts@7.27.0`
57
+
58
+ Internal changes.
59
+
60
+ #### `@mui/x-charts-pro@7.27.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
61
+
62
+ - [charts-pro] Fix automatic type overloads (#16579) @JCQuintas
63
+
64
+ ### Core
65
+
66
+ - [test] Fix Data Grid data source error test on React 18 (#16565) @arminmeh
67
+
6
68
  ## 7.26.0
7
69
 
8
70
  _Feb 7, 2025_
@@ -901,6 +901,12 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
901
901
  * @returns {Promise<R> | R} The final values to update the row.
902
902
  */
903
903
  processRowUpdate: _propTypes.default.func,
904
+ /**
905
+ * If `true`, the page is set to 0 after each sorting or filtering.
906
+ * This prop will be removed in the next major version and resetting the page will become the default behavior.
907
+ * @default false
908
+ */
909
+ resetPageOnSortFilter: _propTypes.default.bool,
904
910
  /**
905
911
  * The milliseconds throttle delay for resizing the grid.
906
912
  * @default 60
@@ -48,8 +48,8 @@ const useDataGridPremiumComponent = (inputApiRef, props) => {
48
48
  (0, _internals.useGridInitializeState)(_internals.columnPinningStateInitializer, apiRef, props);
49
49
  (0, _internals.useGridInitializeState)(_internals.columnsStateInitializer, apiRef, props);
50
50
  (0, _internals.useGridInitializeState)(_internals.rowPinningStateInitializer, apiRef, props);
51
- (0, _internals.useGridInitializeState)(_internals.paginationStateInitializer, apiRef, props);
52
51
  (0, _internals.useGridInitializeState)(_internals.rowsStateInitializer, apiRef, props);
52
+ (0, _internals.useGridInitializeState)(_internals.paginationStateInitializer, apiRef, props);
53
53
  (0, _internals.useGridInitializeState)(_internals.editingStateInitializer, apiRef, props);
54
54
  (0, _internals.useGridInitializeState)(_internals.focusStateInitializer, apiRef, props);
55
55
  (0, _internals.useGridInitializeState)(_internals.sortingStateInitializer, apiRef, props);
@@ -894,6 +894,12 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
894
894
  * @returns {Promise<R> | R} The final values to update the row.
895
895
  */
896
896
  processRowUpdate: PropTypes.func,
897
+ /**
898
+ * If `true`, the page is set to 0 after each sorting or filtering.
899
+ * This prop will be removed in the next major version and resetting the page will become the default behavior.
900
+ * @default false
901
+ */
902
+ resetPageOnSortFilter: PropTypes.bool,
897
903
  /**
898
904
  * The milliseconds throttle delay for resizing the grid.
899
905
  * @default 60
@@ -41,8 +41,8 @@ export const useDataGridPremiumComponent = (inputApiRef, props) => {
41
41
  useGridInitializeState(columnPinningStateInitializer, apiRef, props);
42
42
  useGridInitializeState(columnsStateInitializer, apiRef, props);
43
43
  useGridInitializeState(rowPinningStateInitializer, apiRef, props);
44
- useGridInitializeState(paginationStateInitializer, apiRef, props);
45
44
  useGridInitializeState(rowsStateInitializer, apiRef, props);
45
+ useGridInitializeState(paginationStateInitializer, apiRef, props);
46
46
  useGridInitializeState(editingStateInitializer, apiRef, props);
47
47
  useGridInitializeState(focusStateInitializer, apiRef, props);
48
48
  useGridInitializeState(sortingStateInitializer, apiRef, props);
@@ -255,7 +255,6 @@ export const useGridClipboardImport = (apiRef, props) => {
255
255
  const onProcessRowUpdateError = props.onProcessRowUpdateError;
256
256
  const getRowId = props.getRowId;
257
257
  const enableClipboardPaste = !props.disableClipboardPaste;
258
- const rootEl = apiRef.current.rootElementRef?.current;
259
258
  const logger = useGridLogger(apiRef, 'useGridClipboardImport');
260
259
  const splitClipboardPastedText = props.splitClipboardPastedText;
261
260
  const {
@@ -278,6 +277,7 @@ export const useGridClipboardImport = (apiRef, props) => {
278
277
  return;
279
278
  }
280
279
  }
280
+ const rootEl = apiRef.current.rootElementRef?.current;
281
281
  if (!rootEl) {
282
282
  return;
283
283
  }
@@ -318,7 +318,7 @@ export const useGridClipboardImport = (apiRef, props) => {
318
318
  paginationMode
319
319
  });
320
320
  cellUpdater.applyUpdates();
321
- }, [apiRef, processRowUpdate, onProcessRowUpdateError, getRowId, enableClipboardPaste, rootEl, splitClipboardPastedText, pagination, paginationMode, onBeforeClipboardPasteStart, logger]);
321
+ }, [apiRef, processRowUpdate, onProcessRowUpdateError, getRowId, enableClipboardPaste, splitClipboardPastedText, pagination, paginationMode, onBeforeClipboardPasteStart, logger]);
322
322
  const checkIfCanStartEditing = React.useCallback((initialValue, {
323
323
  event
324
324
  }) => {
@@ -1,2 +1,2 @@
1
1
  export * from "./gridExcelExportInterface.js";
2
- export { setupExcelExportWebWorker } from "./serializer/excelSerializer.js";
2
+ export { setupExcelExportWebWorker } from "./serializer/setupExcelExportWebWorker.js";
@@ -2,27 +2,32 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { GRID_DATE_COL_DEF, GRID_DATETIME_COL_DEF } from '@mui/x-data-grid-pro';
3
3
  import { isObject, isSingleSelectColDef, gridHasColSpanSelector } from '@mui/x-data-grid/internals';
4
4
  import { warnOnce } from '@mui/x-internals/warning';
5
- const getExcelJs = async () => {
6
- const excelJsModule = await import('exceljs');
7
- return excelJsModule.default ?? excelJsModule;
8
- };
9
- const getFormattedValueOptions = (colDef, row, valueOptions, api) => {
5
+ import { addColumnGroupingHeaders, addSerializedRowToWorksheet, createValueOptionsSheetIfNeeded, getExcelJs } from "./utils.js";
6
+ const getFormattedValueOptions = (colDef, row, valueOptions, api, callback) => {
10
7
  if (!colDef.valueOptions) {
11
- return [];
8
+ return;
12
9
  }
13
- let valueOptionsFormatted = valueOptions;
14
- if (colDef.valueFormatter) {
15
- valueOptionsFormatted = valueOptionsFormatted.map(option => {
10
+ const valueFormatter = colDef.valueFormatter;
11
+ for (let i = 0; i < valueOptions.length; i += 1) {
12
+ const option = valueOptions[i];
13
+ let value;
14
+ if (valueFormatter) {
16
15
  if (typeof option === 'object') {
17
- return option;
16
+ value = option.label;
17
+ } else {
18
+ value = String(colDef.valueFormatter(option, row, colDef, {
19
+ current: api
20
+ }));
18
21
  }
19
- return String(colDef.valueFormatter(option, row, colDef, {
20
- current: api
21
- }));
22
- });
22
+ } else {
23
+ value = typeof option === 'object' ? option.label : option;
24
+ }
25
+ callback(value, i);
23
26
  }
24
- return valueOptionsFormatted.map(option => typeof option === 'object' ? option.label : option);
25
27
  };
28
+ const commaRegex = /,/g;
29
+ const commaReplacement = 'CHAR(44)';
30
+
26
31
  /**
27
32
  * FIXME: This function mutates the colspan info, but colspan info assumes that the columns
28
33
  * passed to it are always consistent. In this case, the exported columns may differ from the
@@ -30,11 +35,15 @@ const getFormattedValueOptions = (colDef, row, valueOptions, api) => {
30
35
  * The caller of this function MUST call `resetColSpan()` before and after usage.
31
36
  */
32
37
  export const serializeRowUnsafe = (id, columns, apiRef, defaultValueOptionsFormulae, options) => {
33
- const row = {};
38
+ const serializedRow = {};
34
39
  const dataValidation = {};
35
40
  const mergedCells = [];
36
- const firstCellParams = apiRef.current.getCellParams(id, columns[0].field);
37
- const outlineLevel = firstCellParams.rowNode.depth;
41
+ const row = apiRef.current.getRow(id);
42
+ const rowNode = apiRef.current.getRowNode(id);
43
+ if (!row || !rowNode) {
44
+ throw new Error(`No row with id #${id} found`);
45
+ }
46
+ const outlineLevel = rowNode.depth;
38
47
  const hasColSpan = gridHasColSpanSelector(apiRef);
39
48
  if (hasColSpan) {
40
49
  // `colSpan` is only calculated for rendered rows, so we need to calculate it during export for every row
@@ -56,25 +65,32 @@ export const serializeRowUnsafe = (id, columns, apiRef, defaultValueOptionsFormu
56
65
  rightIndex: colIndex + colSpanInfo.cellProps.colSpan
57
66
  });
58
67
  }
59
- const cellParams = apiRef.current.getCellParams(id, column.field);
60
68
  let cellValue;
61
- switch (cellParams.colDef.type) {
69
+ switch (column.type) {
62
70
  case 'singleSelect':
63
71
  {
64
- const castColumn = cellParams.colDef;
72
+ const castColumn = column;
65
73
  if (typeof castColumn.valueOptions === 'function') {
66
74
  // If value option depends on the row, set specific options to the cell
67
75
  // This dataValidation is buggy with LibreOffice and does not allow to have coma
68
76
  const valueOptions = castColumn.valueOptions({
69
77
  id,
70
78
  row,
71
- field: cellParams.field
79
+ field: column.field
80
+ });
81
+ let formulae = '"';
82
+ getFormattedValueOptions(castColumn, row, valueOptions, apiRef.current, (value, index) => {
83
+ const formatted = value.toString().replace(commaRegex, commaReplacement);
84
+ formulae += formatted;
85
+ if (index < valueOptions.length - 1) {
86
+ formulae += ',';
87
+ }
72
88
  });
73
- const formattedValueOptions = getFormattedValueOptions(castColumn, row, valueOptions, apiRef.current);
89
+ formulae += '"';
74
90
  dataValidation[castColumn.field] = {
75
91
  type: 'list',
76
92
  allowBlank: true,
77
- formulae: [`"${formattedValueOptions.map(x => x.toString().replaceAll(',', 'CHAR(44)')).join(',')}"`]
93
+ formulae: [formulae]
78
94
  };
79
95
  } else {
80
96
  const address = defaultValueOptionsFormulae[column.field].address;
@@ -86,22 +102,22 @@ export const serializeRowUnsafe = (id, columns, apiRef, defaultValueOptionsFormu
86
102
  formulae: [address]
87
103
  };
88
104
  }
89
- const formattedValue = apiRef.current.getCellParams(id, castColumn.field).formattedValue;
105
+ const formattedValue = apiRef.current.getRowFormattedValue(row, castColumn);
90
106
  if (process.env.NODE_ENV !== 'production') {
91
- if (String(cellParams.formattedValue) === '[object Object]') {
107
+ if (String(formattedValue) === '[object Object]') {
92
108
  warnOnce(['MUI X: When the value of a field is an object or a `renderCell` is provided, the Excel export might not display the value correctly.', 'You can provide a `valueFormatter` with a string representation to be used.']);
93
109
  }
94
110
  }
95
111
  if (isObject(formattedValue)) {
96
- row[castColumn.field] = formattedValue?.label;
112
+ serializedRow[castColumn.field] = formattedValue?.label;
97
113
  } else {
98
- row[castColumn.field] = formattedValue;
114
+ serializedRow[castColumn.field] = formattedValue;
99
115
  }
100
116
  break;
101
117
  }
102
118
  case 'boolean':
103
119
  case 'number':
104
- cellValue = apiRef.current.getCellParams(id, column.field).value;
120
+ cellValue = apiRef.current.getRowValue(row, column);
105
121
  break;
106
122
  case 'date':
107
123
  case 'dateTime':
@@ -109,21 +125,21 @@ export const serializeRowUnsafe = (id, columns, apiRef, defaultValueOptionsFormu
109
125
  // Excel does not do any timezone conversion, so we create a date using UTC instead of local timezone
110
126
  // Solution from: https://github.com/exceljs/exceljs/issues/486#issuecomment-432557582
111
127
  // About Date.UTC(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC#exemples
112
- const value = apiRef.current.getCellParams(id, column.field).value;
128
+ const value = apiRef.current.getRowValue(row, column);
113
129
  // value may be `undefined` in auto-generated grouping rows
114
130
  if (!value) {
115
131
  break;
116
132
  }
117
133
  const utcDate = new Date(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds()));
118
- row[column.field] = utcDate;
134
+ serializedRow[column.field] = utcDate;
119
135
  break;
120
136
  }
121
137
  case 'actions':
122
138
  break;
123
139
  default:
124
- cellValue = apiRef.current.getCellParams(id, column.field).formattedValue;
140
+ cellValue = apiRef.current.getRowFormattedValue(row, column);
125
141
  if (process.env.NODE_ENV !== 'production') {
126
- if (String(cellParams.formattedValue) === '[object Object]') {
142
+ if (String(cellValue) === '[object Object]') {
127
143
  warnOnce(['MUI X: When the value of a field is an object or a `renderCell` is provided, the Excel export might not display the value correctly.', 'You can provide a `valueFormatter` with a string representation to be used.']);
128
144
  }
129
145
  }
@@ -136,11 +152,11 @@ export const serializeRowUnsafe = (id, columns, apiRef, defaultValueOptionsFormu
136
152
  }
137
153
  }
138
154
  if (typeof cellValue !== 'undefined') {
139
- row[column.field] = cellValue;
155
+ serializedRow[column.field] = cellValue;
140
156
  }
141
157
  });
142
158
  return {
143
- row,
159
+ row: serializedRow,
144
160
  dataValidation,
145
161
  outlineLevel,
146
162
  mergedCells
@@ -169,120 +185,39 @@ export const serializeColumn = (column, columnsStyles) => {
169
185
  style: _extends({}, type && defaultColumnsStyles?.[type], columnsStyles?.[field])
170
186
  };
171
187
  };
172
- const addColumnGroupingHeaders = (worksheet, columns, columnGroupPaths, columnGroupDetails) => {
173
- const maxDepth = Math.max(...columns.map(({
174
- key
175
- }) => columnGroupPaths[key]?.length ?? 0));
176
- if (maxDepth === 0) {
177
- return;
178
- }
179
- for (let rowIndex = 0; rowIndex < maxDepth; rowIndex += 1) {
180
- const row = columns.map(({
181
- key
182
- }) => {
183
- const groupingPath = columnGroupPaths[key];
184
- if (groupingPath.length <= rowIndex) {
185
- return {
186
- groupId: null,
187
- parents: groupingPath
188
- };
189
- }
190
- return _extends({}, columnGroupDetails[groupingPath[rowIndex]], {
191
- parents: groupingPath.slice(0, rowIndex)
192
- });
193
- });
194
- const newRow = worksheet.addRow(row.map(group => group.groupId === null ? null : group?.headerName ?? group.groupId));
195
-
196
- // use `rowCount`, since worksheet can have additional rows added in `exceljsPreProcess`
197
- const lastRowIndex = newRow.worksheet.rowCount;
198
- let leftIndex = 0;
199
- let rightIndex = 1;
200
- while (rightIndex < columns.length) {
201
- const {
202
- groupId: leftGroupId,
203
- parents: leftParents
204
- } = row[leftIndex];
205
- const {
206
- groupId: rightGroupId,
207
- parents: rightParents
208
- } = row[rightIndex];
209
- const areInSameGroup = leftGroupId === rightGroupId && leftParents.length === rightParents.length && leftParents.every((leftParent, index) => rightParents[index] === leftParent);
210
- if (areInSameGroup) {
211
- rightIndex += 1;
212
- } else {
213
- if (rightIndex - leftIndex > 1) {
214
- worksheet.mergeCells(lastRowIndex, leftIndex + 1, lastRowIndex, rightIndex);
215
- }
216
- leftIndex = rightIndex;
217
- rightIndex += 1;
218
- }
219
- }
220
- if (rightIndex - leftIndex > 1) {
221
- worksheet.mergeCells(lastRowIndex, leftIndex + 1, lastRowIndex, rightIndex);
222
- }
223
- }
224
- };
225
188
  export function serializeColumns(columns, styles) {
226
189
  return columns.map(column => serializeColumn(column, styles));
227
190
  }
228
191
  export async function getDataForValueOptionsSheet(columns, valueOptionsSheetName, api) {
229
- const candidateColumns = columns.filter(column => isSingleSelectColDef(column) && Array.isArray(column.valueOptions));
230
-
231
192
  // Creates a temp worksheet to obtain the column letters
232
193
  const excelJS = await getExcelJs();
233
194
  const workbook = new excelJS.Workbook();
234
195
  const worksheet = workbook.addWorksheet('Sheet1');
235
- worksheet.columns = candidateColumns.map(column => ({
236
- key: column.field
237
- }));
238
- return candidateColumns.reduce((acc, column) => {
239
- const singleSelectColumn = column;
240
- const formattedValueOptions = getFormattedValueOptions(singleSelectColumn, {}, singleSelectColumn.valueOptions, api);
196
+ const record = {};
197
+ const worksheetColumns = [];
198
+ for (let i = 0; i < columns.length; i += 1) {
199
+ const column = columns[i];
200
+ const isCandidateColumn = isSingleSelectColDef(column) && Array.isArray(column.valueOptions);
201
+ if (!isCandidateColumn) {
202
+ continue;
203
+ }
204
+ worksheetColumns.push({
205
+ key: column.field
206
+ });
207
+ worksheet.columns = worksheetColumns;
241
208
  const header = column.headerName ?? column.field;
242
- const values = [header, ...formattedValueOptions];
209
+ const values = [header];
210
+ getFormattedValueOptions(column, {}, column.valueOptions, api, value => {
211
+ values.push(value);
212
+ });
243
213
  const letter = worksheet.getColumn(column.field).letter;
244
214
  const address = `${valueOptionsSheetName}!$${letter}$2:$${letter}$${values.length}`;
245
- acc[column.field] = {
215
+ record[column.field] = {
246
216
  values,
247
217
  address
248
218
  };
249
- return acc;
250
- }, {});
251
- }
252
- function addSerializedRowToWorksheet(serializedRow, worksheet) {
253
- const {
254
- row,
255
- dataValidation,
256
- outlineLevel,
257
- mergedCells
258
- } = serializedRow;
259
- const newRow = worksheet.addRow(row);
260
- Object.keys(dataValidation).forEach(field => {
261
- newRow.getCell(field).dataValidation = _extends({}, dataValidation[field]);
262
- });
263
- if (outlineLevel) {
264
- newRow.outlineLevel = outlineLevel;
265
219
  }
266
-
267
- // use `rowCount`, since worksheet can have additional rows added in `exceljsPreProcess`
268
- const lastRowIndex = newRow.worksheet.rowCount;
269
- mergedCells.forEach(mergedCell => {
270
- worksheet.mergeCells(lastRowIndex, mergedCell.leftIndex, lastRowIndex, mergedCell.rightIndex);
271
- });
272
- }
273
- async function createValueOptionsSheetIfNeeded(valueOptionsData, sheetName, workbook) {
274
- if (Object.keys(valueOptionsData).length === 0) {
275
- return;
276
- }
277
- const valueOptionsWorksheet = workbook.addWorksheet(sheetName);
278
- valueOptionsWorksheet.columns = Object.keys(valueOptionsData).map(key => ({
279
- key
280
- }));
281
- Object.entries(valueOptionsData).forEach(([field, {
282
- values
283
- }]) => {
284
- valueOptionsWorksheet.getColumn(field).values = values;
285
- });
220
+ return record;
286
221
  }
287
222
  export async function buildExcel(options, apiRef) {
288
223
  const {
@@ -331,50 +266,4 @@ export async function buildExcel(options, apiRef) {
331
266
  });
332
267
  }
333
268
  return workbook;
334
- }
335
- export function setupExcelExportWebWorker(workerOptions = {}) {
336
- // eslint-disable-next-line no-restricted-globals
337
- addEventListener('message', async event => {
338
- const {
339
- serializedColumns,
340
- serializedRows,
341
- options,
342
- valueOptionsSheetName,
343
- valueOptionsData,
344
- columnGroupDetails,
345
- columnGroupPaths
346
- } = event.data;
347
- const {
348
- exceljsPostProcess,
349
- exceljsPreProcess
350
- } = workerOptions;
351
- const excelJS = await getExcelJs();
352
- const workbook = new excelJS.Workbook();
353
- const worksheet = workbook.addWorksheet('Sheet1');
354
- worksheet.columns = serializedColumns;
355
- if (exceljsPreProcess) {
356
- await exceljsPreProcess({
357
- workbook,
358
- worksheet
359
- });
360
- }
361
- if (options.includeColumnGroupsHeaders) {
362
- addColumnGroupingHeaders(worksheet, serializedColumns, columnGroupPaths, columnGroupDetails);
363
- }
364
- const includeHeaders = options.includeHeaders ?? true;
365
- if (includeHeaders) {
366
- worksheet.addRow(serializedColumns.map(column => column.headerText));
367
- }
368
- createValueOptionsSheetIfNeeded(valueOptionsData, valueOptionsSheetName, workbook);
369
- serializedRows.forEach(serializedRow => {
370
- addSerializedRowToWorksheet(serializedRow, worksheet);
371
- });
372
- if (exceljsPostProcess) {
373
- await exceljsPostProcess({
374
- workbook,
375
- worksheet
376
- });
377
- }
378
- postMessage(await workbook.xlsx.writeBuffer());
379
- });
380
269
  }
@@ -0,0 +1,53 @@
1
+ import { addColumnGroupingHeaders, addSerializedRowToWorksheet, createValueOptionsSheetIfNeeded, getExcelJs } from "./utils.js";
2
+ export function setupExcelExportWebWorker(workerOptions = {}) {
3
+ // eslint-disable-next-line no-restricted-globals
4
+ addEventListener('message', async event => {
5
+ const {
6
+ namespace,
7
+ serializedColumns,
8
+ serializedRows,
9
+ options,
10
+ valueOptionsSheetName,
11
+ valueOptionsData,
12
+ columnGroupDetails,
13
+ columnGroupPaths
14
+ } = event.data;
15
+
16
+ // workers share the pub-sub channel namespace. Use this property to filter out messages.
17
+ if (namespace !== 'mui-x-data-grid-export') {
18
+ return;
19
+ }
20
+ const {
21
+ exceljsPostProcess,
22
+ exceljsPreProcess
23
+ } = workerOptions;
24
+ const excelJS = await getExcelJs();
25
+ const workbook = new excelJS.Workbook();
26
+ const worksheet = workbook.addWorksheet('Sheet1');
27
+ worksheet.columns = serializedColumns;
28
+ if (exceljsPreProcess) {
29
+ await exceljsPreProcess({
30
+ workbook,
31
+ worksheet
32
+ });
33
+ }
34
+ if (options.includeColumnGroupsHeaders) {
35
+ addColumnGroupingHeaders(worksheet, serializedColumns, columnGroupPaths, columnGroupDetails);
36
+ }
37
+ const includeHeaders = options.includeHeaders ?? true;
38
+ if (includeHeaders) {
39
+ worksheet.addRow(serializedColumns.map(column => column.headerText));
40
+ }
41
+ createValueOptionsSheetIfNeeded(valueOptionsData, valueOptionsSheetName, workbook);
42
+ serializedRows.forEach(serializedRow => {
43
+ addSerializedRowToWorksheet(serializedRow, worksheet);
44
+ });
45
+ if (exceljsPostProcess) {
46
+ await exceljsPostProcess({
47
+ workbook,
48
+ worksheet
49
+ });
50
+ }
51
+ postMessage(await workbook.xlsx.writeBuffer());
52
+ });
53
+ }
@@ -0,0 +1,93 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ export const getExcelJs = async () => {
3
+ const excelJsModule = await import('exceljs');
4
+ return excelJsModule.default ?? excelJsModule;
5
+ };
6
+ export const addColumnGroupingHeaders = (worksheet, columns, columnGroupPaths, columnGroupDetails) => {
7
+ const maxDepth = Math.max(...columns.map(({
8
+ key
9
+ }) => columnGroupPaths[key]?.length ?? 0));
10
+ if (maxDepth === 0) {
11
+ return;
12
+ }
13
+ for (let rowIndex = 0; rowIndex < maxDepth; rowIndex += 1) {
14
+ const row = columns.map(({
15
+ key
16
+ }) => {
17
+ const groupingPath = columnGroupPaths[key];
18
+ if (groupingPath.length <= rowIndex) {
19
+ return {
20
+ groupId: null,
21
+ parents: groupingPath
22
+ };
23
+ }
24
+ return _extends({}, columnGroupDetails[groupingPath[rowIndex]], {
25
+ parents: groupingPath.slice(0, rowIndex)
26
+ });
27
+ });
28
+ const newRow = worksheet.addRow(row.map(group => group.groupId === null ? null : group?.headerName ?? group.groupId));
29
+
30
+ // use `rowCount`, since worksheet can have additional rows added in `exceljsPreProcess`
31
+ const lastRowIndex = newRow.worksheet.rowCount;
32
+ let leftIndex = 0;
33
+ let rightIndex = 1;
34
+ while (rightIndex < columns.length) {
35
+ const {
36
+ groupId: leftGroupId,
37
+ parents: leftParents
38
+ } = row[leftIndex];
39
+ const {
40
+ groupId: rightGroupId,
41
+ parents: rightParents
42
+ } = row[rightIndex];
43
+ const areInSameGroup = leftGroupId === rightGroupId && leftParents.length === rightParents.length && leftParents.every((leftParent, index) => rightParents[index] === leftParent);
44
+ if (areInSameGroup) {
45
+ rightIndex += 1;
46
+ } else {
47
+ if (rightIndex - leftIndex > 1) {
48
+ worksheet.mergeCells(lastRowIndex, leftIndex + 1, lastRowIndex, rightIndex);
49
+ }
50
+ leftIndex = rightIndex;
51
+ rightIndex += 1;
52
+ }
53
+ }
54
+ if (rightIndex - leftIndex > 1) {
55
+ worksheet.mergeCells(lastRowIndex, leftIndex + 1, lastRowIndex, rightIndex);
56
+ }
57
+ }
58
+ };
59
+ export function addSerializedRowToWorksheet(serializedRow, worksheet) {
60
+ const {
61
+ row,
62
+ dataValidation,
63
+ outlineLevel,
64
+ mergedCells
65
+ } = serializedRow;
66
+ const newRow = worksheet.addRow(row);
67
+ Object.keys(dataValidation).forEach(field => {
68
+ newRow.getCell(field).dataValidation = _extends({}, dataValidation[field]);
69
+ });
70
+ if (outlineLevel) {
71
+ newRow.outlineLevel = outlineLevel;
72
+ }
73
+
74
+ // use `rowCount`, since worksheet can have additional rows added in `exceljsPreProcess`
75
+ const lastRowIndex = newRow.worksheet.rowCount;
76
+ mergedCells.forEach(mergedCell => {
77
+ worksheet.mergeCells(lastRowIndex, mergedCell.leftIndex, lastRowIndex, mergedCell.rightIndex);
78
+ });
79
+ }
80
+ export async function createValueOptionsSheetIfNeeded(valueOptionsData, sheetName, workbook) {
81
+ if (Object.keys(valueOptionsData).length === 0) {
82
+ return;
83
+ }
84
+ const valueOptionsWorksheet = workbook.addWorksheet(sheetName);
85
+ valueOptionsWorksheet.columns = Object.keys(valueOptionsData).map(key => ({
86
+ key
87
+ }));
88
+ Object.entries(valueOptionsData).forEach(([field, {
89
+ values
90
+ }]) => {
91
+ valueOptionsWorksheet.getColumn(field).values = values;
92
+ });
93
+ }