@mui/x-data-grid-pro 7.7.1 → 7.8.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 +100 -1
- package/DataGridPro/DataGridPro.js +13 -1
- package/DataGridPro/useDataGridProComponent.js +6 -1
- package/DataGridPro/useDataGridProProps.js +10 -3
- package/components/GridDataSourceTreeDataGroupingCell.d.ts +12 -0
- package/components/GridDataSourceTreeDataGroupingCell.js +120 -0
- package/components/GridTreeDataGroupingCell.js +1 -4
- package/esm/DataGridPro/DataGridPro.js +13 -1
- package/esm/DataGridPro/useDataGridProComponent.js +6 -1
- package/esm/DataGridPro/useDataGridProProps.js +9 -3
- package/esm/components/GridDataSourceTreeDataGroupingCell.js +111 -0
- package/esm/components/GridTreeDataGroupingCell.js +1 -4
- package/esm/hooks/features/dataSource/cache.js +36 -0
- package/esm/hooks/features/dataSource/gridDataSourceSelector.js +24 -0
- package/esm/hooks/features/dataSource/useGridDataSource.js +218 -0
- package/esm/hooks/features/dataSource/utils.js +87 -0
- package/esm/hooks/features/index.js +3 -1
- package/esm/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.js +148 -0
- package/esm/hooks/features/serverSideTreeData/utils.js +18 -0
- package/esm/hooks/features/treeData/useGridTreeData.js +6 -2
- package/esm/hooks/features/treeData/useGridTreeDataPreProcessors.js +6 -3
- package/esm/internals/index.js +2 -0
- package/esm/internals/propValidation.js +1 -1
- package/esm/models/index.js +2 -1
- package/esm/utils/releaseInfo.js +1 -1
- package/esm/utils/tree/createRowTree.js +6 -2
- package/esm/utils/tree/insertDataRowInTree.js +34 -10
- package/esm/utils/tree/updateRowTree.js +13 -5
- package/esm/utils/tree/utils.js +5 -1
- package/hooks/features/columnHeaders/useGridColumnHeaders.d.ts +1 -1
- package/hooks/features/columnPinning/useGridColumnPinning.d.ts +1 -1
- package/hooks/features/columnReorder/useGridColumnReorder.d.ts +1 -1
- package/hooks/features/dataSource/cache.d.ts +18 -0
- package/hooks/features/dataSource/cache.js +43 -0
- package/hooks/features/dataSource/gridDataSourceSelector.d.ts +14 -0
- package/hooks/features/dataSource/gridDataSourceSelector.js +32 -0
- package/hooks/features/dataSource/interfaces.d.ts +50 -0
- package/hooks/features/dataSource/useGridDataSource.d.ts +6 -0
- package/hooks/features/dataSource/useGridDataSource.js +229 -0
- package/hooks/features/dataSource/utils.d.ts +29 -0
- package/hooks/features/dataSource/utils.js +95 -0
- package/hooks/features/detailPanel/gridDetailPanelSelector.d.ts +0 -1
- package/hooks/features/detailPanel/useGridDetailPanel.d.ts +1 -1
- package/hooks/features/index.d.ts +2 -0
- package/hooks/features/index.js +22 -0
- package/hooks/features/infiniteLoader/useGridInfiniteLoader.d.ts +1 -1
- package/hooks/features/lazyLoader/useGridLazyLoader.d.ts +1 -1
- package/hooks/features/lazyLoader/useGridLazyLoaderPreProcessors.d.ts +1 -1
- package/hooks/features/rowPinning/useGridRowPinning.d.ts +1 -1
- package/hooks/features/rowPinning/useGridRowPinningPreProcessors.d.ts +2 -2
- package/hooks/features/rowReorder/useGridRowReorder.d.ts +1 -1
- package/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.d.ts +4 -0
- package/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.js +158 -0
- package/hooks/features/serverSideTreeData/utils.d.ts +6 -0
- package/hooks/features/serverSideTreeData/utils.js +25 -0
- package/hooks/features/treeData/gridTreeDataUtils.d.ts +1 -2
- package/hooks/features/treeData/useGridTreeData.d.ts +2 -1
- package/hooks/features/treeData/useGridTreeData.js +6 -2
- package/hooks/features/treeData/useGridTreeDataPreProcessors.d.ts +1 -1
- package/hooks/features/treeData/useGridTreeDataPreProcessors.js +6 -3
- package/hooks/utils/useGridApiContext.d.ts +0 -1
- package/hooks/utils/useGridApiRef.d.ts +0 -1
- package/hooks/utils/useGridPrivateApiContext.d.ts +0 -1
- package/index.js +1 -1
- package/internals/index.d.ts +2 -0
- package/internals/index.js +23 -0
- package/internals/propValidation.js +1 -1
- package/material/index.d.ts +0 -1
- package/models/dataGridProProps.d.ts +17 -11
- package/models/gridApiPro.d.ts +3 -3
- package/models/gridProSlotsComponent.d.ts +0 -1
- package/models/gridStatePro.d.ts +2 -0
- package/models/index.d.ts +1 -0
- package/modern/DataGridPro/DataGridPro.js +13 -1
- package/modern/DataGridPro/useDataGridProComponent.js +6 -1
- package/modern/DataGridPro/useDataGridProProps.js +9 -3
- package/modern/components/GridDataSourceTreeDataGroupingCell.js +111 -0
- package/modern/components/GridTreeDataGroupingCell.js +1 -4
- package/modern/hooks/features/dataSource/cache.js +36 -0
- package/modern/hooks/features/dataSource/gridDataSourceSelector.js +24 -0
- package/modern/hooks/features/dataSource/useGridDataSource.js +218 -0
- package/modern/hooks/features/dataSource/utils.js +87 -0
- package/modern/hooks/features/index.js +3 -1
- package/modern/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.js +148 -0
- package/modern/hooks/features/serverSideTreeData/utils.js +18 -0
- package/modern/hooks/features/treeData/useGridTreeData.js +6 -2
- package/modern/hooks/features/treeData/useGridTreeDataPreProcessors.js +6 -3
- package/modern/index.js +1 -1
- package/modern/internals/index.js +2 -0
- package/modern/internals/propValidation.js +1 -1
- package/modern/models/index.js +2 -1
- package/modern/utils/releaseInfo.js +1 -1
- package/modern/utils/tree/createRowTree.js +6 -2
- package/modern/utils/tree/insertDataRowInTree.js +34 -10
- package/modern/utils/tree/updateRowTree.js +13 -5
- package/modern/utils/tree/utils.js +5 -1
- package/package.json +3 -3
- package/typeOverloads/modules.d.ts +0 -1
- package/utils/releaseInfo.js +1 -1
- package/utils/tree/createRowTree.js +6 -2
- package/utils/tree/insertDataRowInTree.d.ts +3 -1
- package/utils/tree/insertDataRowInTree.js +33 -9
- package/utils/tree/models.d.ts +1 -0
- package/utils/tree/updateRowTree.d.ts +1 -0
- package/utils/tree/updateRowTree.js +13 -5
- package/utils/tree/utils.d.ts +5 -4
- package/utils/tree/utils.js +7 -2
- package/models/dataSource.d.ts +0 -64
- /package/esm/{models/dataSource.js → hooks/features/dataSource/interfaces.js} +0 -0
- /package/{models/dataSource.js → hooks/features/dataSource/interfaces.js} +0 -0
- /package/modern/{models/dataSource.js → hooks/features/dataSource/interfaces.js} +0 -0
|
@@ -4,6 +4,7 @@ import { useGridInfiniteLoader } from '../hooks/features/infiniteLoader/useGridI
|
|
|
4
4
|
import { useGridColumnReorder, columnReorderStateInitializer } from '../hooks/features/columnReorder/useGridColumnReorder';
|
|
5
5
|
import { useGridTreeData } from '../hooks/features/treeData/useGridTreeData';
|
|
6
6
|
import { useGridTreeDataPreProcessors } from '../hooks/features/treeData/useGridTreeDataPreProcessors';
|
|
7
|
+
import { useGridDataSourceTreeDataPreProcessors } from '../hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors';
|
|
7
8
|
import { useGridColumnPinning, columnPinningStateInitializer } from '../hooks/features/columnPinning/useGridColumnPinning';
|
|
8
9
|
import { useGridColumnPinningPreProcessors } from '../hooks/features/columnPinning/useGridColumnPinningPreProcessors';
|
|
9
10
|
import { useGridDetailPanel, detailPanelStateInitializer } from '../hooks/features/detailPanel/useGridDetailPanel';
|
|
@@ -14,6 +15,7 @@ import { useGridLazyLoader } from '../hooks/features/lazyLoader/useGridLazyLoade
|
|
|
14
15
|
import { useGridLazyLoaderPreProcessors } from '../hooks/features/lazyLoader/useGridLazyLoaderPreProcessors';
|
|
15
16
|
import { useGridRowPinning, rowPinningStateInitializer } from '../hooks/features/rowPinning/useGridRowPinning';
|
|
16
17
|
import { useGridRowPinningPreProcessors } from '../hooks/features/rowPinning/useGridRowPinningPreProcessors';
|
|
18
|
+
import { useGridDataSource, dataSourceStateInitializer } from '../hooks/features/dataSource/useGridDataSource';
|
|
17
19
|
export const useDataGridProComponent = (inputApiRef, props) => {
|
|
18
20
|
const apiRef = useGridInitialization(inputApiRef, props);
|
|
19
21
|
|
|
@@ -23,6 +25,7 @@ export const useDataGridProComponent = (inputApiRef, props) => {
|
|
|
23
25
|
useGridRowSelectionPreProcessors(apiRef, props);
|
|
24
26
|
useGridRowReorderPreProcessors(apiRef, props);
|
|
25
27
|
useGridTreeDataPreProcessors(apiRef, props);
|
|
28
|
+
useGridDataSourceTreeDataPreProcessors(apiRef, props);
|
|
26
29
|
useGridLazyLoaderPreProcessors(apiRef, props);
|
|
27
30
|
useGridRowPinningPreProcessors(apiRef);
|
|
28
31
|
useGridDetailPanelPreProcessors(apiRef, props);
|
|
@@ -55,8 +58,9 @@ export const useDataGridProComponent = (inputApiRef, props) => {
|
|
|
55
58
|
useGridInitializeState(columnMenuStateInitializer, apiRef, props);
|
|
56
59
|
useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
|
|
57
60
|
useGridInitializeState(virtualizationStateInitializer, apiRef, props);
|
|
61
|
+
useGridInitializeState(dataSourceStateInitializer, apiRef, props);
|
|
58
62
|
useGridHeaderFiltering(apiRef, props);
|
|
59
|
-
useGridTreeData(apiRef);
|
|
63
|
+
useGridTreeData(apiRef, props);
|
|
60
64
|
useGridKeyboardNavigation(apiRef, props);
|
|
61
65
|
useGridRowSelection(apiRef, props);
|
|
62
66
|
useGridColumnPinning(apiRef, props);
|
|
@@ -89,5 +93,6 @@ export const useDataGridProComponent = (inputApiRef, props) => {
|
|
|
89
93
|
useGridEvents(apiRef, props);
|
|
90
94
|
useGridStatePersistence(apiRef);
|
|
91
95
|
useGridVirtualization(apiRef, props);
|
|
96
|
+
useGridDataSource(apiRef, props);
|
|
92
97
|
return apiRef;
|
|
93
98
|
};
|
|
@@ -4,6 +4,13 @@ import { useThemeProps } from '@mui/material/styles';
|
|
|
4
4
|
import { GRID_DEFAULT_LOCALE_TEXT, DATA_GRID_PROPS_DEFAULT_VALUES } from '@mui/x-data-grid';
|
|
5
5
|
import { computeSlots, useProps } from '@mui/x-data-grid/internals';
|
|
6
6
|
import { DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS } from '../constants/dataGridProDefaultSlotsComponents';
|
|
7
|
+
const getDataGridProForcedProps = themedProps => _extends({
|
|
8
|
+
signature: 'DataGridPro'
|
|
9
|
+
}, themedProps.unstable_dataSource ? {
|
|
10
|
+
filterMode: 'server',
|
|
11
|
+
sortingMode: 'server',
|
|
12
|
+
paginationMode: 'server'
|
|
13
|
+
} : {});
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
16
|
* The default values of `DataGridProPropsWithDefaultValue` to inject in the props of DataGridPro.
|
|
@@ -38,7 +45,6 @@ export const useDataGridProProps = inProps => {
|
|
|
38
45
|
}), [themedProps.slots]);
|
|
39
46
|
return React.useMemo(() => _extends({}, DATA_GRID_PRO_PROPS_DEFAULT_VALUES, themedProps, {
|
|
40
47
|
localeText,
|
|
41
|
-
slots
|
|
42
|
-
|
|
43
|
-
}), [themedProps, localeText, slots]);
|
|
48
|
+
slots
|
|
49
|
+
}, getDataGridProForcedProps(themedProps)), [themedProps, localeText, slots]);
|
|
44
50
|
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { unstable_composeClasses as composeClasses } from '@mui/utils';
|
|
4
|
+
import Box from '@mui/material/Box';
|
|
5
|
+
import Badge from '@mui/material/Badge';
|
|
6
|
+
import { getDataGridUtilityClass, useGridSelector } from '@mui/x-data-grid';
|
|
7
|
+
import CircularProgress from '@mui/material/CircularProgress';
|
|
8
|
+
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
|
|
9
|
+
import { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext';
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
const useUtilityClasses = ownerState => {
|
|
12
|
+
const {
|
|
13
|
+
classes
|
|
14
|
+
} = ownerState;
|
|
15
|
+
const slots = {
|
|
16
|
+
root: ['treeDataGroupingCell'],
|
|
17
|
+
toggle: ['treeDataGroupingCellToggle'],
|
|
18
|
+
loadingContainer: ['treeDataGroupingCellLoadingContainer']
|
|
19
|
+
};
|
|
20
|
+
return composeClasses(slots, getDataGridUtilityClass, classes);
|
|
21
|
+
};
|
|
22
|
+
function GridTreeDataGroupingCellIcon(props) {
|
|
23
|
+
const apiRef = useGridPrivateApiContext();
|
|
24
|
+
const rootProps = useGridRootProps();
|
|
25
|
+
const classes = useUtilityClasses(rootProps);
|
|
26
|
+
const {
|
|
27
|
+
rowNode,
|
|
28
|
+
id,
|
|
29
|
+
field,
|
|
30
|
+
descendantCount
|
|
31
|
+
} = props;
|
|
32
|
+
const loadingSelector = state => state.dataSource.loading[id] ?? false;
|
|
33
|
+
const errorSelector = state => state.dataSource.errors[id];
|
|
34
|
+
const isDataLoading = useGridSelector(apiRef, loadingSelector);
|
|
35
|
+
const error = useGridSelector(apiRef, errorSelector);
|
|
36
|
+
const handleClick = event => {
|
|
37
|
+
if (!rowNode.childrenExpanded) {
|
|
38
|
+
// always fetch/get from cache the children when the node is expanded
|
|
39
|
+
apiRef.current.unstable_dataSource.fetchRows(id);
|
|
40
|
+
} else {
|
|
41
|
+
apiRef.current.setRowChildrenExpansion(id, !rowNode.childrenExpanded);
|
|
42
|
+
}
|
|
43
|
+
apiRef.current.setCellFocus(id, field);
|
|
44
|
+
event.stopPropagation(); // TODO remove event.stopPropagation
|
|
45
|
+
};
|
|
46
|
+
const Icon = rowNode.childrenExpanded ? rootProps.slots.treeDataCollapseIcon : rootProps.slots.treeDataExpandIcon;
|
|
47
|
+
if (isDataLoading) {
|
|
48
|
+
return /*#__PURE__*/_jsx("div", {
|
|
49
|
+
className: classes.loadingContainer,
|
|
50
|
+
children: /*#__PURE__*/_jsx(CircularProgress, {
|
|
51
|
+
size: "1rem",
|
|
52
|
+
color: "inherit"
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return descendantCount > 0 ? /*#__PURE__*/_jsx(rootProps.slots.baseIconButton, _extends({
|
|
57
|
+
size: "small",
|
|
58
|
+
onClick: handleClick,
|
|
59
|
+
tabIndex: -1,
|
|
60
|
+
"aria-label": rowNode.childrenExpanded ? apiRef.current.getLocaleText('treeDataCollapse') : apiRef.current.getLocaleText('treeDataExpand')
|
|
61
|
+
}, rootProps?.slotProps?.baseIconButton, {
|
|
62
|
+
children: /*#__PURE__*/_jsx(rootProps.slots.baseTooltip, {
|
|
63
|
+
title: error?.message ?? null,
|
|
64
|
+
children: /*#__PURE__*/_jsx(Badge, {
|
|
65
|
+
variant: "dot",
|
|
66
|
+
color: "error",
|
|
67
|
+
invisible: !error,
|
|
68
|
+
children: /*#__PURE__*/_jsx(Icon, {
|
|
69
|
+
fontSize: "inherit"
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})) : null;
|
|
74
|
+
}
|
|
75
|
+
export function GridDataSourceTreeDataGroupingCell(props) {
|
|
76
|
+
const {
|
|
77
|
+
id,
|
|
78
|
+
field,
|
|
79
|
+
formattedValue,
|
|
80
|
+
rowNode,
|
|
81
|
+
hideDescendantCount,
|
|
82
|
+
offsetMultiplier = 2
|
|
83
|
+
} = props;
|
|
84
|
+
const rootProps = useGridRootProps();
|
|
85
|
+
const apiRef = useGridPrivateApiContext();
|
|
86
|
+
const rowSelector = state => state.rows.dataRowIdToModelLookup[id];
|
|
87
|
+
const row = useGridSelector(apiRef, rowSelector);
|
|
88
|
+
const classes = useUtilityClasses(rootProps);
|
|
89
|
+
let descendantCount = 0;
|
|
90
|
+
if (row) {
|
|
91
|
+
descendantCount = Math.max(rootProps.unstable_dataSource?.getChildrenCount?.(row) ?? 0, 0);
|
|
92
|
+
}
|
|
93
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
94
|
+
className: classes.root,
|
|
95
|
+
sx: {
|
|
96
|
+
ml: rowNode.depth * offsetMultiplier
|
|
97
|
+
},
|
|
98
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
99
|
+
className: classes.toggle,
|
|
100
|
+
children: /*#__PURE__*/_jsx(GridTreeDataGroupingCellIcon, {
|
|
101
|
+
id: id,
|
|
102
|
+
field: field,
|
|
103
|
+
rowNode: rowNode,
|
|
104
|
+
row: row,
|
|
105
|
+
descendantCount: descendantCount
|
|
106
|
+
})
|
|
107
|
+
}), /*#__PURE__*/_jsxs("span", {
|
|
108
|
+
children: [formattedValue === undefined ? rowNode.groupingKey : formattedValue, !hideDescendantCount && descendantCount > 0 ? ` (${descendantCount})` : '']
|
|
109
|
+
})]
|
|
110
|
+
});
|
|
111
|
+
}
|
|
@@ -28,10 +28,7 @@ function GridTreeDataGroupingCell(props) {
|
|
|
28
28
|
} = props;
|
|
29
29
|
const rootProps = useGridRootProps();
|
|
30
30
|
const apiRef = useGridApiContext();
|
|
31
|
-
const
|
|
32
|
-
classes: rootProps.classes
|
|
33
|
-
};
|
|
34
|
-
const classes = useUtilityClasses(ownerState);
|
|
31
|
+
const classes = useUtilityClasses(rootProps);
|
|
35
32
|
const filteredDescendantCountLookup = useGridSelector(apiRef, gridFilteredDescendantCountLookupSelector);
|
|
36
33
|
const filteredDescendantCount = filteredDescendantCountLookup[rowNode.id] ?? 0;
|
|
37
34
|
const Icon = rowNode.childrenExpanded ? rootProps.slots.treeDataCollapseIcon : rootProps.slots.treeDataExpandIcon;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function getKey(params) {
|
|
2
|
+
return JSON.stringify([params.paginationModel, params.filterModel, params.sortModel, params.groupKeys]);
|
|
3
|
+
}
|
|
4
|
+
export class GridDataSourceCacheDefault {
|
|
5
|
+
constructor({
|
|
6
|
+
ttl = 300000
|
|
7
|
+
}) {
|
|
8
|
+
this.cache = void 0;
|
|
9
|
+
this.ttl = void 0;
|
|
10
|
+
this.cache = {};
|
|
11
|
+
this.ttl = ttl;
|
|
12
|
+
}
|
|
13
|
+
set(key, value) {
|
|
14
|
+
const keyString = getKey(key);
|
|
15
|
+
const expiry = Date.now() + this.ttl;
|
|
16
|
+
this.cache[keyString] = {
|
|
17
|
+
value,
|
|
18
|
+
expiry
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
get(key) {
|
|
22
|
+
const keyString = getKey(key);
|
|
23
|
+
const entry = this.cache[keyString];
|
|
24
|
+
if (!entry) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
if (Date.now() > entry.expiry) {
|
|
28
|
+
delete this.cache[keyString];
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return entry.value;
|
|
32
|
+
}
|
|
33
|
+
clear() {
|
|
34
|
+
this.cache = {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
import { gridFilterModelSelector, gridSortModelSelector, gridPaginationModelSelector } from '@mui/x-data-grid';
|
|
3
|
+
import { createSelector } from '@mui/x-data-grid/internals';
|
|
4
|
+
const computeStartEnd = paginationModel => {
|
|
5
|
+
const start = paginationModel.page * paginationModel.pageSize;
|
|
6
|
+
const end = start + paginationModel.pageSize - 1;
|
|
7
|
+
return {
|
|
8
|
+
start,
|
|
9
|
+
end
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export const gridGetRowsParamsSelector = createSelector(gridFilterModelSelector, gridSortModelSelector, gridPaginationModelSelector, (filterModel, sortModel, paginationModel) => {
|
|
13
|
+
return _extends({
|
|
14
|
+
groupKeys: [],
|
|
15
|
+
// TODO: Implement with `rowGrouping`
|
|
16
|
+
groupFields: [],
|
|
17
|
+
paginationModel,
|
|
18
|
+
sortModel,
|
|
19
|
+
filterModel
|
|
20
|
+
}, computeStartEnd(paginationModel));
|
|
21
|
+
});
|
|
22
|
+
export const gridDataSourceStateSelector = state => state.dataSource;
|
|
23
|
+
export const gridDataSourceLoadingSelector = createSelector(gridDataSourceStateSelector, dataSource => dataSource.loading);
|
|
24
|
+
export const gridDataSourceErrorsSelector = createSelector(gridDataSourceStateSelector, dataSource => dataSource.errors);
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import useLazyRef from '@mui/utils/useLazyRef';
|
|
4
|
+
import { useGridApiEventHandler, gridRowsLoadingSelector, useGridApiMethod, useGridSelector } from '@mui/x-data-grid';
|
|
5
|
+
import { gridRowGroupsToFetchSelector } from '@mui/x-data-grid/internals';
|
|
6
|
+
import { gridGetRowsParamsSelector, gridDataSourceErrorsSelector } from './gridDataSourceSelector';
|
|
7
|
+
import { runIfServerMode, NestedDataManager, RequestStatus } from './utils';
|
|
8
|
+
import { GridDataSourceCacheDefault } from './cache';
|
|
9
|
+
const INITIAL_STATE = {
|
|
10
|
+
loading: {},
|
|
11
|
+
errors: {}
|
|
12
|
+
};
|
|
13
|
+
const noopCache = {
|
|
14
|
+
clear: () => {},
|
|
15
|
+
get: () => undefined,
|
|
16
|
+
set: () => {}
|
|
17
|
+
};
|
|
18
|
+
function getCache(cacheProp) {
|
|
19
|
+
if (cacheProp === null) {
|
|
20
|
+
return noopCache;
|
|
21
|
+
}
|
|
22
|
+
return cacheProp ?? new GridDataSourceCacheDefault({});
|
|
23
|
+
}
|
|
24
|
+
export const dataSourceStateInitializer = state => {
|
|
25
|
+
return _extends({}, state, {
|
|
26
|
+
dataSource: INITIAL_STATE
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
export const useGridDataSource = (apiRef, props) => {
|
|
30
|
+
const nestedDataManager = useLazyRef(() => new NestedDataManager(apiRef)).current;
|
|
31
|
+
const groupsToAutoFetch = useGridSelector(apiRef, gridRowGroupsToFetchSelector);
|
|
32
|
+
const scheduledGroups = React.useRef(0);
|
|
33
|
+
const onError = props.unstable_onDataSourceError;
|
|
34
|
+
const [cache, setCache] = React.useState(() => getCache(props.unstable_dataSourceCache));
|
|
35
|
+
const fetchRows = React.useCallback(async parentId => {
|
|
36
|
+
const getRows = props.unstable_dataSource?.getRows;
|
|
37
|
+
if (!getRows) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (parentId) {
|
|
41
|
+
nestedDataManager.queue([parentId]);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
nestedDataManager.clear();
|
|
45
|
+
scheduledGroups.current = 0;
|
|
46
|
+
const dataSourceState = apiRef.current.state.dataSource;
|
|
47
|
+
if (dataSourceState !== INITIAL_STATE) {
|
|
48
|
+
apiRef.current.resetDataSourceState();
|
|
49
|
+
}
|
|
50
|
+
const fetchParams = gridGetRowsParamsSelector(apiRef);
|
|
51
|
+
const cachedData = apiRef.current.unstable_dataSource.cache.get(fetchParams);
|
|
52
|
+
if (cachedData !== undefined) {
|
|
53
|
+
const rows = cachedData.rows;
|
|
54
|
+
apiRef.current.setRows(rows);
|
|
55
|
+
if (cachedData.rowCount) {
|
|
56
|
+
apiRef.current.setRowCount(cachedData.rowCount);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const isLoading = gridRowsLoadingSelector(apiRef);
|
|
61
|
+
if (!isLoading) {
|
|
62
|
+
apiRef.current.setLoading(true);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const getRowsResponse = await getRows(fetchParams);
|
|
66
|
+
apiRef.current.unstable_dataSource.cache.set(fetchParams, getRowsResponse);
|
|
67
|
+
if (getRowsResponse.rowCount) {
|
|
68
|
+
apiRef.current.setRowCount(getRowsResponse.rowCount);
|
|
69
|
+
}
|
|
70
|
+
apiRef.current.setRows(getRowsResponse.rows);
|
|
71
|
+
apiRef.current.setLoading(false);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
apiRef.current.setRows([]);
|
|
74
|
+
apiRef.current.setLoading(false);
|
|
75
|
+
onError?.(error, fetchParams);
|
|
76
|
+
}
|
|
77
|
+
}, [nestedDataManager, apiRef, props.unstable_dataSource?.getRows, onError]);
|
|
78
|
+
const fetchRowChildren = React.useCallback(async id => {
|
|
79
|
+
if (!props.treeData) {
|
|
80
|
+
nestedDataManager.clearPendingRequest(id);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const getRows = props.unstable_dataSource?.getRows;
|
|
84
|
+
if (!getRows) {
|
|
85
|
+
nestedDataManager.clearPendingRequest(id);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const rowNode = apiRef.current.getRowNode(id);
|
|
89
|
+
if (!rowNode) {
|
|
90
|
+
nestedDataManager.clearPendingRequest(id);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const fetchParams = _extends({}, gridGetRowsParamsSelector(apiRef), {
|
|
94
|
+
groupKeys: rowNode.path
|
|
95
|
+
});
|
|
96
|
+
const cachedData = apiRef.current.unstable_dataSource.cache.get(fetchParams);
|
|
97
|
+
if (cachedData !== undefined) {
|
|
98
|
+
const rows = cachedData.rows;
|
|
99
|
+
nestedDataManager.setRequestSettled(id);
|
|
100
|
+
apiRef.current.updateServerRows(rows, rowNode.path);
|
|
101
|
+
if (cachedData.rowCount) {
|
|
102
|
+
apiRef.current.setRowCount(cachedData.rowCount);
|
|
103
|
+
}
|
|
104
|
+
apiRef.current.setRowChildrenExpansion(id, true);
|
|
105
|
+
apiRef.current.unstable_dataSource.setChildrenLoading(id, false);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const existingError = gridDataSourceErrorsSelector(apiRef)[id] ?? null;
|
|
109
|
+
if (existingError) {
|
|
110
|
+
apiRef.current.unstable_dataSource.setChildrenFetchError(id, null);
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const getRowsResponse = await getRows(fetchParams);
|
|
114
|
+
if (!apiRef.current.getRowNode(id)) {
|
|
115
|
+
// The row has been removed from the grid
|
|
116
|
+
nestedDataManager.clearPendingRequest(id);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (nestedDataManager.getRequestStatus(id) === RequestStatus.UNKNOWN) {
|
|
120
|
+
apiRef.current.unstable_dataSource.setChildrenLoading(id, false);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
nestedDataManager.setRequestSettled(id);
|
|
124
|
+
apiRef.current.unstable_dataSource.cache.set(fetchParams, getRowsResponse);
|
|
125
|
+
if (getRowsResponse.rowCount) {
|
|
126
|
+
apiRef.current.setRowCount(getRowsResponse.rowCount);
|
|
127
|
+
}
|
|
128
|
+
apiRef.current.updateServerRows(getRowsResponse.rows, rowNode.path);
|
|
129
|
+
apiRef.current.setRowChildrenExpansion(id, true);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const e = error;
|
|
132
|
+
apiRef.current.unstable_dataSource.setChildrenFetchError(id, e);
|
|
133
|
+
onError?.(e, fetchParams);
|
|
134
|
+
} finally {
|
|
135
|
+
apiRef.current.unstable_dataSource.setChildrenLoading(id, false);
|
|
136
|
+
nestedDataManager.setRequestSettled(id);
|
|
137
|
+
}
|
|
138
|
+
}, [nestedDataManager, onError, apiRef, props.treeData, props.unstable_dataSource?.getRows]);
|
|
139
|
+
const setChildrenLoading = React.useCallback((parentId, isLoading) => {
|
|
140
|
+
apiRef.current.setState(state => {
|
|
141
|
+
if (!state.dataSource.loading[parentId] && isLoading === false) {
|
|
142
|
+
return state;
|
|
143
|
+
}
|
|
144
|
+
const newLoadingState = _extends({}, state.dataSource.loading);
|
|
145
|
+
if (isLoading === false) {
|
|
146
|
+
delete newLoadingState[parentId];
|
|
147
|
+
} else {
|
|
148
|
+
newLoadingState[parentId] = isLoading;
|
|
149
|
+
}
|
|
150
|
+
return _extends({}, state, {
|
|
151
|
+
dataSource: _extends({}, state.dataSource, {
|
|
152
|
+
loading: newLoadingState
|
|
153
|
+
})
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}, [apiRef]);
|
|
157
|
+
const setChildrenFetchError = React.useCallback((parentId, error) => {
|
|
158
|
+
apiRef.current.setState(state => {
|
|
159
|
+
const newErrorsState = _extends({}, state.dataSource.errors);
|
|
160
|
+
if (error === null && newErrorsState[parentId] !== undefined) {
|
|
161
|
+
delete newErrorsState[parentId];
|
|
162
|
+
} else {
|
|
163
|
+
newErrorsState[parentId] = error;
|
|
164
|
+
}
|
|
165
|
+
return _extends({}, state, {
|
|
166
|
+
dataSource: _extends({}, state.dataSource, {
|
|
167
|
+
errors: newErrorsState
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}, [apiRef]);
|
|
172
|
+
const resetDataSourceState = React.useCallback(() => {
|
|
173
|
+
apiRef.current.setState(state => {
|
|
174
|
+
return _extends({}, state, {
|
|
175
|
+
dataSource: INITIAL_STATE
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}, [apiRef]);
|
|
179
|
+
const dataSourceApi = {
|
|
180
|
+
unstable_dataSource: {
|
|
181
|
+
setChildrenLoading,
|
|
182
|
+
setChildrenFetchError,
|
|
183
|
+
fetchRows,
|
|
184
|
+
cache
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const dataSourcePrivateApi = {
|
|
188
|
+
fetchRowChildren,
|
|
189
|
+
resetDataSourceState
|
|
190
|
+
};
|
|
191
|
+
useGridApiMethod(apiRef, dataSourceApi, 'public');
|
|
192
|
+
useGridApiMethod(apiRef, dataSourcePrivateApi, 'private');
|
|
193
|
+
useGridApiEventHandler(apiRef, 'sortModelChange', runIfServerMode(props.sortingMode, fetchRows));
|
|
194
|
+
useGridApiEventHandler(apiRef, 'filterModelChange', runIfServerMode(props.filterMode, fetchRows));
|
|
195
|
+
useGridApiEventHandler(apiRef, 'paginationModelChange', runIfServerMode(props.paginationMode, fetchRows));
|
|
196
|
+
const isFirstRender = React.useRef(true);
|
|
197
|
+
React.useEffect(() => {
|
|
198
|
+
if (isFirstRender.current) {
|
|
199
|
+
isFirstRender.current = false;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const newCache = getCache(props.unstable_dataSourceCache);
|
|
203
|
+
setCache(prevCache => prevCache !== newCache ? newCache : prevCache);
|
|
204
|
+
}, [props.unstable_dataSourceCache]);
|
|
205
|
+
React.useEffect(() => {
|
|
206
|
+
if (props.unstable_dataSource) {
|
|
207
|
+
apiRef.current.unstable_dataSource.cache.clear();
|
|
208
|
+
apiRef.current.unstable_dataSource.fetchRows();
|
|
209
|
+
}
|
|
210
|
+
}, [apiRef, props.unstable_dataSource]);
|
|
211
|
+
React.useEffect(() => {
|
|
212
|
+
if (groupsToAutoFetch && groupsToAutoFetch.length && scheduledGroups.current < groupsToAutoFetch.length) {
|
|
213
|
+
const groupsToSchedule = groupsToAutoFetch.slice(scheduledGroups.current);
|
|
214
|
+
nestedDataManager.queue(groupsToSchedule);
|
|
215
|
+
scheduledGroups.current = groupsToAutoFetch.length;
|
|
216
|
+
}
|
|
217
|
+
}, [apiRef, nestedDataManager, groupsToAutoFetch]);
|
|
218
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
const MAX_CONCURRENT_REQUESTS = Infinity;
|
|
3
|
+
export const runIfServerMode = (modeProp, fn) => () => {
|
|
4
|
+
if (modeProp === 'server') {
|
|
5
|
+
fn();
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
export let RequestStatus = /*#__PURE__*/function (RequestStatus) {
|
|
9
|
+
RequestStatus[RequestStatus["QUEUED"] = 0] = "QUEUED";
|
|
10
|
+
RequestStatus[RequestStatus["PENDING"] = 1] = "PENDING";
|
|
11
|
+
RequestStatus[RequestStatus["SETTLED"] = 2] = "SETTLED";
|
|
12
|
+
RequestStatus[RequestStatus["UNKNOWN"] = 3] = "UNKNOWN";
|
|
13
|
+
return RequestStatus;
|
|
14
|
+
}({});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fetches row children from the server with option to limit the number of concurrent requests
|
|
18
|
+
* Determines the status of a request based on the enum `RequestStatus`
|
|
19
|
+
* Uses `GridRowId` to uniquely identify a request
|
|
20
|
+
*/
|
|
21
|
+
export class NestedDataManager {
|
|
22
|
+
constructor(privateApiRef, maxConcurrentRequests = MAX_CONCURRENT_REQUESTS) {
|
|
23
|
+
this.pendingRequests = new Set();
|
|
24
|
+
this.queuedRequests = new Set();
|
|
25
|
+
this.settledRequests = new Set();
|
|
26
|
+
this.api = void 0;
|
|
27
|
+
this.maxConcurrentRequests = void 0;
|
|
28
|
+
this.processQueue = async () => {
|
|
29
|
+
if (this.queuedRequests.size === 0 || this.pendingRequests.size >= this.maxConcurrentRequests) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const loopLength = Math.min(this.maxConcurrentRequests - this.pendingRequests.size, this.queuedRequests.size);
|
|
33
|
+
if (loopLength === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const fetchQueue = Array.from(this.queuedRequests);
|
|
37
|
+
for (let i = 0; i < loopLength; i += 1) {
|
|
38
|
+
const id = fetchQueue[i];
|
|
39
|
+
this.queuedRequests.delete(id);
|
|
40
|
+
this.pendingRequests.add(id);
|
|
41
|
+
this.api.fetchRowChildren(id);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
this.queue = async ids => {
|
|
45
|
+
const loadingIds = {};
|
|
46
|
+
ids.forEach(id => {
|
|
47
|
+
this.queuedRequests.add(id);
|
|
48
|
+
loadingIds[id] = true;
|
|
49
|
+
});
|
|
50
|
+
this.api.setState(state => _extends({}, state, {
|
|
51
|
+
dataSource: _extends({}, state.dataSource, {
|
|
52
|
+
loading: _extends({}, state.dataSource.loading, loadingIds)
|
|
53
|
+
})
|
|
54
|
+
}));
|
|
55
|
+
this.processQueue();
|
|
56
|
+
};
|
|
57
|
+
this.setRequestSettled = id => {
|
|
58
|
+
this.pendingRequests.delete(id);
|
|
59
|
+
this.settledRequests.add(id);
|
|
60
|
+
this.processQueue();
|
|
61
|
+
};
|
|
62
|
+
this.clear = () => {
|
|
63
|
+
this.queuedRequests.clear();
|
|
64
|
+
Array.from(this.pendingRequests).forEach(id => this.clearPendingRequest(id));
|
|
65
|
+
};
|
|
66
|
+
this.clearPendingRequest = id => {
|
|
67
|
+
this.api.unstable_dataSource.setChildrenLoading(id, false);
|
|
68
|
+
this.pendingRequests.delete(id);
|
|
69
|
+
this.processQueue();
|
|
70
|
+
};
|
|
71
|
+
this.getRequestStatus = id => {
|
|
72
|
+
if (this.pendingRequests.has(id)) {
|
|
73
|
+
return RequestStatus.PENDING;
|
|
74
|
+
}
|
|
75
|
+
if (this.queuedRequests.has(id)) {
|
|
76
|
+
return RequestStatus.QUEUED;
|
|
77
|
+
}
|
|
78
|
+
if (this.settledRequests.has(id)) {
|
|
79
|
+
return RequestStatus.SETTLED;
|
|
80
|
+
}
|
|
81
|
+
return RequestStatus.UNKNOWN;
|
|
82
|
+
};
|
|
83
|
+
this.getActiveRequestsCount = () => this.pendingRequests.size + this.queuedRequests.size;
|
|
84
|
+
this.api = privateApiRef.current;
|
|
85
|
+
this.maxConcurrentRequests = maxConcurrentRequests;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -4,4 +4,6 @@ export * from './columnReorder';
|
|
|
4
4
|
export * from './rowReorder';
|
|
5
5
|
export * from './treeData';
|
|
6
6
|
export * from './detailPanel';
|
|
7
|
-
export * from './rowPinning';
|
|
7
|
+
export * from './rowPinning';
|
|
8
|
+
export * from './dataSource/interfaces';
|
|
9
|
+
export * from './dataSource/cache';
|