@mui/x-data-grid-premium 6.3.1 → 6.4.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.
- package/CHANGELOG.md +57 -0
- package/DataGridPremium/DataGridPremium.js +41 -1
- package/DataGridPremium/useDataGridPremiumComponent.js +4 -2
- package/DataGridPremium/useDataGridPremiumProps.js +3 -1
- package/hooks/features/cellSelection/useGridCellSelection.d.ts +1 -1
- package/hooks/features/cellSelection/useGridCellSelection.js +29 -1
- package/hooks/features/clipboard/useGridClipboardImport.d.ts +4 -0
- package/hooks/features/clipboard/useGridClipboardImport.js +315 -0
- package/index.js +1 -1
- package/legacy/DataGridPremium/DataGridPremium.js +41 -1
- package/legacy/DataGridPremium/useDataGridPremiumComponent.js +4 -2
- package/legacy/DataGridPremium/useDataGridPremiumProps.js +6 -0
- package/legacy/hooks/features/cellSelection/useGridCellSelection.js +30 -1
- package/legacy/hooks/features/clipboard/useGridClipboardImport.js +395 -0
- package/legacy/index.js +1 -1
- package/legacy/utils/releaseInfo.js +1 -1
- package/models/dataGridPremiumProps.d.ts +30 -6
- package/modern/DataGridPremium/DataGridPremium.js +41 -1
- package/modern/DataGridPremium/useDataGridPremiumComponent.js +4 -2
- package/modern/DataGridPremium/useDataGridPremiumProps.js +3 -1
- package/modern/hooks/features/cellSelection/useGridCellSelection.js +29 -1
- package/modern/hooks/features/clipboard/useGridClipboardImport.js +313 -0
- package/modern/index.js +1 -1
- package/modern/utils/releaseInfo.js +1 -1
- package/node/DataGridPremium/DataGridPremium.js +41 -1
- package/node/DataGridPremium/useDataGridPremiumComponent.js +4 -2
- package/node/DataGridPremium/useDataGridPremiumProps.js +3 -1
- package/node/hooks/features/cellSelection/useGridCellSelection.js +28 -0
- package/node/hooks/features/clipboard/useGridClipboardImport.js +323 -0
- package/node/index.js +1 -1
- package/node/utils/releaseInfo.js +1 -1
- package/package.json +3 -3
- package/typeOverloads/modules.d.ts +26 -2
- package/utils/releaseInfo.js +1 -1
|
@@ -6,6 +6,7 @@ import { useGridRowGrouping, rowGroupingStateInitializer } from '../hooks/featur
|
|
|
6
6
|
import { useGridRowGroupingPreProcessors } from '../hooks/features/rowGrouping/useGridRowGroupingPreProcessors';
|
|
7
7
|
import { useGridExcelExport } from '../hooks/features/export/useGridExcelExport';
|
|
8
8
|
import { cellSelectionStateInitializer, useGridCellSelection } from '../hooks/features/cellSelection/useGridCellSelection';
|
|
9
|
+
import { useGridClipboardImport } from '../hooks/features/clipboard/useGridClipboardImport';
|
|
9
10
|
export const useDataGridPremiumComponent = (inputApiRef, props) => {
|
|
10
11
|
const privateApiRef = useGridInitialization(inputApiRef, props);
|
|
11
12
|
|
|
@@ -63,6 +64,7 @@ export const useDataGridPremiumComponent = (inputApiRef, props) => {
|
|
|
63
64
|
useGridDetailPanel(privateApiRef, props);
|
|
64
65
|
useGridColumnSpanning(privateApiRef);
|
|
65
66
|
useGridColumnGrouping(privateApiRef, props);
|
|
67
|
+
useGridClipboardImport(privateApiRef, props);
|
|
66
68
|
useGridEditing(privateApiRef, props);
|
|
67
69
|
useGridFocus(privateApiRef, props);
|
|
68
70
|
useGridPreferencesPanel(privateApiRef, props);
|
|
@@ -78,10 +80,10 @@ export const useDataGridPremiumComponent = (inputApiRef, props) => {
|
|
|
78
80
|
useGridInfiniteLoader(privateApiRef, props);
|
|
79
81
|
useGridLazyLoader(privateApiRef, props);
|
|
80
82
|
useGridColumnMenu(privateApiRef);
|
|
81
|
-
useGridCsvExport(privateApiRef);
|
|
83
|
+
useGridCsvExport(privateApiRef, props);
|
|
82
84
|
useGridPrintExport(privateApiRef, props);
|
|
83
85
|
useGridExcelExport(privateApiRef, props);
|
|
84
|
-
useGridClipboard(privateApiRef);
|
|
86
|
+
useGridClipboard(privateApiRef, props);
|
|
85
87
|
useGridDimensions(privateApiRef, props);
|
|
86
88
|
useGridEvents(privateApiRef, props);
|
|
87
89
|
useGridStatePersistence(privateApiRef);
|
|
@@ -18,7 +18,9 @@ export const DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES = _extends({}, DATA_GRID_PRO
|
|
|
18
18
|
rowGroupingColumnMode: 'single',
|
|
19
19
|
aggregationFunctions: GRID_AGGREGATION_FUNCTIONS,
|
|
20
20
|
aggregationRowsScope: 'filtered',
|
|
21
|
-
getAggregationPosition: groupNode => groupNode.depth === -1 ? 'footer' : 'inline'
|
|
21
|
+
getAggregationPosition: groupNode => groupNode.depth === -1 ? 'footer' : 'inline',
|
|
22
|
+
disableClipboardPaste: false,
|
|
23
|
+
unstable_splitClipboardPastedText: text => text.split(/\r\n|\n|\r/).map(row => row.split('\t'))
|
|
22
24
|
});
|
|
23
25
|
const defaultSlots = uncapitalizeObjectKeys(DATA_GRID_PREMIUM_DEFAULT_SLOTS_COMPONENTS);
|
|
24
26
|
export const useDataGridPremiumProps = inProps => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { useEventCallback } from '@mui/material/utils';
|
|
4
|
-
import { isNavigationKey, useGridRegisterPipeProcessor, useGridVisibleRows } from '@mui/x-data-grid-pro/internals';
|
|
4
|
+
import { isNavigationKey, serializeCellValue, useGridRegisterPipeProcessor, useGridVisibleRows } from '@mui/x-data-grid-pro/internals';
|
|
5
5
|
import { useGridApiEventHandler, useGridApiMethod, GRID_ACTIONS_COLUMN_TYPE, GRID_CHECKBOX_SELECTION_COL_DEF, GRID_DETAIL_PANEL_TOGGLE_FIELD, gridRowsDataRowIdToIdLookupSelector, gridClasses, gridFocusCellSelector } from '@mui/x-data-grid-pro';
|
|
6
6
|
import { gridCellSelectionStateSelector } from './gridCellSelectionSelector';
|
|
7
7
|
export const cellSelectionStateInitializer = (state, props) => _extends({}, state, {
|
|
@@ -14,6 +14,9 @@ export const useGridCellSelection = (apiRef, props) => {
|
|
|
14
14
|
const visibleRows = useGridVisibleRows(apiRef, props);
|
|
15
15
|
const cellWithVirtualFocus = React.useRef();
|
|
16
16
|
const lastMouseDownCell = React.useRef();
|
|
17
|
+
const ignoreValueFormatterProp = props.unstable_ignoreValueFormatterDuringExport;
|
|
18
|
+
const ignoreValueFormatter = (typeof ignoreValueFormatterProp === 'object' ? ignoreValueFormatterProp?.clipboardExport : ignoreValueFormatterProp) || false;
|
|
19
|
+
const clipboardCopyCellDelimiter = props.clipboardCopyCellDelimiter;
|
|
17
20
|
apiRef.current.registerControlState({
|
|
18
21
|
stateId: 'cellSelection',
|
|
19
22
|
propModel: props.unstable_cellSelectionModel,
|
|
@@ -312,7 +315,32 @@ export const useGridCellSelection = (apiRef, props) => {
|
|
|
312
315
|
}
|
|
313
316
|
return initialValue;
|
|
314
317
|
}, [apiRef, props.unstable_cellSelection, hasClickedValidCellForRangeSelection]);
|
|
318
|
+
const handleClipboardCopy = React.useCallback(value => {
|
|
319
|
+
if (apiRef.current.unstable_getSelectedCellsAsArray().length <= 1) {
|
|
320
|
+
return value;
|
|
321
|
+
}
|
|
322
|
+
const cellSelectionModel = apiRef.current.unstable_getCellSelectionModel();
|
|
323
|
+
const copyData = Object.keys(cellSelectionModel).reduce((acc, rowId) => {
|
|
324
|
+
const fieldsMap = cellSelectionModel[rowId];
|
|
325
|
+
const rowString = Object.keys(fieldsMap).reduce((acc2, field) => {
|
|
326
|
+
let cellData;
|
|
327
|
+
if (fieldsMap[field]) {
|
|
328
|
+
const cellParams = apiRef.current.getCellParams(rowId, field);
|
|
329
|
+
cellData = serializeCellValue(cellParams, {
|
|
330
|
+
delimiterCharacter: clipboardCopyCellDelimiter,
|
|
331
|
+
ignoreValueFormatter
|
|
332
|
+
});
|
|
333
|
+
} else {
|
|
334
|
+
cellData = '';
|
|
335
|
+
}
|
|
336
|
+
return acc2 === '' ? cellData : [acc2, cellData].join(clipboardCopyCellDelimiter);
|
|
337
|
+
}, '');
|
|
338
|
+
return acc === '' ? rowString : [acc, rowString].join('\r\n');
|
|
339
|
+
}, '');
|
|
340
|
+
return copyData;
|
|
341
|
+
}, [apiRef, ignoreValueFormatter, clipboardCopyCellDelimiter]);
|
|
315
342
|
useGridRegisterPipeProcessor(apiRef, 'isCellSelected', checkIfCellIsSelected);
|
|
316
343
|
useGridRegisterPipeProcessor(apiRef, 'cellClassName', addClassesToCells);
|
|
317
344
|
useGridRegisterPipeProcessor(apiRef, 'canUpdateFocus', canUpdateFocus);
|
|
345
|
+
useGridRegisterPipeProcessor(apiRef, 'clipboardCopy', handleClipboardCopy);
|
|
318
346
|
};
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { GRID_CHECKBOX_SELECTION_FIELD, gridFocusCellSelector, gridVisibleColumnFieldsSelector, useGridApiOptionHandler, useGridApiEventHandler, gridPaginatedVisibleSortedGridRowIdsSelector } from '@mui/x-data-grid';
|
|
4
|
+
import { buildWarning, getRowIdFromRowModel, getActiveElement, useGridRegisterPipeProcessor } from '@mui/x-data-grid/internals';
|
|
5
|
+
import { GRID_DETAIL_PANEL_TOGGLE_FIELD, GRID_REORDER_COL_DEF } from '@mui/x-data-grid-pro';
|
|
6
|
+
import { unstable_debounce as debounce } from '@mui/utils';
|
|
7
|
+
const missingOnProcessRowUpdateErrorWarning = buildWarning(['MUI: A call to `processRowUpdate` threw an error which was not handled because `onProcessRowUpdateError` is missing.', 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, e.g. `<DataGrid onProcessRowUpdateError={(error) => ...} />`.', 'For more detail, see http://mui.com/components/data-grid/editing/#persistence.'], 'error');
|
|
8
|
+
const columnFieldsToExcludeFromPaste = [GRID_CHECKBOX_SELECTION_FIELD, GRID_REORDER_COL_DEF.field, GRID_DETAIL_PANEL_TOGGLE_FIELD];
|
|
9
|
+
|
|
10
|
+
// Batches rows that are updated during clipboard paste to reduce `updateRows` calls
|
|
11
|
+
function batchRowUpdates(func, wait) {
|
|
12
|
+
let rows = [];
|
|
13
|
+
const debounced = debounce(() => {
|
|
14
|
+
func(rows);
|
|
15
|
+
rows = [];
|
|
16
|
+
}, wait);
|
|
17
|
+
return row => {
|
|
18
|
+
rows.push(row);
|
|
19
|
+
debounced();
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function getTextFromClipboard(rootEl) {
|
|
23
|
+
return new Promise(resolve => {
|
|
24
|
+
const focusedCell = getActiveElement(document);
|
|
25
|
+
const el = document.createElement('input');
|
|
26
|
+
el.style.width = '0px';
|
|
27
|
+
el.style.height = '0px';
|
|
28
|
+
el.style.border = 'none';
|
|
29
|
+
el.style.margin = '0';
|
|
30
|
+
el.style.padding = '0';
|
|
31
|
+
el.style.outline = 'none';
|
|
32
|
+
el.style.position = 'absolute';
|
|
33
|
+
el.style.top = '0';
|
|
34
|
+
el.style.left = '0';
|
|
35
|
+
const handlePasteEvent = event => {
|
|
36
|
+
el.removeEventListener('paste', handlePasteEvent);
|
|
37
|
+
const text = event.clipboardData?.getData('text/plain');
|
|
38
|
+
if (focusedCell instanceof HTMLElement) {
|
|
39
|
+
focusedCell.focus({
|
|
40
|
+
preventScroll: true
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
el.remove();
|
|
44
|
+
resolve(text || '');
|
|
45
|
+
};
|
|
46
|
+
el.addEventListener('paste', handlePasteEvent);
|
|
47
|
+
rootEl.appendChild(el);
|
|
48
|
+
el.focus({
|
|
49
|
+
preventScroll: true
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Keeps track of updated rows during clipboard paste
|
|
55
|
+
class CellValueUpdater {
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.rowsToUpdate = {};
|
|
58
|
+
this.updateRow = void 0;
|
|
59
|
+
this.options = void 0;
|
|
60
|
+
this.options = options;
|
|
61
|
+
this.updateRow = batchRowUpdates(options.apiRef.current.updateRows, 50);
|
|
62
|
+
}
|
|
63
|
+
updateCell({
|
|
64
|
+
rowId,
|
|
65
|
+
field,
|
|
66
|
+
pastedCellValue
|
|
67
|
+
}) {
|
|
68
|
+
if (pastedCellValue === undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const {
|
|
72
|
+
apiRef,
|
|
73
|
+
getRowId
|
|
74
|
+
} = this.options;
|
|
75
|
+
const colDef = apiRef.current.getColumn(field);
|
|
76
|
+
if (!colDef || !colDef.editable) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const row = this.rowsToUpdate[rowId] || _extends({}, apiRef.current.getRow(rowId));
|
|
80
|
+
if (!row) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const cellParams = apiRef.current.getCellParams(rowId, field);
|
|
84
|
+
let parsedValue = pastedCellValue;
|
|
85
|
+
if (colDef.pastedValueParser) {
|
|
86
|
+
parsedValue = colDef.pastedValueParser(pastedCellValue, cellParams);
|
|
87
|
+
} else if (colDef.valueParser) {
|
|
88
|
+
parsedValue = colDef.valueParser(parsedValue, cellParams);
|
|
89
|
+
}
|
|
90
|
+
if (parsedValue === undefined) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let rowCopy = _extends({}, row);
|
|
94
|
+
if (typeof colDef.valueSetter === 'function') {
|
|
95
|
+
rowCopy = colDef.valueSetter({
|
|
96
|
+
value: parsedValue,
|
|
97
|
+
row: rowCopy
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
rowCopy[field] = parsedValue;
|
|
101
|
+
}
|
|
102
|
+
const newRowId = getRowIdFromRowModel(rowCopy, getRowId);
|
|
103
|
+
if (String(newRowId) !== String(rowId)) {
|
|
104
|
+
// We cannot update row id, so this cell value update should be ignored
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.rowsToUpdate[rowId] = rowCopy;
|
|
108
|
+
}
|
|
109
|
+
applyUpdates() {
|
|
110
|
+
const {
|
|
111
|
+
apiRef,
|
|
112
|
+
processRowUpdate,
|
|
113
|
+
onProcessRowUpdateError
|
|
114
|
+
} = this.options;
|
|
115
|
+
const rowsToUpdate = this.rowsToUpdate;
|
|
116
|
+
const rowIdsToUpdate = Object.keys(rowsToUpdate);
|
|
117
|
+
if (rowIdsToUpdate.length === 0) {
|
|
118
|
+
apiRef.current.publishEvent('clipboardPasteEnd');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const handleRowUpdate = async rowId => {
|
|
122
|
+
const newRow = rowsToUpdate[rowId];
|
|
123
|
+
if (typeof processRowUpdate === 'function') {
|
|
124
|
+
const handleError = errorThrown => {
|
|
125
|
+
if (onProcessRowUpdateError) {
|
|
126
|
+
onProcessRowUpdateError(errorThrown);
|
|
127
|
+
} else {
|
|
128
|
+
missingOnProcessRowUpdateErrorWarning();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
const oldRow = apiRef.current.getRow(rowId);
|
|
133
|
+
const finalRowUpdate = await processRowUpdate(newRow, oldRow);
|
|
134
|
+
this.updateRow(finalRowUpdate);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
handleError(error);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
this.updateRow(newRow);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const promises = rowIdsToUpdate.map(rowId => {
|
|
143
|
+
// Wrap in promise that always resolves to avoid Promise.all from stopping on first error.
|
|
144
|
+
// This is to avoid using `Promise.allSettled` that has worse browser support.
|
|
145
|
+
return new Promise(resolve => {
|
|
146
|
+
handleRowUpdate(rowId).then(resolve).catch(resolve);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
Promise.all(promises).then(() => {
|
|
150
|
+
this.rowsToUpdate = {};
|
|
151
|
+
apiRef.current.publishEvent('clipboardPasteEnd');
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function defaultPasteResolver({
|
|
156
|
+
pastedData,
|
|
157
|
+
apiRef,
|
|
158
|
+
updateCell
|
|
159
|
+
}) {
|
|
160
|
+
const isSingleValuePasted = pastedData.length === 1 && pastedData[0].length === 1;
|
|
161
|
+
const cellSelectionModel = apiRef.current.unstable_getCellSelectionModel();
|
|
162
|
+
if (cellSelectionModel && apiRef.current.unstable_getSelectedCellsAsArray().length > 1) {
|
|
163
|
+
Object.keys(cellSelectionModel).forEach((rowId, rowIndex) => {
|
|
164
|
+
const rowDataArr = pastedData[isSingleValuePasted ? 0 : rowIndex];
|
|
165
|
+
const hasRowData = isSingleValuePasted ? true : rowDataArr !== undefined;
|
|
166
|
+
if (!hasRowData) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
Object.keys(cellSelectionModel[rowId]).forEach((field, colIndex) => {
|
|
170
|
+
const cellValue = isSingleValuePasted ? rowDataArr[0] : rowDataArr[colIndex];
|
|
171
|
+
updateCell({
|
|
172
|
+
rowId,
|
|
173
|
+
field,
|
|
174
|
+
pastedCellValue: cellValue
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef).filter(field => {
|
|
181
|
+
if (columnFieldsToExcludeFromPaste.includes(field)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
});
|
|
186
|
+
const selectedRows = apiRef.current.getSelectedRows();
|
|
187
|
+
if (selectedRows.size > 0 && !isSingleValuePasted) {
|
|
188
|
+
// Multiple values are pasted starting from the first and top-most cell
|
|
189
|
+
const pastedRowsDataCount = pastedData.length;
|
|
190
|
+
|
|
191
|
+
// There's no guarantee that the selected rows are in the same order as the pasted rows
|
|
192
|
+
selectedRows.forEach((row, rowId) => {
|
|
193
|
+
let rowData;
|
|
194
|
+
if (pastedRowsDataCount === 1) {
|
|
195
|
+
// If only one row is pasted - paste it to all selected rows
|
|
196
|
+
rowData = pastedData[0];
|
|
197
|
+
} else {
|
|
198
|
+
rowData = pastedData.shift();
|
|
199
|
+
}
|
|
200
|
+
if (rowData === undefined) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
rowData.forEach((newCellValue, cellIndex) => {
|
|
204
|
+
updateCell({
|
|
205
|
+
rowId,
|
|
206
|
+
field: visibleColumnFields[cellIndex],
|
|
207
|
+
pastedCellValue: newCellValue
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const selectedCell = gridFocusCellSelector(apiRef);
|
|
214
|
+
if (!selectedCell) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (columnFieldsToExcludeFromPaste.includes(selectedCell.field)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const selectedRowId = selectedCell.id;
|
|
221
|
+
const selectedRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(selectedRowId);
|
|
222
|
+
const visibleRowIds = gridPaginatedVisibleSortedGridRowIdsSelector(apiRef);
|
|
223
|
+
const selectedFieldIndex = visibleColumnFields.indexOf(selectedCell.field);
|
|
224
|
+
pastedData.forEach((rowData, index) => {
|
|
225
|
+
const rowId = visibleRowIds[selectedRowIndex + index];
|
|
226
|
+
if (typeof rowId === 'undefined') {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
for (let i = selectedFieldIndex; i < visibleColumnFields.length; i += 1) {
|
|
230
|
+
const field = visibleColumnFields[i];
|
|
231
|
+
const stringValue = rowData[i - selectedFieldIndex];
|
|
232
|
+
updateCell({
|
|
233
|
+
rowId,
|
|
234
|
+
field,
|
|
235
|
+
pastedCellValue: stringValue
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function isPasteShortcut(event) {
|
|
241
|
+
const isModifierKeyPressed = event.ctrlKey || event.metaKey || event.altKey;
|
|
242
|
+
if (event.code === 'KeyV' && isModifierKeyPressed) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
export const useGridClipboardImport = (apiRef, props) => {
|
|
248
|
+
const processRowUpdate = props.processRowUpdate;
|
|
249
|
+
const onProcessRowUpdateError = props.onProcessRowUpdateError;
|
|
250
|
+
const getRowId = props.getRowId;
|
|
251
|
+
const enableClipboardPaste = (!props.disableClipboardPaste && props.experimentalFeatures?.clipboardPaste) ?? false;
|
|
252
|
+
const rootEl = apiRef.current.rootElementRef?.current;
|
|
253
|
+
const splitClipboardPastedText = props.unstable_splitClipboardPastedText;
|
|
254
|
+
const handlePaste = React.useCallback(async (params, event) => {
|
|
255
|
+
if (!enableClipboardPaste) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (!isPasteShortcut(event)) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const focusedCell = gridFocusCellSelector(apiRef);
|
|
262
|
+
if (focusedCell !== null) {
|
|
263
|
+
const cellMode = apiRef.current.getCellMode(focusedCell.id, focusedCell.field);
|
|
264
|
+
if (cellMode === 'edit') {
|
|
265
|
+
// Do not paste data when the cell is in edit mode
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!rootEl) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const text = await getTextFromClipboard(rootEl);
|
|
273
|
+
if (!text) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const pastedData = splitClipboardPastedText(text);
|
|
277
|
+
if (!pastedData) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const cellUpdater = new CellValueUpdater({
|
|
281
|
+
apiRef,
|
|
282
|
+
processRowUpdate,
|
|
283
|
+
onProcessRowUpdateError,
|
|
284
|
+
getRowId
|
|
285
|
+
});
|
|
286
|
+
apiRef.current.publishEvent('clipboardPasteStart', {
|
|
287
|
+
data: pastedData
|
|
288
|
+
});
|
|
289
|
+
defaultPasteResolver({
|
|
290
|
+
pastedData,
|
|
291
|
+
apiRef: {
|
|
292
|
+
current: apiRef.current.getPublicApi()
|
|
293
|
+
},
|
|
294
|
+
updateCell: (...args) => {
|
|
295
|
+
cellUpdater.updateCell(...args);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
cellUpdater.applyUpdates();
|
|
299
|
+
}, [apiRef, processRowUpdate, onProcessRowUpdateError, getRowId, enableClipboardPaste, rootEl, splitClipboardPastedText]);
|
|
300
|
+
const checkIfCanStartEditing = React.useCallback((initialValue, {
|
|
301
|
+
event
|
|
302
|
+
}) => {
|
|
303
|
+
if (isPasteShortcut(event) && enableClipboardPaste) {
|
|
304
|
+
// Do not enter cell edit mode on paste
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return initialValue;
|
|
308
|
+
}, [enableClipboardPaste]);
|
|
309
|
+
useGridApiEventHandler(apiRef, 'cellKeyDown', handlePaste);
|
|
310
|
+
useGridApiOptionHandler(apiRef, 'clipboardPasteStart', props.onClipboardPasteStart);
|
|
311
|
+
useGridApiOptionHandler(apiRef, 'clipboardPasteEnd', props.onClipboardPasteEnd);
|
|
312
|
+
useGridRegisterPipeProcessor(apiRef, 'canStartEditing', checkIfCanStartEditing);
|
|
313
|
+
};
|
package/modern/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ponyfillGlobal } from '@mui/utils';
|
|
2
2
|
export const getReleaseInfo = () => {
|
|
3
|
-
const releaseInfo = "
|
|
3
|
+
const releaseInfo = "MTY4Mzg0MjQwMDAwMA==";
|
|
4
4
|
if (process.env.NODE_ENV !== 'production') {
|
|
5
5
|
// A simple hack to set the value in the test environment (has no build step).
|
|
6
6
|
// eslint-disable-next-line no-useless-concat
|
|
@@ -115,6 +115,11 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
|
|
|
115
115
|
* Override or extend the styles applied to the component.
|
|
116
116
|
*/
|
|
117
117
|
classes: _propTypes.default.object,
|
|
118
|
+
/**
|
|
119
|
+
* The character used to separate cell values when copying to the clipboard.
|
|
120
|
+
* @default '\t'
|
|
121
|
+
*/
|
|
122
|
+
clipboardCopyCellDelimiter: _propTypes.default.string,
|
|
118
123
|
/**
|
|
119
124
|
* Number of extra columns to be rendered before/after the visible slice.
|
|
120
125
|
* @default 3
|
|
@@ -180,6 +185,11 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
|
|
|
180
185
|
* @default false
|
|
181
186
|
*/
|
|
182
187
|
disableChildrenSorting: _propTypes.default.bool,
|
|
188
|
+
/**
|
|
189
|
+
* If `true`, the clipboard paste is disabled.
|
|
190
|
+
* @default false
|
|
191
|
+
*/
|
|
192
|
+
disableClipboardPaste: _propTypes.default.bool,
|
|
183
193
|
/**
|
|
184
194
|
* If `true`, column filters are disabled.
|
|
185
195
|
* @default false
|
|
@@ -255,6 +265,7 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
|
|
|
255
265
|
* For each feature, if the flag is not explicitly set to `true`, then the feature is fully disabled, and neither property nor method calls will have any effect.
|
|
256
266
|
*/
|
|
257
267
|
experimentalFeatures: _propTypes.default.shape({
|
|
268
|
+
clipboardPaste: _propTypes.default.bool,
|
|
258
269
|
columnGrouping: _propTypes.default.bool,
|
|
259
270
|
lazyLoading: _propTypes.default.bool,
|
|
260
271
|
warnIfFocusStateIsNotSynced: _propTypes.default.bool
|
|
@@ -490,6 +501,19 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
|
|
|
490
501
|
* @param {GridCallbackDetails} details Additional details for this callback.
|
|
491
502
|
*/
|
|
492
503
|
onCellModesModelChange: _propTypes.default.func,
|
|
504
|
+
/**
|
|
505
|
+
* Callback called when the data is copied to the clipboard.
|
|
506
|
+
* @param {string} data The data copied to the clipboard.
|
|
507
|
+
*/
|
|
508
|
+
onClipboardCopy: _propTypes.default.func,
|
|
509
|
+
/**
|
|
510
|
+
* Callback fired when the clipboard paste operation ends.
|
|
511
|
+
*/
|
|
512
|
+
onClipboardPasteEnd: _propTypes.default.func,
|
|
513
|
+
/**
|
|
514
|
+
* Callback fired when the clipboard paste operation starts.
|
|
515
|
+
*/
|
|
516
|
+
onClipboardPasteStart: _propTypes.default.func,
|
|
493
517
|
/**
|
|
494
518
|
* Callback fired when a click event comes from a column header element.
|
|
495
519
|
* @param {GridColumnHeaderParams} params With all properties from [[GridColumnHeaderParams]].
|
|
@@ -894,10 +918,26 @@ process.env.NODE_ENV !== "production" ? DataGridPremiumRaw.propTypes = {
|
|
|
894
918
|
* Set the cell selection model of the grid.
|
|
895
919
|
*/
|
|
896
920
|
unstable_cellSelectionModel: _propTypes.default.object,
|
|
921
|
+
/**
|
|
922
|
+
* If `true`, the grid will not use `valueFormatter` when exporting to CSV or copying to clipboard.
|
|
923
|
+
* If an object is provided, you can choose to ignore the `valueFormatter` for CSV export or clipboard export.
|
|
924
|
+
* @default: false
|
|
925
|
+
*/
|
|
926
|
+
unstable_ignoreValueFormatterDuringExport: _propTypes.default.oneOfType([_propTypes.default.shape({
|
|
927
|
+
clipboardExport: _propTypes.default.bool,
|
|
928
|
+
csvExport: _propTypes.default.bool
|
|
929
|
+
}), _propTypes.default.bool]),
|
|
897
930
|
/**
|
|
898
931
|
* Callback fired when the selection state of one or multiple cells changes.
|
|
899
932
|
* @param {GridCellSelectionModel} cellSelectionModel Object in the shape of [[GridCellSelectionModel]] containing the selected cells.
|
|
900
933
|
* @param {GridCallbackDetails} details Additional details for this callback.
|
|
901
934
|
*/
|
|
902
|
-
unstable_onCellSelectionModelChange: _propTypes.default.func
|
|
935
|
+
unstable_onCellSelectionModelChange: _propTypes.default.func,
|
|
936
|
+
/**
|
|
937
|
+
* The function is used to split the pasted text into rows and cells.
|
|
938
|
+
* @param {string} text The text pasted from the clipboard.
|
|
939
|
+
* @returns {string[][] | null} A 2D array of strings. The first dimension is the rows, the second dimension is the columns.
|
|
940
|
+
* @default `(text) => text.split(/\r\n|\n|\r/).map((row) => row.split('\t'))`
|
|
941
|
+
*/
|
|
942
|
+
unstable_splitClipboardPastedText: _propTypes.default.func
|
|
903
943
|
} : void 0;
|
|
@@ -11,6 +11,7 @@ var _useGridRowGrouping = require("../hooks/features/rowGrouping/useGridRowGroup
|
|
|
11
11
|
var _useGridRowGroupingPreProcessors = require("../hooks/features/rowGrouping/useGridRowGroupingPreProcessors");
|
|
12
12
|
var _useGridExcelExport = require("../hooks/features/export/useGridExcelExport");
|
|
13
13
|
var _useGridCellSelection = require("../hooks/features/cellSelection/useGridCellSelection");
|
|
14
|
+
var _useGridClipboardImport = require("../hooks/features/clipboard/useGridClipboardImport");
|
|
14
15
|
// Premium-only features
|
|
15
16
|
|
|
16
17
|
const useDataGridPremiumComponent = (inputApiRef, props) => {
|
|
@@ -70,6 +71,7 @@ const useDataGridPremiumComponent = (inputApiRef, props) => {
|
|
|
70
71
|
(0, _internals.useGridDetailPanel)(privateApiRef, props);
|
|
71
72
|
(0, _internals.useGridColumnSpanning)(privateApiRef);
|
|
72
73
|
(0, _internals.useGridColumnGrouping)(privateApiRef, props);
|
|
74
|
+
(0, _useGridClipboardImport.useGridClipboardImport)(privateApiRef, props);
|
|
73
75
|
(0, _internals.useGridEditing)(privateApiRef, props);
|
|
74
76
|
(0, _internals.useGridFocus)(privateApiRef, props);
|
|
75
77
|
(0, _internals.useGridPreferencesPanel)(privateApiRef, props);
|
|
@@ -85,10 +87,10 @@ const useDataGridPremiumComponent = (inputApiRef, props) => {
|
|
|
85
87
|
(0, _internals.useGridInfiniteLoader)(privateApiRef, props);
|
|
86
88
|
(0, _internals.useGridLazyLoader)(privateApiRef, props);
|
|
87
89
|
(0, _internals.useGridColumnMenu)(privateApiRef);
|
|
88
|
-
(0, _internals.useGridCsvExport)(privateApiRef);
|
|
90
|
+
(0, _internals.useGridCsvExport)(privateApiRef, props);
|
|
89
91
|
(0, _internals.useGridPrintExport)(privateApiRef, props);
|
|
90
92
|
(0, _useGridExcelExport.useGridExcelExport)(privateApiRef, props);
|
|
91
|
-
(0, _internals.useGridClipboard)(privateApiRef);
|
|
93
|
+
(0, _internals.useGridClipboard)(privateApiRef, props);
|
|
92
94
|
(0, _internals.useGridDimensions)(privateApiRef, props);
|
|
93
95
|
(0, _internals.useGridEvents)(privateApiRef, props);
|
|
94
96
|
(0, _internals.useGridStatePersistence)(privateApiRef);
|
|
@@ -26,7 +26,9 @@ const DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES = (0, _extends2.default)({}, _xData
|
|
|
26
26
|
rowGroupingColumnMode: 'single',
|
|
27
27
|
aggregationFunctions: _aggregation.GRID_AGGREGATION_FUNCTIONS,
|
|
28
28
|
aggregationRowsScope: 'filtered',
|
|
29
|
-
getAggregationPosition: groupNode => groupNode.depth === -1 ? 'footer' : 'inline'
|
|
29
|
+
getAggregationPosition: groupNode => groupNode.depth === -1 ? 'footer' : 'inline',
|
|
30
|
+
disableClipboardPaste: false,
|
|
31
|
+
unstable_splitClipboardPastedText: text => text.split(/\r\n|\n|\r/).map(row => row.split('\t'))
|
|
30
32
|
});
|
|
31
33
|
exports.DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES = DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES;
|
|
32
34
|
const defaultSlots = (0, _internals.uncapitalizeObjectKeys)(_dataGridPremiumDefaultSlotsComponents.DATA_GRID_PREMIUM_DEFAULT_SLOTS_COMPONENTS);
|
|
@@ -24,6 +24,9 @@ const useGridCellSelection = (apiRef, props) => {
|
|
|
24
24
|
const visibleRows = (0, _internals.useGridVisibleRows)(apiRef, props);
|
|
25
25
|
const cellWithVirtualFocus = React.useRef();
|
|
26
26
|
const lastMouseDownCell = React.useRef();
|
|
27
|
+
const ignoreValueFormatterProp = props.unstable_ignoreValueFormatterDuringExport;
|
|
28
|
+
const ignoreValueFormatter = (typeof ignoreValueFormatterProp === 'object' ? ignoreValueFormatterProp?.clipboardExport : ignoreValueFormatterProp) || false;
|
|
29
|
+
const clipboardCopyCellDelimiter = props.clipboardCopyCellDelimiter;
|
|
27
30
|
apiRef.current.registerControlState({
|
|
28
31
|
stateId: 'cellSelection',
|
|
29
32
|
propModel: props.unstable_cellSelectionModel,
|
|
@@ -322,8 +325,33 @@ const useGridCellSelection = (apiRef, props) => {
|
|
|
322
325
|
}
|
|
323
326
|
return initialValue;
|
|
324
327
|
}, [apiRef, props.unstable_cellSelection, hasClickedValidCellForRangeSelection]);
|
|
328
|
+
const handleClipboardCopy = React.useCallback(value => {
|
|
329
|
+
if (apiRef.current.unstable_getSelectedCellsAsArray().length <= 1) {
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
const cellSelectionModel = apiRef.current.unstable_getCellSelectionModel();
|
|
333
|
+
const copyData = Object.keys(cellSelectionModel).reduce((acc, rowId) => {
|
|
334
|
+
const fieldsMap = cellSelectionModel[rowId];
|
|
335
|
+
const rowString = Object.keys(fieldsMap).reduce((acc2, field) => {
|
|
336
|
+
let cellData;
|
|
337
|
+
if (fieldsMap[field]) {
|
|
338
|
+
const cellParams = apiRef.current.getCellParams(rowId, field);
|
|
339
|
+
cellData = (0, _internals.serializeCellValue)(cellParams, {
|
|
340
|
+
delimiterCharacter: clipboardCopyCellDelimiter,
|
|
341
|
+
ignoreValueFormatter
|
|
342
|
+
});
|
|
343
|
+
} else {
|
|
344
|
+
cellData = '';
|
|
345
|
+
}
|
|
346
|
+
return acc2 === '' ? cellData : [acc2, cellData].join(clipboardCopyCellDelimiter);
|
|
347
|
+
}, '');
|
|
348
|
+
return acc === '' ? rowString : [acc, rowString].join('\r\n');
|
|
349
|
+
}, '');
|
|
350
|
+
return copyData;
|
|
351
|
+
}, [apiRef, ignoreValueFormatter, clipboardCopyCellDelimiter]);
|
|
325
352
|
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'isCellSelected', checkIfCellIsSelected);
|
|
326
353
|
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'cellClassName', addClassesToCells);
|
|
327
354
|
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'canUpdateFocus', canUpdateFocus);
|
|
355
|
+
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'clipboardCopy', handleClipboardCopy);
|
|
328
356
|
};
|
|
329
357
|
exports.useGridCellSelection = useGridCellSelection;
|