@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.
Files changed (111) hide show
  1. package/CHANGELOG.md +100 -1
  2. package/DataGridPro/DataGridPro.js +13 -1
  3. package/DataGridPro/useDataGridProComponent.js +6 -1
  4. package/DataGridPro/useDataGridProProps.js +10 -3
  5. package/components/GridDataSourceTreeDataGroupingCell.d.ts +12 -0
  6. package/components/GridDataSourceTreeDataGroupingCell.js +120 -0
  7. package/components/GridTreeDataGroupingCell.js +1 -4
  8. package/esm/DataGridPro/DataGridPro.js +13 -1
  9. package/esm/DataGridPro/useDataGridProComponent.js +6 -1
  10. package/esm/DataGridPro/useDataGridProProps.js +9 -3
  11. package/esm/components/GridDataSourceTreeDataGroupingCell.js +111 -0
  12. package/esm/components/GridTreeDataGroupingCell.js +1 -4
  13. package/esm/hooks/features/dataSource/cache.js +36 -0
  14. package/esm/hooks/features/dataSource/gridDataSourceSelector.js +24 -0
  15. package/esm/hooks/features/dataSource/useGridDataSource.js +218 -0
  16. package/esm/hooks/features/dataSource/utils.js +87 -0
  17. package/esm/hooks/features/index.js +3 -1
  18. package/esm/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.js +148 -0
  19. package/esm/hooks/features/serverSideTreeData/utils.js +18 -0
  20. package/esm/hooks/features/treeData/useGridTreeData.js +6 -2
  21. package/esm/hooks/features/treeData/useGridTreeDataPreProcessors.js +6 -3
  22. package/esm/internals/index.js +2 -0
  23. package/esm/internals/propValidation.js +1 -1
  24. package/esm/models/index.js +2 -1
  25. package/esm/utils/releaseInfo.js +1 -1
  26. package/esm/utils/tree/createRowTree.js +6 -2
  27. package/esm/utils/tree/insertDataRowInTree.js +34 -10
  28. package/esm/utils/tree/updateRowTree.js +13 -5
  29. package/esm/utils/tree/utils.js +5 -1
  30. package/hooks/features/columnHeaders/useGridColumnHeaders.d.ts +1 -1
  31. package/hooks/features/columnPinning/useGridColumnPinning.d.ts +1 -1
  32. package/hooks/features/columnReorder/useGridColumnReorder.d.ts +1 -1
  33. package/hooks/features/dataSource/cache.d.ts +18 -0
  34. package/hooks/features/dataSource/cache.js +43 -0
  35. package/hooks/features/dataSource/gridDataSourceSelector.d.ts +14 -0
  36. package/hooks/features/dataSource/gridDataSourceSelector.js +32 -0
  37. package/hooks/features/dataSource/interfaces.d.ts +50 -0
  38. package/hooks/features/dataSource/useGridDataSource.d.ts +6 -0
  39. package/hooks/features/dataSource/useGridDataSource.js +229 -0
  40. package/hooks/features/dataSource/utils.d.ts +29 -0
  41. package/hooks/features/dataSource/utils.js +95 -0
  42. package/hooks/features/detailPanel/gridDetailPanelSelector.d.ts +0 -1
  43. package/hooks/features/detailPanel/useGridDetailPanel.d.ts +1 -1
  44. package/hooks/features/index.d.ts +2 -0
  45. package/hooks/features/index.js +22 -0
  46. package/hooks/features/infiniteLoader/useGridInfiniteLoader.d.ts +1 -1
  47. package/hooks/features/lazyLoader/useGridLazyLoader.d.ts +1 -1
  48. package/hooks/features/lazyLoader/useGridLazyLoaderPreProcessors.d.ts +1 -1
  49. package/hooks/features/rowPinning/useGridRowPinning.d.ts +1 -1
  50. package/hooks/features/rowPinning/useGridRowPinningPreProcessors.d.ts +2 -2
  51. package/hooks/features/rowReorder/useGridRowReorder.d.ts +1 -1
  52. package/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.d.ts +4 -0
  53. package/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.js +158 -0
  54. package/hooks/features/serverSideTreeData/utils.d.ts +6 -0
  55. package/hooks/features/serverSideTreeData/utils.js +25 -0
  56. package/hooks/features/treeData/gridTreeDataUtils.d.ts +1 -2
  57. package/hooks/features/treeData/useGridTreeData.d.ts +2 -1
  58. package/hooks/features/treeData/useGridTreeData.js +6 -2
  59. package/hooks/features/treeData/useGridTreeDataPreProcessors.d.ts +1 -1
  60. package/hooks/features/treeData/useGridTreeDataPreProcessors.js +6 -3
  61. package/hooks/utils/useGridApiContext.d.ts +0 -1
  62. package/hooks/utils/useGridApiRef.d.ts +0 -1
  63. package/hooks/utils/useGridPrivateApiContext.d.ts +0 -1
  64. package/index.js +1 -1
  65. package/internals/index.d.ts +2 -0
  66. package/internals/index.js +23 -0
  67. package/internals/propValidation.js +1 -1
  68. package/material/index.d.ts +0 -1
  69. package/models/dataGridProProps.d.ts +17 -11
  70. package/models/gridApiPro.d.ts +3 -3
  71. package/models/gridProSlotsComponent.d.ts +0 -1
  72. package/models/gridStatePro.d.ts +2 -0
  73. package/models/index.d.ts +1 -0
  74. package/modern/DataGridPro/DataGridPro.js +13 -1
  75. package/modern/DataGridPro/useDataGridProComponent.js +6 -1
  76. package/modern/DataGridPro/useDataGridProProps.js +9 -3
  77. package/modern/components/GridDataSourceTreeDataGroupingCell.js +111 -0
  78. package/modern/components/GridTreeDataGroupingCell.js +1 -4
  79. package/modern/hooks/features/dataSource/cache.js +36 -0
  80. package/modern/hooks/features/dataSource/gridDataSourceSelector.js +24 -0
  81. package/modern/hooks/features/dataSource/useGridDataSource.js +218 -0
  82. package/modern/hooks/features/dataSource/utils.js +87 -0
  83. package/modern/hooks/features/index.js +3 -1
  84. package/modern/hooks/features/serverSideTreeData/useGridDataSourceTreeDataPreProcessors.js +148 -0
  85. package/modern/hooks/features/serverSideTreeData/utils.js +18 -0
  86. package/modern/hooks/features/treeData/useGridTreeData.js +6 -2
  87. package/modern/hooks/features/treeData/useGridTreeDataPreProcessors.js +6 -3
  88. package/modern/index.js +1 -1
  89. package/modern/internals/index.js +2 -0
  90. package/modern/internals/propValidation.js +1 -1
  91. package/modern/models/index.js +2 -1
  92. package/modern/utils/releaseInfo.js +1 -1
  93. package/modern/utils/tree/createRowTree.js +6 -2
  94. package/modern/utils/tree/insertDataRowInTree.js +34 -10
  95. package/modern/utils/tree/updateRowTree.js +13 -5
  96. package/modern/utils/tree/utils.js +5 -1
  97. package/package.json +3 -3
  98. package/typeOverloads/modules.d.ts +0 -1
  99. package/utils/releaseInfo.js +1 -1
  100. package/utils/tree/createRowTree.js +6 -2
  101. package/utils/tree/insertDataRowInTree.d.ts +3 -1
  102. package/utils/tree/insertDataRowInTree.js +33 -9
  103. package/utils/tree/models.d.ts +1 -0
  104. package/utils/tree/updateRowTree.d.ts +1 -0
  105. package/utils/tree/updateRowTree.js +13 -5
  106. package/utils/tree/utils.d.ts +5 -4
  107. package/utils/tree/utils.js +7 -2
  108. package/models/dataSource.d.ts +0 -64
  109. /package/esm/{models/dataSource.js → hooks/features/dataSource/interfaces.js} +0 -0
  110. /package/{models/dataSource.js → hooks/features/dataSource/interfaces.js} +0 -0
  111. /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
- signature: 'DataGridPro'
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 ownerState = {
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';