@mui/x-data-grid 7.22.1 → 7.22.2
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 +64 -0
- package/colDef/gridBooleanOperators.js +4 -6
- package/components/panel/filterPanel/GridFilterInputBoolean.d.ts +1 -0
- package/components/panel/filterPanel/GridFilterInputBoolean.js +14 -5
- package/components/virtualization/GridVirtualScrollbar.js +6 -0
- package/hooks/features/editing/useGridCellEditing.js +23 -4
- package/hooks/features/editing/useGridRowEditing.js +23 -2
- package/hooks/features/rowSelection/useGridRowSelection.d.ts +1 -1
- package/hooks/features/rowSelection/useGridRowSelection.js +30 -16
- package/index.js +1 -1
- package/modern/colDef/gridBooleanOperators.js +4 -6
- package/modern/components/panel/filterPanel/GridFilterInputBoolean.js +14 -5
- package/modern/components/virtualization/GridVirtualScrollbar.js +6 -0
- package/modern/hooks/features/editing/useGridCellEditing.js +23 -4
- package/modern/hooks/features/editing/useGridRowEditing.js +23 -2
- package/modern/hooks/features/rowSelection/useGridRowSelection.js +30 -16
- package/modern/index.js +1 -1
- package/node/colDef/gridBooleanOperators.js +3 -5
- package/node/components/panel/filterPanel/GridFilterInputBoolean.js +16 -5
- package/node/components/virtualization/GridVirtualScrollbar.js +6 -0
- package/node/hooks/features/editing/useGridCellEditing.js +23 -4
- package/node/hooks/features/editing/useGridRowEditing.js +23 -2
- package/node/hooks/features/rowSelection/useGridRowSelection.js +29 -15
- package/node/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,70 @@
|
|
|
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.22.2
|
|
7
|
+
|
|
8
|
+
_Nov 8, 2024_
|
|
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
|
+
- 👨🏽💻 API enhancements
|
|
13
|
+
- 🐞 Bugfixes
|
|
14
|
+
|
|
15
|
+
Special thanks go out to the community contributors who have helped make this release possible:
|
|
16
|
+
@clins1994, @GuillaumeMeheut, @k-rajat19.
|
|
17
|
+
Following are all team members who have contributed to this release:
|
|
18
|
+
@LukasTy, @MBilalShafi, @KenanYusuf, @arminmeh.
|
|
19
|
+
|
|
20
|
+
### Upcoming alpha
|
|
21
|
+
|
|
22
|
+
Keep an eye out for the MUI⠀X `v8.0.0-aplha.0` release soon. It will follow a weekly release schedule as always until it is stable.
|
|
23
|
+
|
|
24
|
+
<!--/ HIGHLIGHT_ABOVE_SEPARATOR /-->
|
|
25
|
+
|
|
26
|
+
### Data Grid
|
|
27
|
+
|
|
28
|
+
#### `@mui/x-data-grid@7.22.2`
|
|
29
|
+
|
|
30
|
+
- [DataGrid] Fix `null` reference error in `GridVirtualScrollbar` (#15289) @MBilalShafi
|
|
31
|
+
- [DataGrid] Fix filtering with `boolean` column type (#15257) @k-rajat19
|
|
32
|
+
- [DataGrid] Improve row selection propagation trigger (#15274) @MBilalShafi
|
|
33
|
+
- [DataGrid] Preprocess edit cell props on backspace/delete (#15223) @KenanYusuf
|
|
34
|
+
- [DataGrid] Add a recipe to persist column width and order (#15309) @MBilalShafi
|
|
35
|
+
|
|
36
|
+
#### `@mui/x-data-grid-pro@7.22.2` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
37
|
+
|
|
38
|
+
Same changes as in `@mui/x-data-grid@7.22.2`, plus:
|
|
39
|
+
|
|
40
|
+
- [DataGridPro] Apply default properties if they are not passed in a reorder column (#15320) @k-rajat19
|
|
41
|
+
- [DataGridPro] Toggle row expansion with `Enter` key in Tree data (#15313) @k-rajat19
|
|
42
|
+
|
|
43
|
+
#### `@mui/x-data-grid-premium@7.22.2` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
44
|
+
|
|
45
|
+
Same changes as in `@mui/x-data-grid-pro@7.22.2`, plus:
|
|
46
|
+
|
|
47
|
+
- [DataGridPremium] Fix incorrect rows selection count when selection propagation is enabled with row grouping (#15222) @arminmeh
|
|
48
|
+
|
|
49
|
+
### Date and Time Pickers
|
|
50
|
+
|
|
51
|
+
#### `@mui/x-date-pickers@7.22.2`
|
|
52
|
+
|
|
53
|
+
- [pickers] Add support for `moment-hijri@3.0.0` (#15248) @LukasTy
|
|
54
|
+
|
|
55
|
+
#### `@mui/x-date-pickers-pro@7.22.2` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
56
|
+
|
|
57
|
+
Same changes as in `@mui/x-date-pickers@7.22.2`.
|
|
58
|
+
|
|
59
|
+
### Charts
|
|
60
|
+
|
|
61
|
+
#### `@mui/x-charts@7.22.2`
|
|
62
|
+
|
|
63
|
+
- [charts] Allow `SeriesValueFormatter` to return `null` value (#15295) @clins1994
|
|
64
|
+
- [charts] Allow configuring the `domainLimit` for each axis. (#15325) @GuillaumeMeheut
|
|
65
|
+
|
|
66
|
+
#### `@mui/x-charts-pro@7.0.0-beta.7` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
67
|
+
|
|
68
|
+
Same changes as in `@mui/x-charts@7.22.2`.
|
|
69
|
+
|
|
6
70
|
## 7.22.1
|
|
7
71
|
|
|
8
72
|
_Nov 1, 2024_
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { GridFilterInputBoolean } from "../components/panel/filterPanel/GridFilterInputBoolean.js";
|
|
1
|
+
import { GridFilterInputBoolean, sanitizeFilterItemValue } from "../components/panel/filterPanel/GridFilterInputBoolean.js";
|
|
2
2
|
export const getGridBooleanOperators = () => [{
|
|
3
3
|
value: 'is',
|
|
4
4
|
getApplyFilterFn: filterItem => {
|
|
5
|
-
|
|
5
|
+
const sanitizedValue = sanitizeFilterItemValue(filterItem.value);
|
|
6
|
+
if (sanitizedValue === undefined) {
|
|
6
7
|
return null;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
return value => {
|
|
10
|
-
return Boolean(value) === valueAsBoolean;
|
|
11
|
-
};
|
|
9
|
+
return value => Boolean(value) === sanitizedValue;
|
|
12
10
|
},
|
|
13
11
|
InputComponent: GridFilterInputBoolean
|
|
14
12
|
}];
|
|
@@ -9,6 +9,7 @@ export type GridFilterInputBooleanProps = GridFilterInputValueProps & TextFieldP
|
|
|
9
9
|
*/
|
|
10
10
|
isFilterActive?: boolean;
|
|
11
11
|
};
|
|
12
|
+
export declare const sanitizeFilterItemValue: (value: any) => boolean | undefined;
|
|
12
13
|
declare function GridFilterInputBoolean(props: GridFilterInputBooleanProps): React.JSX.Element;
|
|
13
14
|
declare namespace GridFilterInputBoolean {
|
|
14
15
|
var propTypes: any;
|
|
@@ -7,6 +7,15 @@ import { refType, unstable_useId as useId } from '@mui/utils';
|
|
|
7
7
|
import { styled } from '@mui/material/styles';
|
|
8
8
|
import { useGridRootProps } from "../../../hooks/utils/useGridRootProps.js";
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
export const sanitizeFilterItemValue = value => {
|
|
11
|
+
if (String(value).toLowerCase() === 'true') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (String(value).toLowerCase() === 'false') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
};
|
|
10
19
|
const BooleanOperatorContainer = styled('div')({
|
|
11
20
|
display: 'flex',
|
|
12
21
|
alignItems: 'center',
|
|
@@ -27,7 +36,7 @@ function GridFilterInputBoolean(props) {
|
|
|
27
36
|
variant = 'standard'
|
|
28
37
|
} = props,
|
|
29
38
|
others = _objectWithoutPropertiesLoose(props, _excluded);
|
|
30
|
-
const [filterValueState, setFilterValueState] = React.useState(item.value
|
|
39
|
+
const [filterValueState, setFilterValueState] = React.useState(sanitizeFilterItemValue(item.value));
|
|
31
40
|
const rootProps = useGridRootProps();
|
|
32
41
|
const labelId = useId();
|
|
33
42
|
const selectId = useId();
|
|
@@ -35,14 +44,14 @@ function GridFilterInputBoolean(props) {
|
|
|
35
44
|
const isSelectNative = baseSelectProps.native ?? false;
|
|
36
45
|
const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
|
|
37
46
|
const onFilterChange = React.useCallback(event => {
|
|
38
|
-
const value = event.target.value;
|
|
47
|
+
const value = sanitizeFilterItemValue(event.target.value);
|
|
39
48
|
setFilterValueState(value);
|
|
40
49
|
applyValue(_extends({}, item, {
|
|
41
|
-
value
|
|
50
|
+
value
|
|
42
51
|
}));
|
|
43
52
|
}, [applyValue, item]);
|
|
44
53
|
React.useEffect(() => {
|
|
45
|
-
setFilterValueState(item.value
|
|
54
|
+
setFilterValueState(sanitizeFilterItemValue(item.value));
|
|
46
55
|
}, [item.value]);
|
|
47
56
|
const label = labelProp ?? apiRef.current.getLocaleText('filterPanelInputLabel');
|
|
48
57
|
return /*#__PURE__*/_jsxs(BooleanOperatorContainer, {
|
|
@@ -57,7 +66,7 @@ function GridFilterInputBoolean(props) {
|
|
|
57
66
|
labelId: labelId,
|
|
58
67
|
id: selectId,
|
|
59
68
|
label: label,
|
|
60
|
-
value: filterValueState,
|
|
69
|
+
value: filterValueState === undefined ? '' : String(filterValueState),
|
|
61
70
|
onChange: onFilterChange,
|
|
62
71
|
variant: variant,
|
|
63
72
|
notched: variant === 'outlined' ? true : undefined,
|
|
@@ -70,6 +70,9 @@ const GridVirtualScrollbar = /*#__PURE__*/React.forwardRef(function GridVirtualS
|
|
|
70
70
|
const onScrollerScroll = useEventCallback(() => {
|
|
71
71
|
const scroller = apiRef.current.virtualScrollerRef.current;
|
|
72
72
|
const scrollbar = scrollbarRef.current;
|
|
73
|
+
if (!scrollbar) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
73
76
|
if (scroller[propertyScroll] === lastPosition.current) {
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
@@ -85,6 +88,9 @@ const GridVirtualScrollbar = /*#__PURE__*/React.forwardRef(function GridVirtualS
|
|
|
85
88
|
const onScrollbarScroll = useEventCallback(() => {
|
|
86
89
|
const scroller = apiRef.current.virtualScrollerRef.current;
|
|
87
90
|
const scrollbar = scrollbarRef.current;
|
|
91
|
+
if (!scrollbar) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
88
94
|
if (isLocked.current) {
|
|
89
95
|
isLocked.current = false;
|
|
90
96
|
return;
|
|
@@ -241,26 +241,45 @@ export const useGridCellEditing = (apiRef, props) => {
|
|
|
241
241
|
mode: GridCellModes.Edit
|
|
242
242
|
}, other));
|
|
243
243
|
}, [throwIfNotEditable, throwIfNotInMode, updateFieldInCellModesModel]);
|
|
244
|
-
const updateStateToStartCellEditMode = useEventCallback(params => {
|
|
244
|
+
const updateStateToStartCellEditMode = useEventCallback(async params => {
|
|
245
245
|
const {
|
|
246
246
|
id,
|
|
247
247
|
field,
|
|
248
248
|
deleteValue,
|
|
249
249
|
initialValue
|
|
250
250
|
} = params;
|
|
251
|
-
|
|
251
|
+
const value = apiRef.current.getCellValue(id, field);
|
|
252
|
+
let newValue = value;
|
|
252
253
|
if (deleteValue) {
|
|
253
254
|
newValue = getDefaultCellValue(apiRef.current.getColumn(field));
|
|
254
255
|
} else if (initialValue) {
|
|
255
256
|
newValue = initialValue;
|
|
256
257
|
}
|
|
257
|
-
const
|
|
258
|
+
const column = apiRef.current.getColumn(field);
|
|
259
|
+
const shouldProcessEditCellProps = !!column.preProcessEditCellProps && deleteValue;
|
|
260
|
+
let newProps = {
|
|
258
261
|
value: newValue,
|
|
259
262
|
error: false,
|
|
260
|
-
isProcessingProps:
|
|
263
|
+
isProcessingProps: shouldProcessEditCellProps
|
|
261
264
|
};
|
|
262
265
|
updateOrDeleteFieldState(id, field, newProps);
|
|
263
266
|
apiRef.current.setCellFocus(id, field);
|
|
267
|
+
if (shouldProcessEditCellProps) {
|
|
268
|
+
newProps = await Promise.resolve(column.preProcessEditCellProps({
|
|
269
|
+
id,
|
|
270
|
+
row: apiRef.current.getRow(id),
|
|
271
|
+
props: newProps,
|
|
272
|
+
hasChanged: newValue !== value
|
|
273
|
+
}));
|
|
274
|
+
// Check if still in edit mode before updating
|
|
275
|
+
if (apiRef.current.getCellMode(id, field) === GridCellModes.Edit) {
|
|
276
|
+
const editingState = gridEditRowsStateSelector(apiRef.current.state);
|
|
277
|
+
updateOrDeleteFieldState(id, field, _extends({}, newProps, {
|
|
278
|
+
value: editingState[id][field].value,
|
|
279
|
+
isProcessingProps: false
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
264
283
|
});
|
|
265
284
|
const stopCellEditMode = React.useCallback(params => {
|
|
266
285
|
const {
|
|
@@ -318,10 +318,11 @@ export const useGridRowEditing = (apiRef, props) => {
|
|
|
318
318
|
if (!cellParams.isEditable) {
|
|
319
319
|
return acc;
|
|
320
320
|
}
|
|
321
|
+
const column = apiRef.current.getColumn(field);
|
|
321
322
|
let newValue = apiRef.current.getCellValue(id, field);
|
|
322
323
|
if (fieldToFocus === field && (deleteValue || initialValue)) {
|
|
323
324
|
if (deleteValue) {
|
|
324
|
-
newValue = getDefaultCellValue(
|
|
325
|
+
newValue = getDefaultCellValue(column);
|
|
325
326
|
} else if (initialValue) {
|
|
326
327
|
newValue = initialValue;
|
|
327
328
|
}
|
|
@@ -329,7 +330,7 @@ export const useGridRowEditing = (apiRef, props) => {
|
|
|
329
330
|
acc[field] = {
|
|
330
331
|
value: newValue,
|
|
331
332
|
error: false,
|
|
332
|
-
isProcessingProps:
|
|
333
|
+
isProcessingProps: !!column.preProcessEditCellProps && deleteValue
|
|
333
334
|
};
|
|
334
335
|
return acc;
|
|
335
336
|
}, {});
|
|
@@ -337,6 +338,26 @@ export const useGridRowEditing = (apiRef, props) => {
|
|
|
337
338
|
if (fieldToFocus) {
|
|
338
339
|
apiRef.current.setCellFocus(id, fieldToFocus);
|
|
339
340
|
}
|
|
341
|
+
columnFields.filter(field => !!apiRef.current.getColumn(field).preProcessEditCellProps && deleteValue).forEach(field => {
|
|
342
|
+
const column = apiRef.current.getColumn(field);
|
|
343
|
+
const value = apiRef.current.getCellValue(id, field);
|
|
344
|
+
const newValue = deleteValue ? getDefaultCellValue(column) : initialValue ?? value;
|
|
345
|
+
Promise.resolve(column.preProcessEditCellProps({
|
|
346
|
+
id,
|
|
347
|
+
row: apiRef.current.getRow(id),
|
|
348
|
+
props: newProps[field],
|
|
349
|
+
hasChanged: newValue !== value
|
|
350
|
+
})).then(processedProps => {
|
|
351
|
+
// Check if still in edit mode before updating
|
|
352
|
+
if (apiRef.current.getRowMode(id) === GridRowModes.Edit) {
|
|
353
|
+
const editingState = gridEditRowsStateSelector(apiRef.current.state);
|
|
354
|
+
updateOrDeleteFieldState(id, field, _extends({}, processedProps, {
|
|
355
|
+
value: editingState[id][field].value,
|
|
356
|
+
isProcessingProps: false
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
340
361
|
});
|
|
341
362
|
const stopRowEditMode = React.useCallback(params => {
|
|
342
363
|
const {
|
|
@@ -9,4 +9,4 @@ export declare const rowSelectionStateInitializer: GridStateInitializer<Pick<Dat
|
|
|
9
9
|
* @requires useGridFocus (state) - can be after
|
|
10
10
|
* @requires useGridKeyboardNavigation (`cellKeyDown` event must first be consumed by it)
|
|
11
11
|
*/
|
|
12
|
-
export declare const useGridRowSelection: (apiRef: React.MutableRefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "checkboxSelection" | "rowSelectionModel" | "onRowSelectionModelChange" | "disableMultipleRowSelection" | "disableRowSelectionOnClick" | "isRowSelectable" | "checkboxSelectionVisibleOnly" | "pagination" | "paginationMode" | "classes" | "keepNonExistentRowsSelected" | "rowSelection" | "rowSelectionPropagation" | "signature">) => void;
|
|
12
|
+
export declare const useGridRowSelection: (apiRef: React.MutableRefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "checkboxSelection" | "rowSelectionModel" | "onRowSelectionModelChange" | "disableMultipleRowSelection" | "disableRowSelectionOnClick" | "isRowSelectable" | "checkboxSelectionVisibleOnly" | "pagination" | "paginationMode" | "filterMode" | "classes" | "keepNonExistentRowsSelected" | "rowSelection" | "rowSelectionPropagation" | "signature">) => void;
|
|
@@ -4,7 +4,7 @@ import { GridSignature, useGridApiEventHandler } from "../../utils/useGridApiEve
|
|
|
4
4
|
import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
|
|
5
5
|
import { useGridLogger } from "../../utils/useGridLogger.js";
|
|
6
6
|
import { useGridSelector } from "../../utils/useGridSelector.js";
|
|
7
|
-
import { gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
|
|
7
|
+
import { gridRowsLookupSelector, gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
|
|
8
8
|
import { gridRowSelectionStateSelector, selectedGridRowsSelector, selectedIdsLookupSelector } from "./gridRowSelectionSelector.js";
|
|
9
9
|
import { gridPaginatedVisibleSortedGridRowIdsSelector } from "../pagination/index.js";
|
|
10
10
|
import { gridFocusCellSelector } from "../focus/gridFocusStateSelector.js";
|
|
@@ -171,30 +171,33 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
171
171
|
let newSelection;
|
|
172
172
|
if (resetSelection) {
|
|
173
173
|
if (isSelected) {
|
|
174
|
-
newSelection = selectableIds;
|
|
174
|
+
newSelection = new Set(selectableIds);
|
|
175
175
|
if (applyAutoSelection) {
|
|
176
176
|
const addRow = rowId => {
|
|
177
|
-
newSelection.
|
|
177
|
+
newSelection.add(rowId);
|
|
178
178
|
};
|
|
179
179
|
selectableIds.forEach(id => {
|
|
180
180
|
findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
183
|
} else {
|
|
184
|
-
newSelection =
|
|
184
|
+
newSelection = new Set();
|
|
185
|
+
}
|
|
186
|
+
const currentLookup = selectedIdsLookupSelector(apiRef);
|
|
187
|
+
if (newSelection.size === Object.keys(currentLookup).length && Array.from(newSelection).every(id => currentLookup[id] === id)) {
|
|
188
|
+
return;
|
|
185
189
|
}
|
|
186
190
|
} else {
|
|
187
|
-
|
|
188
|
-
const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
|
|
191
|
+
newSelection = new Set(Object.values(selectedIdsLookupSelector(apiRef)));
|
|
189
192
|
const addRow = rowId => {
|
|
190
|
-
|
|
193
|
+
newSelection.add(rowId);
|
|
191
194
|
};
|
|
192
195
|
const removeRow = rowId => {
|
|
193
|
-
delete
|
|
196
|
+
newSelection.delete(rowId);
|
|
194
197
|
};
|
|
195
198
|
selectableIds.forEach(id => {
|
|
196
199
|
if (isSelected) {
|
|
197
|
-
|
|
200
|
+
newSelection.add(id);
|
|
198
201
|
if (applyAutoSelection) {
|
|
199
202
|
findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
|
|
200
203
|
}
|
|
@@ -205,11 +208,10 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
210
|
});
|
|
208
|
-
newSelection = Object.values(selectionLookup);
|
|
209
211
|
}
|
|
210
|
-
const isSelectionValid = newSelection.
|
|
212
|
+
const isSelectionValid = newSelection.size < 2 || canHaveMultipleSelection;
|
|
211
213
|
if (isSelectionValid) {
|
|
212
|
-
apiRef.current.setRowSelectionModel(newSelection);
|
|
214
|
+
apiRef.current.setRowSelectionModel(Array.from(newSelection));
|
|
213
215
|
}
|
|
214
216
|
}, [logger, applyAutoSelection, canHaveMultipleSelection, apiRef, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents]);
|
|
215
217
|
const selectRowRange = React.useCallback(({
|
|
@@ -252,13 +254,20 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
252
254
|
return;
|
|
253
255
|
}
|
|
254
256
|
const currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
|
|
257
|
+
const rowsLookup = gridRowsLookupSelector(apiRef);
|
|
255
258
|
const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
|
|
256
259
|
|
|
257
260
|
// We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
|
|
258
261
|
const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
|
|
262
|
+
const isNonExistent = id => {
|
|
263
|
+
if (props.filterMode === 'server') {
|
|
264
|
+
return !rowsLookup[id];
|
|
265
|
+
}
|
|
266
|
+
return filteredRowsLookup[id] !== true;
|
|
267
|
+
};
|
|
259
268
|
let hasChanged = false;
|
|
260
269
|
currentSelection.forEach(id => {
|
|
261
|
-
if (
|
|
270
|
+
if (isNonExistent(id)) {
|
|
262
271
|
if (props.keepNonExistentRowsSelected) {
|
|
263
272
|
return;
|
|
264
273
|
}
|
|
@@ -284,15 +293,20 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
284
293
|
}
|
|
285
294
|
}
|
|
286
295
|
});
|
|
287
|
-
|
|
296
|
+
|
|
297
|
+
// For nested data, on row tree updation (filtering, adding rows, etc.) when the selection is
|
|
298
|
+
// not empty, we need to re-run scanning of the tree to propagate the selection changes
|
|
299
|
+
// Example: A parent whose de-selected children are filtered out should now be selected
|
|
300
|
+
const shouldReapplyPropagation = isNestedData && props.rowSelectionPropagation?.parents && Object.keys(selectionLookup).length > 0;
|
|
301
|
+
if (hasChanged || shouldReapplyPropagation && !sortModelUpdated) {
|
|
288
302
|
const newSelection = Object.values(selectionLookup);
|
|
289
|
-
if (
|
|
303
|
+
if (shouldReapplyPropagation) {
|
|
290
304
|
apiRef.current.selectRows(newSelection, true, true);
|
|
291
305
|
} else {
|
|
292
306
|
apiRef.current.setRowSelectionModel(newSelection);
|
|
293
307
|
}
|
|
294
308
|
}
|
|
295
|
-
}, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, tree]);
|
|
309
|
+
}, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, props.filterMode, tree]);
|
|
296
310
|
const handleSingleRowSelection = React.useCallback((id, event) => {
|
|
297
311
|
const hasCtrlKey = event.metaKey || event.ctrlKey;
|
|
298
312
|
|
package/index.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { GridFilterInputBoolean } from "../components/panel/filterPanel/GridFilterInputBoolean.js";
|
|
1
|
+
import { GridFilterInputBoolean, sanitizeFilterItemValue } from "../components/panel/filterPanel/GridFilterInputBoolean.js";
|
|
2
2
|
export const getGridBooleanOperators = () => [{
|
|
3
3
|
value: 'is',
|
|
4
4
|
getApplyFilterFn: filterItem => {
|
|
5
|
-
|
|
5
|
+
const sanitizedValue = sanitizeFilterItemValue(filterItem.value);
|
|
6
|
+
if (sanitizedValue === undefined) {
|
|
6
7
|
return null;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
return value => {
|
|
10
|
-
return Boolean(value) === valueAsBoolean;
|
|
11
|
-
};
|
|
9
|
+
return value => Boolean(value) === sanitizedValue;
|
|
12
10
|
},
|
|
13
11
|
InputComponent: GridFilterInputBoolean
|
|
14
12
|
}];
|
|
@@ -7,6 +7,15 @@ import { refType, unstable_useId as useId } from '@mui/utils';
|
|
|
7
7
|
import { styled } from '@mui/material/styles';
|
|
8
8
|
import { useGridRootProps } from "../../../hooks/utils/useGridRootProps.js";
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
export const sanitizeFilterItemValue = value => {
|
|
11
|
+
if (String(value).toLowerCase() === 'true') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (String(value).toLowerCase() === 'false') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
};
|
|
10
19
|
const BooleanOperatorContainer = styled('div')({
|
|
11
20
|
display: 'flex',
|
|
12
21
|
alignItems: 'center',
|
|
@@ -27,7 +36,7 @@ function GridFilterInputBoolean(props) {
|
|
|
27
36
|
variant = 'standard'
|
|
28
37
|
} = props,
|
|
29
38
|
others = _objectWithoutPropertiesLoose(props, _excluded);
|
|
30
|
-
const [filterValueState, setFilterValueState] = React.useState(item.value
|
|
39
|
+
const [filterValueState, setFilterValueState] = React.useState(sanitizeFilterItemValue(item.value));
|
|
31
40
|
const rootProps = useGridRootProps();
|
|
32
41
|
const labelId = useId();
|
|
33
42
|
const selectId = useId();
|
|
@@ -35,14 +44,14 @@ function GridFilterInputBoolean(props) {
|
|
|
35
44
|
const isSelectNative = baseSelectProps.native ?? false;
|
|
36
45
|
const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
|
|
37
46
|
const onFilterChange = React.useCallback(event => {
|
|
38
|
-
const value = event.target.value;
|
|
47
|
+
const value = sanitizeFilterItemValue(event.target.value);
|
|
39
48
|
setFilterValueState(value);
|
|
40
49
|
applyValue(_extends({}, item, {
|
|
41
|
-
value
|
|
50
|
+
value
|
|
42
51
|
}));
|
|
43
52
|
}, [applyValue, item]);
|
|
44
53
|
React.useEffect(() => {
|
|
45
|
-
setFilterValueState(item.value
|
|
54
|
+
setFilterValueState(sanitizeFilterItemValue(item.value));
|
|
46
55
|
}, [item.value]);
|
|
47
56
|
const label = labelProp ?? apiRef.current.getLocaleText('filterPanelInputLabel');
|
|
48
57
|
return /*#__PURE__*/_jsxs(BooleanOperatorContainer, {
|
|
@@ -57,7 +66,7 @@ function GridFilterInputBoolean(props) {
|
|
|
57
66
|
labelId: labelId,
|
|
58
67
|
id: selectId,
|
|
59
68
|
label: label,
|
|
60
|
-
value: filterValueState,
|
|
69
|
+
value: filterValueState === undefined ? '' : String(filterValueState),
|
|
61
70
|
onChange: onFilterChange,
|
|
62
71
|
variant: variant,
|
|
63
72
|
notched: variant === 'outlined' ? true : undefined,
|
|
@@ -70,6 +70,9 @@ const GridVirtualScrollbar = /*#__PURE__*/React.forwardRef(function GridVirtualS
|
|
|
70
70
|
const onScrollerScroll = useEventCallback(() => {
|
|
71
71
|
const scroller = apiRef.current.virtualScrollerRef.current;
|
|
72
72
|
const scrollbar = scrollbarRef.current;
|
|
73
|
+
if (!scrollbar) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
73
76
|
if (scroller[propertyScroll] === lastPosition.current) {
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
@@ -85,6 +88,9 @@ const GridVirtualScrollbar = /*#__PURE__*/React.forwardRef(function GridVirtualS
|
|
|
85
88
|
const onScrollbarScroll = useEventCallback(() => {
|
|
86
89
|
const scroller = apiRef.current.virtualScrollerRef.current;
|
|
87
90
|
const scrollbar = scrollbarRef.current;
|
|
91
|
+
if (!scrollbar) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
88
94
|
if (isLocked.current) {
|
|
89
95
|
isLocked.current = false;
|
|
90
96
|
return;
|
|
@@ -241,26 +241,45 @@ export const useGridCellEditing = (apiRef, props) => {
|
|
|
241
241
|
mode: GridCellModes.Edit
|
|
242
242
|
}, other));
|
|
243
243
|
}, [throwIfNotEditable, throwIfNotInMode, updateFieldInCellModesModel]);
|
|
244
|
-
const updateStateToStartCellEditMode = useEventCallback(params => {
|
|
244
|
+
const updateStateToStartCellEditMode = useEventCallback(async params => {
|
|
245
245
|
const {
|
|
246
246
|
id,
|
|
247
247
|
field,
|
|
248
248
|
deleteValue,
|
|
249
249
|
initialValue
|
|
250
250
|
} = params;
|
|
251
|
-
|
|
251
|
+
const value = apiRef.current.getCellValue(id, field);
|
|
252
|
+
let newValue = value;
|
|
252
253
|
if (deleteValue) {
|
|
253
254
|
newValue = getDefaultCellValue(apiRef.current.getColumn(field));
|
|
254
255
|
} else if (initialValue) {
|
|
255
256
|
newValue = initialValue;
|
|
256
257
|
}
|
|
257
|
-
const
|
|
258
|
+
const column = apiRef.current.getColumn(field);
|
|
259
|
+
const shouldProcessEditCellProps = !!column.preProcessEditCellProps && deleteValue;
|
|
260
|
+
let newProps = {
|
|
258
261
|
value: newValue,
|
|
259
262
|
error: false,
|
|
260
|
-
isProcessingProps:
|
|
263
|
+
isProcessingProps: shouldProcessEditCellProps
|
|
261
264
|
};
|
|
262
265
|
updateOrDeleteFieldState(id, field, newProps);
|
|
263
266
|
apiRef.current.setCellFocus(id, field);
|
|
267
|
+
if (shouldProcessEditCellProps) {
|
|
268
|
+
newProps = await Promise.resolve(column.preProcessEditCellProps({
|
|
269
|
+
id,
|
|
270
|
+
row: apiRef.current.getRow(id),
|
|
271
|
+
props: newProps,
|
|
272
|
+
hasChanged: newValue !== value
|
|
273
|
+
}));
|
|
274
|
+
// Check if still in edit mode before updating
|
|
275
|
+
if (apiRef.current.getCellMode(id, field) === GridCellModes.Edit) {
|
|
276
|
+
const editingState = gridEditRowsStateSelector(apiRef.current.state);
|
|
277
|
+
updateOrDeleteFieldState(id, field, _extends({}, newProps, {
|
|
278
|
+
value: editingState[id][field].value,
|
|
279
|
+
isProcessingProps: false
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
264
283
|
});
|
|
265
284
|
const stopCellEditMode = React.useCallback(params => {
|
|
266
285
|
const {
|
|
@@ -318,10 +318,11 @@ export const useGridRowEditing = (apiRef, props) => {
|
|
|
318
318
|
if (!cellParams.isEditable) {
|
|
319
319
|
return acc;
|
|
320
320
|
}
|
|
321
|
+
const column = apiRef.current.getColumn(field);
|
|
321
322
|
let newValue = apiRef.current.getCellValue(id, field);
|
|
322
323
|
if (fieldToFocus === field && (deleteValue || initialValue)) {
|
|
323
324
|
if (deleteValue) {
|
|
324
|
-
newValue = getDefaultCellValue(
|
|
325
|
+
newValue = getDefaultCellValue(column);
|
|
325
326
|
} else if (initialValue) {
|
|
326
327
|
newValue = initialValue;
|
|
327
328
|
}
|
|
@@ -329,7 +330,7 @@ export const useGridRowEditing = (apiRef, props) => {
|
|
|
329
330
|
acc[field] = {
|
|
330
331
|
value: newValue,
|
|
331
332
|
error: false,
|
|
332
|
-
isProcessingProps:
|
|
333
|
+
isProcessingProps: !!column.preProcessEditCellProps && deleteValue
|
|
333
334
|
};
|
|
334
335
|
return acc;
|
|
335
336
|
}, {});
|
|
@@ -337,6 +338,26 @@ export const useGridRowEditing = (apiRef, props) => {
|
|
|
337
338
|
if (fieldToFocus) {
|
|
338
339
|
apiRef.current.setCellFocus(id, fieldToFocus);
|
|
339
340
|
}
|
|
341
|
+
columnFields.filter(field => !!apiRef.current.getColumn(field).preProcessEditCellProps && deleteValue).forEach(field => {
|
|
342
|
+
const column = apiRef.current.getColumn(field);
|
|
343
|
+
const value = apiRef.current.getCellValue(id, field);
|
|
344
|
+
const newValue = deleteValue ? getDefaultCellValue(column) : initialValue ?? value;
|
|
345
|
+
Promise.resolve(column.preProcessEditCellProps({
|
|
346
|
+
id,
|
|
347
|
+
row: apiRef.current.getRow(id),
|
|
348
|
+
props: newProps[field],
|
|
349
|
+
hasChanged: newValue !== value
|
|
350
|
+
})).then(processedProps => {
|
|
351
|
+
// Check if still in edit mode before updating
|
|
352
|
+
if (apiRef.current.getRowMode(id) === GridRowModes.Edit) {
|
|
353
|
+
const editingState = gridEditRowsStateSelector(apiRef.current.state);
|
|
354
|
+
updateOrDeleteFieldState(id, field, _extends({}, processedProps, {
|
|
355
|
+
value: editingState[id][field].value,
|
|
356
|
+
isProcessingProps: false
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
340
361
|
});
|
|
341
362
|
const stopRowEditMode = React.useCallback(params => {
|
|
342
363
|
const {
|
|
@@ -4,7 +4,7 @@ import { GridSignature, useGridApiEventHandler } from "../../utils/useGridApiEve
|
|
|
4
4
|
import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
|
|
5
5
|
import { useGridLogger } from "../../utils/useGridLogger.js";
|
|
6
6
|
import { useGridSelector } from "../../utils/useGridSelector.js";
|
|
7
|
-
import { gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
|
|
7
|
+
import { gridRowsLookupSelector, gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
|
|
8
8
|
import { gridRowSelectionStateSelector, selectedGridRowsSelector, selectedIdsLookupSelector } from "./gridRowSelectionSelector.js";
|
|
9
9
|
import { gridPaginatedVisibleSortedGridRowIdsSelector } from "../pagination/index.js";
|
|
10
10
|
import { gridFocusCellSelector } from "../focus/gridFocusStateSelector.js";
|
|
@@ -171,30 +171,33 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
171
171
|
let newSelection;
|
|
172
172
|
if (resetSelection) {
|
|
173
173
|
if (isSelected) {
|
|
174
|
-
newSelection = selectableIds;
|
|
174
|
+
newSelection = new Set(selectableIds);
|
|
175
175
|
if (applyAutoSelection) {
|
|
176
176
|
const addRow = rowId => {
|
|
177
|
-
newSelection.
|
|
177
|
+
newSelection.add(rowId);
|
|
178
178
|
};
|
|
179
179
|
selectableIds.forEach(id => {
|
|
180
180
|
findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
183
|
} else {
|
|
184
|
-
newSelection =
|
|
184
|
+
newSelection = new Set();
|
|
185
|
+
}
|
|
186
|
+
const currentLookup = selectedIdsLookupSelector(apiRef);
|
|
187
|
+
if (newSelection.size === Object.keys(currentLookup).length && Array.from(newSelection).every(id => currentLookup[id] === id)) {
|
|
188
|
+
return;
|
|
185
189
|
}
|
|
186
190
|
} else {
|
|
187
|
-
|
|
188
|
-
const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
|
|
191
|
+
newSelection = new Set(Object.values(selectedIdsLookupSelector(apiRef)));
|
|
189
192
|
const addRow = rowId => {
|
|
190
|
-
|
|
193
|
+
newSelection.add(rowId);
|
|
191
194
|
};
|
|
192
195
|
const removeRow = rowId => {
|
|
193
|
-
delete
|
|
196
|
+
newSelection.delete(rowId);
|
|
194
197
|
};
|
|
195
198
|
selectableIds.forEach(id => {
|
|
196
199
|
if (isSelected) {
|
|
197
|
-
|
|
200
|
+
newSelection.add(id);
|
|
198
201
|
if (applyAutoSelection) {
|
|
199
202
|
findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
|
|
200
203
|
}
|
|
@@ -205,11 +208,10 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
210
|
});
|
|
208
|
-
newSelection = Object.values(selectionLookup);
|
|
209
211
|
}
|
|
210
|
-
const isSelectionValid = newSelection.
|
|
212
|
+
const isSelectionValid = newSelection.size < 2 || canHaveMultipleSelection;
|
|
211
213
|
if (isSelectionValid) {
|
|
212
|
-
apiRef.current.setRowSelectionModel(newSelection);
|
|
214
|
+
apiRef.current.setRowSelectionModel(Array.from(newSelection));
|
|
213
215
|
}
|
|
214
216
|
}, [logger, applyAutoSelection, canHaveMultipleSelection, apiRef, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents]);
|
|
215
217
|
const selectRowRange = React.useCallback(({
|
|
@@ -252,13 +254,20 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
252
254
|
return;
|
|
253
255
|
}
|
|
254
256
|
const currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
|
|
257
|
+
const rowsLookup = gridRowsLookupSelector(apiRef);
|
|
255
258
|
const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
|
|
256
259
|
|
|
257
260
|
// We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
|
|
258
261
|
const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
|
|
262
|
+
const isNonExistent = id => {
|
|
263
|
+
if (props.filterMode === 'server') {
|
|
264
|
+
return !rowsLookup[id];
|
|
265
|
+
}
|
|
266
|
+
return filteredRowsLookup[id] !== true;
|
|
267
|
+
};
|
|
259
268
|
let hasChanged = false;
|
|
260
269
|
currentSelection.forEach(id => {
|
|
261
|
-
if (
|
|
270
|
+
if (isNonExistent(id)) {
|
|
262
271
|
if (props.keepNonExistentRowsSelected) {
|
|
263
272
|
return;
|
|
264
273
|
}
|
|
@@ -284,15 +293,20 @@ export const useGridRowSelection = (apiRef, props) => {
|
|
|
284
293
|
}
|
|
285
294
|
}
|
|
286
295
|
});
|
|
287
|
-
|
|
296
|
+
|
|
297
|
+
// For nested data, on row tree updation (filtering, adding rows, etc.) when the selection is
|
|
298
|
+
// not empty, we need to re-run scanning of the tree to propagate the selection changes
|
|
299
|
+
// Example: A parent whose de-selected children are filtered out should now be selected
|
|
300
|
+
const shouldReapplyPropagation = isNestedData && props.rowSelectionPropagation?.parents && Object.keys(selectionLookup).length > 0;
|
|
301
|
+
if (hasChanged || shouldReapplyPropagation && !sortModelUpdated) {
|
|
288
302
|
const newSelection = Object.values(selectionLookup);
|
|
289
|
-
if (
|
|
303
|
+
if (shouldReapplyPropagation) {
|
|
290
304
|
apiRef.current.selectRows(newSelection, true, true);
|
|
291
305
|
} else {
|
|
292
306
|
apiRef.current.setRowSelectionModel(newSelection);
|
|
293
307
|
}
|
|
294
308
|
}
|
|
295
|
-
}, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, tree]);
|
|
309
|
+
}, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, props.filterMode, tree]);
|
|
296
310
|
const handleSingleRowSelection = React.useCallback((id, event) => {
|
|
297
311
|
const hasCtrlKey = event.metaKey || event.ctrlKey;
|
|
298
312
|
|
package/modern/index.js
CHANGED
|
@@ -8,13 +8,11 @@ var _GridFilterInputBoolean = require("../components/panel/filterPanel/GridFilte
|
|
|
8
8
|
const getGridBooleanOperators = () => [{
|
|
9
9
|
value: 'is',
|
|
10
10
|
getApplyFilterFn: filterItem => {
|
|
11
|
-
|
|
11
|
+
const sanitizedValue = (0, _GridFilterInputBoolean.sanitizeFilterItemValue)(filterItem.value);
|
|
12
|
+
if (sanitizedValue === undefined) {
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
return value => {
|
|
16
|
-
return Boolean(value) === valueAsBoolean;
|
|
17
|
-
};
|
|
15
|
+
return value => Boolean(value) === sanitizedValue;
|
|
18
16
|
},
|
|
19
17
|
InputComponent: _GridFilterInputBoolean.GridFilterInputBoolean
|
|
20
18
|
}];
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
8
|
exports.GridFilterInputBoolean = GridFilterInputBoolean;
|
|
9
|
+
exports.sanitizeFilterItemValue = void 0;
|
|
9
10
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
10
11
|
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
11
12
|
var React = _interopRequireWildcard(require("react"));
|
|
@@ -15,6 +16,16 @@ var _styles = require("@mui/material/styles");
|
|
|
15
16
|
var _useGridRootProps = require("../../../hooks/utils/useGridRootProps");
|
|
16
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
18
|
const _excluded = ["item", "applyValue", "apiRef", "focusElementRef", "isFilterActive", "clearButton", "tabIndex", "label", "variant", "InputLabelProps"];
|
|
19
|
+
const sanitizeFilterItemValue = value => {
|
|
20
|
+
if (String(value).toLowerCase() === 'true') {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
if (String(value).toLowerCase() === 'false') {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
};
|
|
28
|
+
exports.sanitizeFilterItemValue = sanitizeFilterItemValue;
|
|
18
29
|
const BooleanOperatorContainer = (0, _styles.styled)('div')({
|
|
19
30
|
display: 'flex',
|
|
20
31
|
alignItems: 'center',
|
|
@@ -35,7 +46,7 @@ function GridFilterInputBoolean(props) {
|
|
|
35
46
|
variant = 'standard'
|
|
36
47
|
} = props,
|
|
37
48
|
others = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
38
|
-
const [filterValueState, setFilterValueState] = React.useState(item.value
|
|
49
|
+
const [filterValueState, setFilterValueState] = React.useState(sanitizeFilterItemValue(item.value));
|
|
39
50
|
const rootProps = (0, _useGridRootProps.useGridRootProps)();
|
|
40
51
|
const labelId = (0, _utils.unstable_useId)();
|
|
41
52
|
const selectId = (0, _utils.unstable_useId)();
|
|
@@ -43,14 +54,14 @@ function GridFilterInputBoolean(props) {
|
|
|
43
54
|
const isSelectNative = baseSelectProps.native ?? false;
|
|
44
55
|
const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
|
|
45
56
|
const onFilterChange = React.useCallback(event => {
|
|
46
|
-
const value = event.target.value;
|
|
57
|
+
const value = sanitizeFilterItemValue(event.target.value);
|
|
47
58
|
setFilterValueState(value);
|
|
48
59
|
applyValue((0, _extends2.default)({}, item, {
|
|
49
|
-
value
|
|
60
|
+
value
|
|
50
61
|
}));
|
|
51
62
|
}, [applyValue, item]);
|
|
52
63
|
React.useEffect(() => {
|
|
53
|
-
setFilterValueState(item.value
|
|
64
|
+
setFilterValueState(sanitizeFilterItemValue(item.value));
|
|
54
65
|
}, [item.value]);
|
|
55
66
|
const label = labelProp ?? apiRef.current.getLocaleText('filterPanelInputLabel');
|
|
56
67
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(BooleanOperatorContainer, {
|
|
@@ -65,7 +76,7 @@ function GridFilterInputBoolean(props) {
|
|
|
65
76
|
labelId: labelId,
|
|
66
77
|
id: selectId,
|
|
67
78
|
label: label,
|
|
68
|
-
value: filterValueState,
|
|
79
|
+
value: filterValueState === undefined ? '' : String(filterValueState),
|
|
69
80
|
onChange: onFilterChange,
|
|
70
81
|
variant: variant,
|
|
71
82
|
notched: variant === 'outlined' ? true : undefined,
|
|
@@ -77,6 +77,9 @@ const GridVirtualScrollbar = exports.GridVirtualScrollbar = /*#__PURE__*/React.f
|
|
|
77
77
|
const onScrollerScroll = (0, _utils.unstable_useEventCallback)(() => {
|
|
78
78
|
const scroller = apiRef.current.virtualScrollerRef.current;
|
|
79
79
|
const scrollbar = scrollbarRef.current;
|
|
80
|
+
if (!scrollbar) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
80
83
|
if (scroller[propertyScroll] === lastPosition.current) {
|
|
81
84
|
return;
|
|
82
85
|
}
|
|
@@ -92,6 +95,9 @@ const GridVirtualScrollbar = exports.GridVirtualScrollbar = /*#__PURE__*/React.f
|
|
|
92
95
|
const onScrollbarScroll = (0, _utils.unstable_useEventCallback)(() => {
|
|
93
96
|
const scroller = apiRef.current.virtualScrollerRef.current;
|
|
94
97
|
const scrollbar = scrollbarRef.current;
|
|
98
|
+
if (!scrollbar) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
95
101
|
if (isLocked.current) {
|
|
96
102
|
isLocked.current = false;
|
|
97
103
|
return;
|
|
@@ -249,26 +249,45 @@ const useGridCellEditing = (apiRef, props) => {
|
|
|
249
249
|
mode: _gridEditRowModel.GridCellModes.Edit
|
|
250
250
|
}, other));
|
|
251
251
|
}, [throwIfNotEditable, throwIfNotInMode, updateFieldInCellModesModel]);
|
|
252
|
-
const updateStateToStartCellEditMode = (0, _utils.unstable_useEventCallback)(params => {
|
|
252
|
+
const updateStateToStartCellEditMode = (0, _utils.unstable_useEventCallback)(async params => {
|
|
253
253
|
const {
|
|
254
254
|
id,
|
|
255
255
|
field,
|
|
256
256
|
deleteValue,
|
|
257
257
|
initialValue
|
|
258
258
|
} = params;
|
|
259
|
-
|
|
259
|
+
const value = apiRef.current.getCellValue(id, field);
|
|
260
|
+
let newValue = value;
|
|
260
261
|
if (deleteValue) {
|
|
261
262
|
newValue = (0, _utils3.getDefaultCellValue)(apiRef.current.getColumn(field));
|
|
262
263
|
} else if (initialValue) {
|
|
263
264
|
newValue = initialValue;
|
|
264
265
|
}
|
|
265
|
-
const
|
|
266
|
+
const column = apiRef.current.getColumn(field);
|
|
267
|
+
const shouldProcessEditCellProps = !!column.preProcessEditCellProps && deleteValue;
|
|
268
|
+
let newProps = {
|
|
266
269
|
value: newValue,
|
|
267
270
|
error: false,
|
|
268
|
-
isProcessingProps:
|
|
271
|
+
isProcessingProps: shouldProcessEditCellProps
|
|
269
272
|
};
|
|
270
273
|
updateOrDeleteFieldState(id, field, newProps);
|
|
271
274
|
apiRef.current.setCellFocus(id, field);
|
|
275
|
+
if (shouldProcessEditCellProps) {
|
|
276
|
+
newProps = await Promise.resolve(column.preProcessEditCellProps({
|
|
277
|
+
id,
|
|
278
|
+
row: apiRef.current.getRow(id),
|
|
279
|
+
props: newProps,
|
|
280
|
+
hasChanged: newValue !== value
|
|
281
|
+
}));
|
|
282
|
+
// Check if still in edit mode before updating
|
|
283
|
+
if (apiRef.current.getCellMode(id, field) === _gridEditRowModel.GridCellModes.Edit) {
|
|
284
|
+
const editingState = (0, _gridEditingSelectors.gridEditRowsStateSelector)(apiRef.current.state);
|
|
285
|
+
updateOrDeleteFieldState(id, field, (0, _extends2.default)({}, newProps, {
|
|
286
|
+
value: editingState[id][field].value,
|
|
287
|
+
isProcessingProps: false
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
272
291
|
});
|
|
273
292
|
const stopCellEditMode = React.useCallback(params => {
|
|
274
293
|
const {
|
|
@@ -326,10 +326,11 @@ const useGridRowEditing = (apiRef, props) => {
|
|
|
326
326
|
if (!cellParams.isEditable) {
|
|
327
327
|
return acc;
|
|
328
328
|
}
|
|
329
|
+
const column = apiRef.current.getColumn(field);
|
|
329
330
|
let newValue = apiRef.current.getCellValue(id, field);
|
|
330
331
|
if (fieldToFocus === field && (deleteValue || initialValue)) {
|
|
331
332
|
if (deleteValue) {
|
|
332
|
-
newValue = (0, _utils3.getDefaultCellValue)(
|
|
333
|
+
newValue = (0, _utils3.getDefaultCellValue)(column);
|
|
333
334
|
} else if (initialValue) {
|
|
334
335
|
newValue = initialValue;
|
|
335
336
|
}
|
|
@@ -337,7 +338,7 @@ const useGridRowEditing = (apiRef, props) => {
|
|
|
337
338
|
acc[field] = {
|
|
338
339
|
value: newValue,
|
|
339
340
|
error: false,
|
|
340
|
-
isProcessingProps:
|
|
341
|
+
isProcessingProps: !!column.preProcessEditCellProps && deleteValue
|
|
341
342
|
};
|
|
342
343
|
return acc;
|
|
343
344
|
}, {});
|
|
@@ -345,6 +346,26 @@ const useGridRowEditing = (apiRef, props) => {
|
|
|
345
346
|
if (fieldToFocus) {
|
|
346
347
|
apiRef.current.setCellFocus(id, fieldToFocus);
|
|
347
348
|
}
|
|
349
|
+
columnFields.filter(field => !!apiRef.current.getColumn(field).preProcessEditCellProps && deleteValue).forEach(field => {
|
|
350
|
+
const column = apiRef.current.getColumn(field);
|
|
351
|
+
const value = apiRef.current.getCellValue(id, field);
|
|
352
|
+
const newValue = deleteValue ? (0, _utils3.getDefaultCellValue)(column) : initialValue ?? value;
|
|
353
|
+
Promise.resolve(column.preProcessEditCellProps({
|
|
354
|
+
id,
|
|
355
|
+
row: apiRef.current.getRow(id),
|
|
356
|
+
props: newProps[field],
|
|
357
|
+
hasChanged: newValue !== value
|
|
358
|
+
})).then(processedProps => {
|
|
359
|
+
// Check if still in edit mode before updating
|
|
360
|
+
if (apiRef.current.getRowMode(id) === _gridEditRowModel.GridRowModes.Edit) {
|
|
361
|
+
const editingState = (0, _gridEditingSelectors.gridEditRowsStateSelector)(apiRef.current.state);
|
|
362
|
+
updateOrDeleteFieldState(id, field, (0, _extends2.default)({}, processedProps, {
|
|
363
|
+
value: editingState[id][field].value,
|
|
364
|
+
isProcessingProps: false
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
});
|
|
348
369
|
});
|
|
349
370
|
const stopRowEditMode = React.useCallback(params => {
|
|
350
371
|
const {
|
|
@@ -180,30 +180,33 @@ const useGridRowSelection = (apiRef, props) => {
|
|
|
180
180
|
let newSelection;
|
|
181
181
|
if (resetSelection) {
|
|
182
182
|
if (isSelected) {
|
|
183
|
-
newSelection = selectableIds;
|
|
183
|
+
newSelection = new Set(selectableIds);
|
|
184
184
|
if (applyAutoSelection) {
|
|
185
185
|
const addRow = rowId => {
|
|
186
|
-
newSelection.
|
|
186
|
+
newSelection.add(rowId);
|
|
187
187
|
};
|
|
188
188
|
selectableIds.forEach(id => {
|
|
189
189
|
(0, _utils.findRowsToSelect)(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
} else {
|
|
193
|
-
newSelection =
|
|
193
|
+
newSelection = new Set();
|
|
194
|
+
}
|
|
195
|
+
const currentLookup = (0, _gridRowSelectionSelector.selectedIdsLookupSelector)(apiRef);
|
|
196
|
+
if (newSelection.size === Object.keys(currentLookup).length && Array.from(newSelection).every(id => currentLookup[id] === id)) {
|
|
197
|
+
return;
|
|
194
198
|
}
|
|
195
199
|
} else {
|
|
196
|
-
|
|
197
|
-
const selectionLookup = (0, _extends2.default)({}, (0, _gridRowSelectionSelector.selectedIdsLookupSelector)(apiRef));
|
|
200
|
+
newSelection = new Set(Object.values((0, _gridRowSelectionSelector.selectedIdsLookupSelector)(apiRef)));
|
|
198
201
|
const addRow = rowId => {
|
|
199
|
-
|
|
202
|
+
newSelection.add(rowId);
|
|
200
203
|
};
|
|
201
204
|
const removeRow = rowId => {
|
|
202
|
-
delete
|
|
205
|
+
newSelection.delete(rowId);
|
|
203
206
|
};
|
|
204
207
|
selectableIds.forEach(id => {
|
|
205
208
|
if (isSelected) {
|
|
206
|
-
|
|
209
|
+
newSelection.add(id);
|
|
207
210
|
if (applyAutoSelection) {
|
|
208
211
|
(0, _utils.findRowsToSelect)(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
|
|
209
212
|
}
|
|
@@ -214,11 +217,10 @@ const useGridRowSelection = (apiRef, props) => {
|
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
});
|
|
217
|
-
newSelection = Object.values(selectionLookup);
|
|
218
220
|
}
|
|
219
|
-
const isSelectionValid = newSelection.
|
|
221
|
+
const isSelectionValid = newSelection.size < 2 || canHaveMultipleSelection;
|
|
220
222
|
if (isSelectionValid) {
|
|
221
|
-
apiRef.current.setRowSelectionModel(newSelection);
|
|
223
|
+
apiRef.current.setRowSelectionModel(Array.from(newSelection));
|
|
222
224
|
}
|
|
223
225
|
}, [logger, applyAutoSelection, canHaveMultipleSelection, apiRef, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents]);
|
|
224
226
|
const selectRowRange = React.useCallback(({
|
|
@@ -261,13 +263,20 @@ const useGridRowSelection = (apiRef, props) => {
|
|
|
261
263
|
return;
|
|
262
264
|
}
|
|
263
265
|
const currentSelection = (0, _gridRowSelectionSelector.gridRowSelectionStateSelector)(apiRef.current.state);
|
|
266
|
+
const rowsLookup = (0, _gridRowsSelector.gridRowsLookupSelector)(apiRef);
|
|
264
267
|
const filteredRowsLookup = (0, _gridFilterSelector.gridFilteredRowsLookupSelector)(apiRef);
|
|
265
268
|
|
|
266
269
|
// We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
|
|
267
270
|
const selectionLookup = (0, _extends2.default)({}, (0, _gridRowSelectionSelector.selectedIdsLookupSelector)(apiRef));
|
|
271
|
+
const isNonExistent = id => {
|
|
272
|
+
if (props.filterMode === 'server') {
|
|
273
|
+
return !rowsLookup[id];
|
|
274
|
+
}
|
|
275
|
+
return filteredRowsLookup[id] !== true;
|
|
276
|
+
};
|
|
268
277
|
let hasChanged = false;
|
|
269
278
|
currentSelection.forEach(id => {
|
|
270
|
-
if (
|
|
279
|
+
if (isNonExistent(id)) {
|
|
271
280
|
if (props.keepNonExistentRowsSelected) {
|
|
272
281
|
return;
|
|
273
282
|
}
|
|
@@ -293,15 +302,20 @@ const useGridRowSelection = (apiRef, props) => {
|
|
|
293
302
|
}
|
|
294
303
|
}
|
|
295
304
|
});
|
|
296
|
-
|
|
305
|
+
|
|
306
|
+
// For nested data, on row tree updation (filtering, adding rows, etc.) when the selection is
|
|
307
|
+
// not empty, we need to re-run scanning of the tree to propagate the selection changes
|
|
308
|
+
// Example: A parent whose de-selected children are filtered out should now be selected
|
|
309
|
+
const shouldReapplyPropagation = isNestedData && props.rowSelectionPropagation?.parents && Object.keys(selectionLookup).length > 0;
|
|
310
|
+
if (hasChanged || shouldReapplyPropagation && !sortModelUpdated) {
|
|
297
311
|
const newSelection = Object.values(selectionLookup);
|
|
298
|
-
if (
|
|
312
|
+
if (shouldReapplyPropagation) {
|
|
299
313
|
apiRef.current.selectRows(newSelection, true, true);
|
|
300
314
|
} else {
|
|
301
315
|
apiRef.current.setRowSelectionModel(newSelection);
|
|
302
316
|
}
|
|
303
317
|
}
|
|
304
|
-
}, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, tree]);
|
|
318
|
+
}, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, props.filterMode, tree]);
|
|
305
319
|
const handleSingleRowSelection = React.useCallback((id, event) => {
|
|
306
320
|
const hasCtrlKey = event.metaKey || event.ctrlKey;
|
|
307
321
|
|
package/node/index.js
CHANGED