diff --git a/docs/data/data-grid/events/events.json b/docs/data/data-grid/events/events.json
index fedd745dd5c45..26bc1b73ed3e6 100644
--- a/docs/data/data-grid/events/events.json
+++ b/docs/data/data-grid/events/events.json
@@ -306,6 +306,13 @@
"params": "ElementSize",
"event": "MuiEvent<{}>"
},
+ {
+ "projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
+ "name": "rootMount",
+ "description": "Fired when rootElementRef.current becomes available.",
+ "params": "HTMLElement",
+ "event": "MuiEvent<{}>"
+ },
{
"projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
"name": "rowClick",
diff --git a/docs/data/data-grid/filtering-recipes/FilteredRowCount.js b/docs/data/data-grid/filtering-recipes/FilteredRowCount.js
index ddfcfdee406c8..7d633e0838567 100644
--- a/docs/data/data-grid/filtering-recipes/FilteredRowCount.js
+++ b/docs/data/data-grid/filtering-recipes/FilteredRowCount.js
@@ -45,15 +45,14 @@ export default function FilteredRowCount() {
const getFilteredRowsCount = React.useCallback(
(filterModel) => {
+ const rowIds = apiRef.current?.getAllRowIds();
const filterState = apiRef.current?.getFilterState(filterModel);
- if (!filterState) {
+ if (!rowIds || !filterState) {
return 0;
}
const { filteredRowsLookup } = filterState;
- return Object.keys(filteredRowsLookup).filter(
- (rowId) => filteredRowsLookup[rowId] === true,
- ).length;
+ return rowIds.filter((rowId) => filteredRowsLookup[rowId] !== false).length;
},
[apiRef],
);
diff --git a/docs/data/data-grid/filtering-recipes/FilteredRowCount.tsx b/docs/data/data-grid/filtering-recipes/FilteredRowCount.tsx
index 156860ec70fbe..9e80a5c1af8c6 100644
--- a/docs/data/data-grid/filtering-recipes/FilteredRowCount.tsx
+++ b/docs/data/data-grid/filtering-recipes/FilteredRowCount.tsx
@@ -45,15 +45,14 @@ export default function FilteredRowCount() {
const getFilteredRowsCount = React.useCallback(
(filterModel: GridFilterModel) => {
+ const rowIds = apiRef.current?.getAllRowIds();
const filterState = apiRef.current?.getFilterState(filterModel);
- if (!filterState) {
+ if (!rowIds || !filterState) {
return 0;
}
const { filteredRowsLookup } = filterState;
- return Object.keys(filteredRowsLookup).filter(
- (rowId) => filteredRowsLookup[rowId] === true,
- ).length;
+ return rowIds.filter((rowId) => filteredRowsLookup[rowId] !== false).length;
},
[apiRef],
);
diff --git a/docs/data/migration/migration-data-grid-v7/migration-data-grid-v7.md b/docs/data/migration/migration-data-grid-v7/migration-data-grid-v7.md
index 47f7db8eb4acf..bd7e499f4701a 100644
--- a/docs/data/migration/migration-data-grid-v7/migration-data-grid-v7.md
+++ b/docs/data/migration/migration-data-grid-v7/migration-data-grid-v7.md
@@ -106,6 +106,23 @@ Below are described the steps you need to make to migrate from v7 to v8.
+const output = useGridSelector(apiRef, selector, arguments, equals);
```
+- The `filteredRowsLookup` object of the filter state does not contain `true` values anymore. If the row is filtered out, the value is `false`. Otherwise, the row id is not present in the object.
+ This change only impacts you if you relied on `filteredRowsLookup` to get ids of filtered rows. In this case,use `gridDataRowIdsSelector` selector to get row ids and check `filteredRowsLookup` for `false` values:
+
+ ```diff
+ const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
+ -const filteredRowIds = Object.keys(filteredRowsLookup).filter((rowId) => filteredRowsLookup[rowId] === true);
+ +const rowIds = gridDataRowIdsSelector(apiRef);
+ +const filteredRowIds = rowIds.filter((rowId) => filteredRowsLookup[rowId] !== false);
+ ```
+
+- The `visibleRowsLookup` state does not contain `true` values anymore. If the row is not visible, the value is `false`. Otherwise, the row id is not present in the object:
+ ```diff
+ const visibleRowsLookup = gridVisibleRowsLookupSelector(apiRef);
+ -const isRowVisible = visibleRowsLookup[rowId] === true;
+ +const isRowVisible = visibleRowsLookup[rowId] !== false;
+ ```
+
### Other exports
- `ariaV8` experimental flag is removed. It's now the default behavior.
diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx
index 5fa2f15565440..c470e57eebff7 100644
--- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx
+++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx
@@ -29,6 +29,7 @@ const configuration = {
},
};
const releaseInfo = getReleaseInfo();
+const watermark = ;
let dataGridPremiumPropValidators: PropValidator[];
@@ -47,6 +48,7 @@ const DataGridPremiumRaw = forwardRef(function DataGridPremium
-
+ {watermark}
);
diff --git a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx
index c11e362499975..0c3780fe00484 100644
--- a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx
+++ b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx
@@ -122,7 +122,6 @@ export const useDataGridPremiumComponent = (
/**
* Register all state initializers here.
*/
- useGridInitializeState(dimensionsStateInitializer, apiRef, props);
useGridInitializeState(headerFilteringStateInitializer, apiRef, props);
useGridInitializeState(rowGroupingStateInitializer, apiRef, props);
useGridInitializeState(aggregationStateInitializer, apiRef, props);
@@ -143,11 +142,12 @@ export const useDataGridPremiumComponent = (
useGridInitializeState(densityStateInitializer, apiRef, props);
useGridInitializeState(columnReorderStateInitializer, apiRef, props);
useGridInitializeState(columnResizeStateInitializer, apiRef, props);
- useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(columnMenuStateInitializer, apiRef, props);
useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
useGridInitializeState(virtualizationStateInitializer, apiRef, props);
useGridInitializeState(dataSourceStateInitializer, apiRef, props);
+ useGridInitializeState(dimensionsStateInitializer, apiRef, props);
+ useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(listViewStateInitializer, apiRef, props);
useGridRowGrouping(apiRef, props);
diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts
index 13b735821ae17..f387e249b33be 100644
--- a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts
+++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts
@@ -69,7 +69,6 @@ export const useGridAggregation = (
const currentModel = gridAggregationModelSelector(apiRef);
if (currentModel !== model) {
apiRef.current.setState(mergeStateWithAggregationModel(model));
- apiRef.current.forceUpdate();
}
},
[apiRef],
@@ -147,7 +146,6 @@ export const useGridAggregation = (
// Re-apply the column hydration to wrap / unwrap the aggregated columns
if (!areAggregationRulesEqual(rulesOnLastColumnHydration, aggregationRules)) {
- apiRef.current.caches.aggregation.rulesOnLastColumnHydration = aggregationRules;
apiRef.current.requestPipeProcessorsApplication('hydrateColumns');
}
}, [
diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx
index 5681f34bf113d..57fc19a1ee4ba 100644
--- a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx
+++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx
@@ -73,7 +73,6 @@ export const useGridAggregationPreProcessors = (
});
rulesOnLastColumnHydration.current = aggregationRules;
-
apiRef.current.caches.aggregation.rulesOnLastColumnHydration = aggregationRules;
return columnsState;
diff --git a/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts b/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts
index a81e42578523f..b131c184ac0aa 100644
--- a/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts
+++ b/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts
@@ -10,7 +10,6 @@ import {
isNavigationKey,
serializeCellValue,
useGridRegisterPipeProcessor,
- useGridVisibleRows,
} from '@mui/x-data-grid-pro/internals';
import {
useGridApiEventHandler,
@@ -26,7 +25,6 @@ import {
gridFocusCellSelector,
GridCellParams,
GRID_REORDER_COL_DEF,
- useGridSelector,
gridSortedRowIdsSelector,
gridDimensionsSelector,
} from '@mui/x-data-grid-pro';
@@ -64,13 +62,10 @@ export const useGridCellSelection = (
>,
) => {
const hasRootReference = apiRef.current.rootElementRef.current !== null;
- const visibleRows = useGridVisibleRows(apiRef, props);
const cellWithVirtualFocus = React.useRef(null);
const lastMouseDownCell = React.useRef(null);
const mousePosition = React.useRef<{ x: number; y: number }>(null);
const autoScrollRAF = React.useRef(null);
- const sortedRowIds = useGridSelector(apiRef, gridSortedRowIdsSelector);
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
const totalHeaderHeight = getTotalHeaderHeight(apiRef, props);
const ignoreValueFormatterProp = props.ignoreValueFormatterDuringExport;
@@ -145,6 +140,7 @@ export const useGridCellSelection = (
}
const visibleColumns = apiRef.current.getVisibleColumns();
+ const visibleRows = getVisibleRows(apiRef);
const rowsInRange = visibleRows.rows.slice(finalStartRowIndex, finalEndRowIndex + 1);
const columnsInRange = visibleColumns.slice(finalStartColumnIndex, finalEndColumnIndex + 1);
@@ -161,7 +157,7 @@ export const useGridCellSelection = (
apiRef.current.setCellSelectionModel(newModel);
},
- [apiRef, visibleRows.rows],
+ [apiRef],
);
const getSelectedCellsAsArray = React.useCallback<
@@ -292,6 +288,8 @@ export const useGridCellSelection = (
return;
}
+ const dimensions = gridDimensionsSelector(apiRef.current.state);
+
const { x: mouseX, y: mouseY } = mousePosition.current;
const { width, height: viewportOuterHeight } = dimensions.viewportOuterSize;
const height = viewportOuterHeight - totalHeaderHeight;
@@ -331,7 +329,7 @@ export const useGridCellSelection = (
}
autoScroll();
- }, [apiRef, dimensions, totalHeaderHeight]);
+ }, [apiRef, totalHeaderHeight]);
const handleCellMouseOver = React.useCallback>(
(params, event) => {
@@ -354,6 +352,7 @@ export const useGridCellSelection = (
return;
}
+ const dimensions = gridDimensionsSelector(apiRef.current.state);
const { x, y } = virtualScrollerRect;
const { width, height: viewportOuterHeight } = dimensions.viewportOuterSize;
const height = viewportOuterHeight - totalHeaderHeight;
@@ -378,7 +377,7 @@ export const useGridCellSelection = (
stopAutoScroll();
}
},
- [apiRef, startAutoScroll, stopAutoScroll, totalHeaderHeight, dimensions],
+ [apiRef, startAutoScroll, stopAutoScroll, totalHeaderHeight],
);
const handleCellClick = useEventCallback<
@@ -438,6 +437,7 @@ export const useGridCellSelection = (
endColumnIndex -= 1;
}
+ const visibleRows = getVisibleRows(apiRef);
if (endRowIndex < 0 || endRowIndex >= visibleRows.rows.length) {
return;
}
@@ -490,6 +490,7 @@ export const useGridCellSelection = (
const addClassesToCells = React.useCallback>(
(classes, { id, field }) => {
+ const visibleRows = getVisibleRows(apiRef);
if (!visibleRows.range || !apiRef.current.isCellSelected(id, field)) {
return classes;
}
@@ -538,7 +539,7 @@ export const useGridCellSelection = (
return newClasses;
},
- [apiRef, visibleRows.range, visibleRows.rows],
+ [apiRef],
);
const canUpdateFocus = React.useCallback>(
@@ -566,6 +567,7 @@ export const useGridCellSelection = (
if (apiRef.current.getSelectedCellsAsArray().length <= 1) {
return value;
}
+ const sortedRowIds = gridSortedRowIdsSelector(apiRef.current.state);
const cellSelectionModel = apiRef.current.getCellSelectionModel();
const unsortedSelectedRowIds = Object.keys(cellSelectionModel);
const sortedSelectedRowIds = sortedRowIds.filter((id) =>
@@ -594,7 +596,7 @@ export const useGridCellSelection = (
}, '');
return copyData;
},
- [apiRef, ignoreValueFormatter, clipboardCopyCellDelimiter, sortedRowIds],
+ [apiRef, ignoreValueFormatter, clipboardCopyCellDelimiter],
);
useGridRegisterPipeProcessor(apiRef, 'isCellSelected', checkIfCellIsSelected);
diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts
index 7e1c99b5f4e2f..846aba5dcfa53 100644
--- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts
+++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts
@@ -1,6 +1,5 @@
import { RefObject } from '@mui/x-internals/types';
import {
- GridRowId,
GridRowTreeConfig,
GridFilterState,
GridFilterModel,
@@ -80,9 +79,9 @@ export const filterRowTreeFromGroupingColumns = (
params: FilterRowTreeFromTreeDataParams,
): Omit => {
const { apiRef, rowTree, isRowMatchingFilters, filterModel } = params;
- const filteredRowsLookup: Record = {};
- const filteredChildrenCountLookup: Record = {};
- const filteredDescendantCountLookup: Record = {};
+ const filteredRowsLookup: GridFilterState['filteredRowsLookup'] = {};
+ const filteredChildrenCountLookup: GridFilterState['filteredChildrenCountLookup'] = {};
+ const filteredDescendantCountLookup: GridFilterState['filteredDescendantCountLookup'] = {};
const filterCache = {};
const filterTreeNode = (
@@ -142,7 +141,9 @@ export const filterRowTreeFromGroupingColumns = (
}
}
- filteredRowsLookup[node.id] = isPassingFiltering;
+ if (!isPassingFiltering) {
+ filteredRowsLookup[node.id] = false;
+ }
if (!isPassingFiltering) {
return 0;
diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
index 37d8c05460ed9..b5e5db188f596 100644
--- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
+++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
@@ -22,6 +22,7 @@ const configuration = {
},
};
const releaseInfo = getReleaseInfo();
+const watermark = ;
const DataGridProRaw = forwardRef(function DataGridPro(
inProps: DataGridProProps,
@@ -34,6 +35,7 @@ const DataGridProRaw = forwardRef(function DataGridPro
-
+ {watermark}
);
diff --git a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx
index 27b1a5bed4d07..929cbbfeec89b 100644
--- a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx
+++ b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx
@@ -110,7 +110,6 @@ export const useDataGridProComponent = (
/**
* Register all state initializers here.
*/
- useGridInitializeState(dimensionsStateInitializer, apiRef, props);
useGridInitializeState(headerFilteringStateInitializer, apiRef, props);
useGridInitializeState(rowSelectionStateInitializer, apiRef, props);
useGridInitializeState(detailPanelStateInitializer, apiRef, props);
@@ -128,11 +127,12 @@ export const useDataGridProComponent = (
useGridInitializeState(densityStateInitializer, apiRef, props);
useGridInitializeState(columnReorderStateInitializer, apiRef, props);
useGridInitializeState(columnResizeStateInitializer, apiRef, props);
- useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(columnMenuStateInitializer, apiRef, props);
useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
useGridInitializeState(virtualizationStateInitializer, apiRef, props);
useGridInitializeState(dataSourceStateInitializer, apiRef, props);
+ useGridInitializeState(dimensionsStateInitializer, apiRef, props);
+ useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(listViewStateInitializer, apiRef, props);
useGridHeaderFiltering(apiRef, props);
diff --git a/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx b/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx
index 04dfb4ccd17c5..7a7451f6520f9 100644
--- a/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx
+++ b/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx
@@ -5,7 +5,6 @@ import { getDataGridUtilityClass, gridClasses, useGridSelector } from '@mui/x-da
import {
GridPinnedRowsProps,
gridPinnedRowsSelector,
- gridRenderContextSelector,
useGridPrivateApiContext,
} from '@mui/x-data-grid/internals';
@@ -20,7 +19,6 @@ export function GridPinnedRows({ position, virtualScroller }: GridPinnedRowsProp
const classes = useUtilityClasses();
const apiRef = useGridPrivateApiContext();
- const renderContext = useGridSelector(apiRef, gridRenderContextSelector);
const pinnedRowsData = useGridSelector(apiRef, gridPinnedRowsSelector);
const rows = pinnedRowsData[position];
@@ -28,10 +26,10 @@ export function GridPinnedRows({ position, virtualScroller }: GridPinnedRowsProp
() => ({
firstRowIndex: 0,
lastRowIndex: rows.length,
- firstColumnIndex: renderContext.firstColumnIndex,
- lastColumnIndex: renderContext.lastColumnIndex,
+ firstColumnIndex: -1,
+ lastColumnIndex: -1,
}),
- [rows, renderContext.firstColumnIndex, renderContext.lastColumnIndex],
+ [rows],
);
if (rows.length === 0) {
diff --git a/packages/x-data-grid-pro/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx b/packages/x-data-grid-pro/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
index 66b8d90ac05f5..71a016ae27b5b 100644
--- a/packages/x-data-grid-pro/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
+++ b/packages/x-data-grid-pro/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
@@ -6,9 +6,12 @@ import {
gridTabIndexColumnHeaderFilterSelector,
getDataGridUtilityClass,
GridFilterItem,
- gridDimensionsSelector,
} from '@mui/x-data-grid';
import {
+ gridColumnsTotalWidthSelector,
+ gridHasFillerSelector,
+ gridHeaderFilterHeightSelector,
+ gridVerticalScrollbarWidthSelector,
useGridColumnHeaders as useGridColumnHeadersCommunity,
UseGridColumnHeadersProps,
GetHeadersParams,
@@ -69,9 +72,11 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
const rootProps = useGridRootProps();
const classes = useUtilityClasses(rootProps);
const disableHeaderFiltering = !rootProps.headerFilters;
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
const filterModel = useGridSelector(apiRef, gridFilterModelSelector);
- const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width;
+ const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector);
+ const gridHasFiller = useGridSelector(apiRef, gridHasFillerSelector);
+ const headerFilterHeight = useGridSelector(apiRef, gridHeaderFilterHeightSelector);
+ const scrollbarWidth = useGridSelector(apiRef, gridVerticalScrollbarWidthSelector);
const columnHeaderFilterFocus = useGridSelector(apiRef, gridFocusColumnHeaderFilterSelector);
@@ -120,13 +125,12 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
const item = getFilterItem(colDef);
const pinnedPosition = params?.position;
- const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0;
const pinnedOffset = getPinnedCellOffset(
pinnedPosition,
colDef.computedWidth,
columnIndex,
columnPositions,
- dimensions.columnsTotalWidth,
+ columnsTotalWidth,
scrollbarWidth,
);
@@ -146,7 +150,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
{
+ if (!props.getDetailPanelContent) {
+ return;
+ }
apiRef.current.setState((state) => {
return {
...state,
@@ -248,16 +245,15 @@ export const useGridDetailPanel = (
},
};
});
- apiRef.current.updateDimensions?.();
apiRef.current.forceUpdate();
}, [apiRef, props.getDetailPanelContent, props.getDetailPanelHeight]);
useGridApiEventHandler(apiRef, 'sortedRowsSet', updateCachesAndForceUpdate);
const previousGetDetailPanelContentProp =
- React.useRef(null);
+ React.useRef(undefined);
const previousGetDetailPanelHeightProp =
- React.useRef(null);
+ React.useRef(undefined);
const updateCachesIfNeeded = React.useCallback(() => {
if (
@@ -281,7 +277,6 @@ export const useGridDetailPanel = (
},
};
});
- apiRef.current.updateDimensions?.();
previousGetDetailPanelContentProp.current = props.getDetailPanelContent;
previousGetDetailPanelHeightProp.current = props.getDetailPanelHeight;
@@ -304,7 +299,8 @@ export const useGridDetailPanel = (
[apiRef, expandedRowIds, updateCachesIfNeeded],
);
- useGridRegisterPipeProcessor(apiRef, 'rowHeight', addDetailHeight);
+ const enabled = props.getDetailPanelContent !== undefined;
+ useGridRegisterPipeProcessor(apiRef, 'rowHeight', addDetailHeight, enabled);
const isFirstRender = React.useRef(true);
if (isFirstRender.current) {
@@ -313,7 +309,6 @@ export const useGridDetailPanel = (
React.useEffect(() => {
if (!isFirstRender.current) {
updateCachesIfNeeded();
- apiRef.current.hydrateRowsMeta();
}
isFirstRender.current = false;
}, [apiRef, updateCachesIfNeeded]);
diff --git a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx
index 61a5fabda0729..ae1b6f11d06ea 100644
--- a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx
+++ b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx
@@ -5,12 +5,12 @@ import {
useGridApiOptionHandler,
gridVisibleColumnDefinitionsSelector,
useGridApiMethod,
- gridDimensionsSelector,
} from '@mui/x-data-grid';
import {
useGridVisibleRows,
GridInfiniteLoaderPrivateApi,
useTimeout,
+ gridHorizontalScrollbarHeightSelector,
} from '@mui/x-data-grid/internals';
import useEventCallback from '@mui/utils/useEventCallback';
import { styled } from '@mui/system';
@@ -64,10 +64,6 @@ export const useGridInfiniteLoader = (
});
const virtualScroller = apiRef.current.virtualScrollerRef.current;
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
-
- const marginBottom =
- props.scrollEndThreshold - (dimensions.hasScrollX ? dimensions.scrollbarSize : 0);
React.useEffect(() => {
if (!isEnabled) {
@@ -78,6 +74,9 @@ export const useGridInfiniteLoader = (
}
observer.current?.disconnect();
+ const horizontalScrollbarHeight = gridHorizontalScrollbarHeightSelector(apiRef.current.state);
+ const marginBottom = props.scrollEndThreshold - horizontalScrollbarHeight;
+
observer.current = new IntersectionObserver(handleLoadMoreRows, {
threshold: 1,
root: virtualScroller,
@@ -86,7 +85,7 @@ export const useGridInfiniteLoader = (
if (triggerElement.current) {
observer.current.observe(triggerElement.current);
}
- }, [virtualScroller, handleLoadMoreRows, isEnabled, marginBottom]);
+ }, [apiRef, virtualScroller, handleLoadMoreRows, isEnabled, props.scrollEndThreshold]);
const updateTarget = (node: HTMLElement | null) => {
if (triggerElement.current !== node) {
diff --git a/packages/x-data-grid-pro/src/hooks/features/serverSideTreeData/utils.ts b/packages/x-data-grid-pro/src/hooks/features/serverSideTreeData/utils.ts
index 677f7210c9274..f319a36f07ef6 100644
--- a/packages/x-data-grid-pro/src/hooks/features/serverSideTreeData/utils.ts
+++ b/packages/x-data-grid-pro/src/hooks/features/serverSideTreeData/utils.ts
@@ -1,22 +1,19 @@
import { GridRowId, GridRowTreeConfig, GRID_ROOT_GROUP_ID } from '@mui/x-data-grid';
-import { getTreeNodeDescendants } from '@mui/x-data-grid/internals';
+import { defaultGridFilterLookup, getTreeNodeDescendants } from '@mui/x-data-grid/internals';
export function skipFiltering(rowTree: GridRowTreeConfig) {
- const filteredRowsLookup: Record = {};
const filteredChildrenCountLookup: Record = {};
- const filteredDescendantCountLookup: Record = {};
const nodes = Object.values(rowTree);
for (let i = 0; i < nodes.length; i += 1) {
const node: any = nodes[i];
- filteredRowsLookup[node.id] = true;
filteredChildrenCountLookup[node.id] = node.serverChildrenCount ?? 0;
}
return {
- filteredRowsLookup,
+ filteredRowsLookup: defaultGridFilterLookup.filteredRowsLookup,
filteredChildrenCountLookup,
- filteredDescendantCountLookup,
+ filteredDescendantCountLookup: defaultGridFilterLookup.filteredDescendantCountLookup,
};
}
diff --git a/packages/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts b/packages/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts
index d3318e53583e5..cbaaee4732b45 100644
--- a/packages/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts
+++ b/packages/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts
@@ -1,6 +1,5 @@
import { RefObject } from '@mui/x-internals/types';
import {
- GridRowId,
GridRowTreeConfig,
GridTreeNode,
GridFilterState,
@@ -35,9 +34,9 @@ export const filterRowTreeFromTreeData = (
params: FilterRowTreeFromTreeDataParams,
): Omit => {
const { apiRef, rowTree, disableChildrenFiltering, isRowMatchingFilters } = params;
- const filteredRowsLookup: Record = {};
- const filteredChildrenCountLookup: Record = {};
- const filteredDescendantCountLookup: Record = {};
+ const filteredRowsLookup: GridFilterState['filteredRowsLookup'] = {};
+ const filteredChildrenCountLookup: GridFilterState['filteredChildrenCountLookup'] = {};
+ const filteredDescendantCountLookup: GridFilterState['filteredDescendantCountLookup'] = {};
const filterCache = {};
const filterResults: GridAggregatedFilterItemApplierResult = {
@@ -103,7 +102,9 @@ export const filterRowTreeFromTreeData = (
}
}
- filteredRowsLookup[node.id] = shouldPassFilters;
+ if (!shouldPassFilters) {
+ filteredRowsLookup[node.id] = false;
+ }
if (!shouldPassFilters) {
return 0;
diff --git a/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx
index b674feba8ef9e..3ed9018d5dc53 100644
--- a/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx
+++ b/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx
@@ -20,7 +20,7 @@ import {
act,
reactMajor,
} from '@mui/internal-test-utils';
-import { $, $$, grid, getRow, getCell, getColumnValues, microtasks } from 'test/utils/helperFn';
+import { $, $$, grid, getRow, getCell, getColumnValues } from 'test/utils/helperFn';
import { fireUserEvent } from 'test/utils/fireUserEvent';
import { testSkipIf, isJSDOM } from 'test/utils/skipIf';
@@ -83,7 +83,7 @@ describe(' - Detail panel', () => {
async () => {
const rowHeight = 50;
const detailPanelHeight = 100;
- render(
+ const { user } = render(
- Detail panel', () => {
getDetailPanelHeight={() => 'auto'}
/>,
);
- fireEvent.click(screen.getAllByRole('button', { name: 'Expand' })[0]);
- await microtasks();
+ await user.click(screen.getAllByRole('button', { name: 'Expand' })[0]);
+ await waitFor(() =>
+ expect(getRow(0).className).to.include(gridClasses['row--detailPanelExpanded']),
+ );
const virtualScrollerContent = $('.MuiDataGrid-virtualScrollerContent')!;
expect(virtualScrollerContent).toHaveComputedStyle({
@@ -133,7 +135,9 @@ describe(' - Detail panel', () => {
const virtualScrollerContent = grid('virtualScrollerContent')!;
await user.click(screen.getByRole('button', { name: 'Expand' }));
- expect(getRow(0).className).to.include(gridClasses['row--detailPanelExpanded']);
+ await waitFor(() =>
+ expect(getRow(0).className).to.include(gridClasses['row--detailPanelExpanded']),
+ );
expect(virtualScrollerContent).toHaveComputedStyle({ height: `${rowHeight + 100}px` });
expect(virtualScrollerContent).toHaveInlineStyle({ width: 'auto' });
diff --git a/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx
index 14a6c258a284c..d910de0a61907 100644
--- a/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx
+++ b/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx
@@ -331,8 +331,11 @@ describe(' - Events params', () => {
// Needs layout
testSkipIf(isJSDOM)(
- 'publishing GRID_ROWS_SCROLL should call onFetchRows callback when rows lazy loading is enabled',
- () => {
+ 'lazy loaded grid should load the rest of the rows when mounted when virtualization is disabled',
+ function test() {
+ if (isJSDOM) {
+ this.skip(); // Needs layout
+ }
const handleFetchRows = spy();
render(
- Events params', () => {
rowCount={50}
/>,
);
- act(() => apiRef.current?.publishEvent('scrollPositionChange', { left: 0, top: 3 * 52 }));
expect(handleFetchRows.callCount).to.equal(1);
+ expect(handleFetchRows.lastCall.firstArg).to.contain({
+ firstRowToRender: 3,
+ lastRowToRender: 50,
+ });
},
);
+ it('publishing renderedRowsIntervalChange should call onFetchRows callback when rows lazy loading is enabled', () => {
+ const handleFetchRows = spy();
+ render(
+ ,
+ );
+ // Since rowheight < viewport height, onmount calls fetchRows directly
+ expect(handleFetchRows.callCount).to.equal(1);
+ act(() => {
+ apiRef.current?.publishEvent('renderedRowsIntervalChange', {
+ firstRowIndex: 3,
+ lastRowIndex: 10,
+ firstColumnIndex: 0,
+ lastColumnIndex: 0,
+ });
+ });
+ expect(handleFetchRows.callCount).to.equal(2);
+ expect(handleFetchRows.lastCall.firstArg).to.contain({
+ firstRowToRender: 3,
+ lastRowToRender: 10,
+ });
+ });
+
it('should publish "unmount" event when unmounting the Grid', () => {
const onUnmount = spy();
diff --git a/packages/x-data-grid-pro/src/utils/tree/utils.ts b/packages/x-data-grid-pro/src/utils/tree/utils.ts
index fba2f4c9bfa23..0e79df66cc14c 100644
--- a/packages/x-data-grid-pro/src/utils/tree/utils.ts
+++ b/packages/x-data-grid-pro/src/utils/tree/utils.ts
@@ -16,6 +16,7 @@ import {
} from '@mui/x-data-grid/internals';
import { RowTreeBuilderGroupingCriterion } from './models';
import { DataGridProProps } from '../../models/dataGridProProps';
+import type { GridStatePro } from '../../models/gridStatePro';
export const getGroupRowIdFromPath = (path: RowTreeBuilderGroupingCriterion[]) => {
const pathStr = path
@@ -240,10 +241,10 @@ export const getVisibleRowsLookup = ({
return {};
}
- const visibleRowsLookup: Record = {};
+ const visibleRowsLookup: GridStatePro['visibleRowsLookup'] = {};
const handleTreeNode = (node: GridTreeNode, areAncestorsExpanded: boolean) => {
- const isPassingFiltering = filteredRowsLookup[node.id];
+ const isPassingFiltering = filteredRowsLookup[node.id] !== false;
if (node.type === 'group') {
node.children.forEach((childId) => {
@@ -252,12 +253,17 @@ export const getVisibleRowsLookup = ({
});
}
- visibleRowsLookup[node.id] = isPassingFiltering && areAncestorsExpanded;
+ const isVisible = isPassingFiltering && areAncestorsExpanded;
+ if (!isVisible) {
+ visibleRowsLookup[node.id] = isVisible;
+ }
// TODO rows v6: Should we keep storing the visibility status of footer independently or rely on the group visibility in the selector ?
if (node.type === 'group' && node.footerId != null) {
- visibleRowsLookup[node.footerId] =
- isPassingFiltering && areAncestorsExpanded && !!node.childrenExpanded;
+ const isFooterVisible = isPassingFiltering && areAncestorsExpanded && !!node.childrenExpanded;
+ if (!isFooterVisible) {
+ visibleRowsLookup[node.footerId] = isFooterVisible;
+ }
}
};
diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json
index 2ca267257f5d4..96587886b8c67 100644
--- a/packages/x-data-grid/package.json
+++ b/packages/x-data-grid/package.json
@@ -52,7 +52,8 @@
"@mui/x-internals": "workspace:*",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
- "reselect": "^5.1.1"
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
@@ -76,6 +77,7 @@
"@mui/system": "^5.16.14",
"@mui/types": "^7.2.20",
"@types/prop-types": "^15.7.14",
+ "@types/use-sync-external-store": "^0.0.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rimraf": "^6.0.1"
diff --git a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx
index 11bd616909589..2c135ec83bcea 100644
--- a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx
+++ b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx
@@ -81,7 +81,6 @@ export const useDataGridComponent = (
/**
* Register all state initializers here.
*/
- useGridInitializeState(dimensionsStateInitializer, apiRef, props);
useGridInitializeState(rowSelectionStateInitializer, apiRef, props);
useGridInitializeState(columnsStateInitializer, apiRef, props);
useGridInitializeState(paginationStateInitializer, apiRef, props);
@@ -94,10 +93,11 @@ export const useDataGridComponent = (
useGridInitializeState(rowSpanningStateInitializer, apiRef, props);
useGridInitializeState(densityStateInitializer, apiRef, props);
useGridInitializeState(columnResizeStateInitializer, apiRef, props);
- useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(columnMenuStateInitializer, apiRef, props);
useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
useGridInitializeState(virtualizationStateInitializer, apiRef, props);
+ useGridInitializeState(dimensionsStateInitializer, apiRef, props);
+ useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(listViewStateInitializer, apiRef, props);
useGridKeyboardNavigation(apiRef, props);
diff --git a/packages/x-data-grid/src/components/GridRow.tsx b/packages/x-data-grid/src/components/GridRow.tsx
index 4f2730916d9f2..da359f417d686 100644
--- a/packages/x-data-grid/src/components/GridRow.tsx
+++ b/packages/x-data-grid/src/components/GridRow.tsx
@@ -126,7 +126,10 @@ const GridRow = forwardRef(function GridRow(props,
);
const handleRef = useForkRef(ref, refProp);
const rowNode = apiRef.current.getRowNode(rowId);
- const editing = useGridSelector(apiRef, gridRowIsEditingSelector, rowId);
+ const editing = useGridSelector(apiRef, gridRowIsEditingSelector, {
+ rowId,
+ editMode: rootProps.editMode,
+ });
const editable = rootProps.editMode === GridEditModes.Row;
const hasFocusCell = focusedColumnIndex !== undefined;
const hasVirtualFocusCellLeft =
@@ -473,7 +476,9 @@ const GridRow = forwardRef(function GridRow(props,
{cells}
{rightCells}
- {scrollbarWidth !== 0 && 0} />}
+ {scrollbarWidth !== 0 && (
+ 0} borderTop={!isFirstVisible} />
+ )}
);
});
diff --git a/packages/x-data-grid/src/components/GridScrollArea.tsx b/packages/x-data-grid/src/components/GridScrollArea.tsx
index 268a29a0572d1..af98a1bd0c2d3 100644
--- a/packages/x-data-grid/src/components/GridScrollArea.tsx
+++ b/packages/x-data-grid/src/components/GridScrollArea.tsx
@@ -7,25 +7,30 @@ import {
} from '@mui/utils';
import { styled } from '@mui/system';
import { fastMemo } from '@mui/x-internals/fastMemo';
+import { RefObject } from '@mui/x-internals/types';
import { DataGridProcessedProps } from '../models/props/DataGridProps';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
import { getDataGridUtilityClass, gridClasses } from '../constants';
import { useGridApiContext } from '../hooks/utils/useGridApiContext';
import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler';
import { useGridSelector } from '../hooks/utils/useGridSelector';
-import { gridDimensionsSelector } from '../hooks/features/dimensions/gridDimensionsSelectors';
+import {
+ gridDimensionsSelector,
+ gridColumnsTotalWidthSelector,
+} from '../hooks/features/dimensions/gridDimensionsSelectors';
import { gridDensityFactorSelector } from '../hooks/features/density/densitySelector';
-import { gridColumnsTotalWidthSelector } from '../hooks/features/columns/gridColumnsSelector';
import { GridScrollParams } from '../models/params/gridScrollParams';
import { GridEventListener } from '../models/events';
import { useTimeout } from '../hooks/utils/useTimeout';
import { getTotalHeaderHeight } from '../hooks/features/columns/gridColumnsUtils';
+import { createSelector } from '../utils/createSelector';
const CLIFF = 1;
const SLOP = 1.5;
interface ScrollAreaProps {
scrollDirection: 'left' | 'right';
+ scrollPosition: RefObject;
}
type OwnerState = DataGridProcessedProps & Pick;
@@ -62,21 +67,44 @@ const GridScrollAreaRawRoot = styled('div', {
},
}));
-function GridScrollAreaRaw(props: ScrollAreaProps) {
- const { scrollDirection } = props;
+const offsetSelector = createSelector(
+ gridDimensionsSelector,
+ (dimensions, direction: ScrollAreaProps['scrollDirection']) => {
+ if (direction === 'left') {
+ return dimensions.leftPinnedWidth;
+ }
+ if (direction === 'right') {
+ return dimensions.rightPinnedWidth + (dimensions.hasScrollX ? dimensions.scrollbarSize : 0);
+ }
+ return 0;
+ },
+);
+
+function GridScrollAreaWrapper(props: ScrollAreaProps) {
+ const apiRef = useGridApiContext();
+ const [dragging, setDragging] = React.useState(false);
+
+ useGridApiEventHandler(apiRef, 'columnHeaderDragStart', () => setDragging(true));
+ useGridApiEventHandler(apiRef, 'columnHeaderDragEnd', () => setDragging(false));
+
+ if (!dragging) {
+ return null;
+ }
+
+ return ;
+}
+
+function GridScrollAreaContent(props: ScrollAreaProps) {
+ const { scrollDirection, scrollPosition } = props;
const rootRef = React.useRef(null);
const apiRef = useGridApiContext();
const timeout = useTimeout();
const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector);
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
-
- const scrollPosition = React.useRef({
- left: 0,
- top: 0,
- });
+ const sideOffset = useGridSelector(apiRef, offsetSelector, scrollDirection);
const getCanScrollMore = () => {
+ const dimensions = gridDimensionsSelector(apiRef.current.state);
if (scrollDirection === 'left') {
// Only render if the user has not reached yet the start of the list
return scrollPosition.current.left > 0;
@@ -91,7 +119,6 @@ function GridScrollAreaRaw(props: ScrollAreaProps) {
return false;
};
- const [dragging, setDragging] = React.useState(false);
const [canScrollMore, setCanScrollMore] = React.useState(getCanScrollMore);
const rootProps = useGridRootProps();
@@ -106,15 +133,12 @@ function GridScrollAreaRaw(props: ScrollAreaProps) {
};
if (scrollDirection === 'left') {
- style.left = dimensions.leftPinnedWidth;
+ style.left = sideOffset;
} else if (scrollDirection === 'right') {
- style.right =
- dimensions.rightPinnedWidth + (dimensions.hasScrollX ? dimensions.scrollbarSize : 0);
+ style.right = sideOffset;
}
- const handleScrolling: GridEventListener<'scrollPositionChange'> = (newScrollPosition) => {
- scrollPosition.current = newScrollPosition;
-
+ const handleScrolling: GridEventListener<'scrollPositionChange'> = () => {
setCanScrollMore(getCanScrollMore);
};
@@ -143,19 +167,9 @@ function GridScrollAreaRaw(props: ScrollAreaProps) {
});
});
- const handleColumnHeaderDragStart = useEventCallback(() => {
- setDragging(true);
- });
-
- const handleColumnHeaderDragEnd = useEventCallback(() => {
- setDragging(false);
- });
-
useGridApiEventHandler(apiRef, 'scrollPositionChange', handleScrolling);
- useGridApiEventHandler(apiRef, 'columnHeaderDragStart', handleColumnHeaderDragStart);
- useGridApiEventHandler(apiRef, 'columnHeaderDragEnd', handleColumnHeaderDragEnd);
- if (!dragging || !canScrollMore) {
+ if (!canScrollMore) {
return null;
}
@@ -170,4 +184,4 @@ function GridScrollAreaRaw(props: ScrollAreaProps) {
);
}
-export const GridScrollArea = fastMemo(GridScrollAreaRaw);
+export const GridScrollArea = fastMemo(GridScrollAreaWrapper);
diff --git a/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx b/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx
index b17a03a468401..db9651d1dbd95 100644
--- a/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx
+++ b/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx
@@ -9,7 +9,6 @@ import { useGridApiContext } from '../hooks/utils/useGridApiContext';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
import {
gridColumnPositionsSelector,
- gridColumnsTotalWidthSelector,
gridDimensionsSelector,
gridVisibleColumnDefinitionsSelector,
gridVisiblePinnedColumnDefinitionsSelector,
@@ -17,6 +16,7 @@ import {
useGridSelector,
} from '../hooks';
import { PinnedColumnPosition } from '../internals/constants';
+import { gridColumnsTotalWidthSelector } from '../hooks/features/dimensions/gridDimensionsSelectors';
import { GridEventListener } from '../models';
import { DataGridProcessedProps } from '../models/props/DataGridProps';
import { getDataGridUtilityClass, gridClasses } from '../constants/gridClasses';
diff --git a/packages/x-data-grid/src/components/containers/GridRoot.tsx b/packages/x-data-grid/src/components/containers/GridRoot.tsx
index 24fe8d5afe876..42cfbd9d949db 100644
--- a/packages/x-data-grid/src/components/containers/GridRoot.tsx
+++ b/packages/x-data-grid/src/components/containers/GridRoot.tsx
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import clsx from 'clsx';
import {
unstable_useForkRef as useForkRef,
- unstable_useEnhancedEffect as useEnhancedEffect,
unstable_capitalize as capitalize,
unstable_composeClasses as composeClasses,
} from '@mui/utils';
@@ -19,6 +18,7 @@ import { getDataGridUtilityClass } from '../../constants/gridClasses';
import { gridDensitySelector } from '../../hooks/features/density/densitySelector';
import { DataGridProcessedProps } from '../../models/props/DataGridProps';
import { GridDensity } from '../../models/gridDensity';
+import { useIsSSR } from '../../hooks/utils/useIsSSR';
import { GridHeader } from '../GridHeader';
import { GridBody, GridFooterPlaceholder } from '../base';
@@ -54,19 +54,26 @@ const GridRoot = forwardRef(function GridRoot(pro
const apiRef = useGridPrivateApiContext();
const density = useGridSelector(apiRef, gridDensitySelector);
const rootElementRef = apiRef.current.rootElementRef;
- const handleRef = useForkRef(rootElementRef, ref);
+
+ const rootMountCallback = React.useCallback(
+ (node: HTMLElement | null) => {
+ if (node === null) {
+ return;
+ }
+ apiRef.current.publishEvent('rootMount', node);
+ },
+ [apiRef],
+ );
+
+ const handleRef = useForkRef(rootElementRef, ref, rootMountCallback);
const ownerState = rootProps;
const classes = useUtilityClasses(ownerState, density);
- // Our implementation of
- const [mountedState, setMountedState] = React.useState(false);
- useEnhancedEffect(() => {
- setMountedState(true);
- }, []);
+ const isSSR = useIsSSR();
- if (!mountedState) {
+ if (isSSR) {
return null;
}
diff --git a/packages/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/x-data-grid/src/components/containers/GridRootStyles.ts
index c98994048cfee..98249c2f005ea 100644
--- a/packages/x-data-grid/src/components/containers/GridRootStyles.ts
+++ b/packages/x-data-grid/src/components/containers/GridRootStyles.ts
@@ -13,7 +13,7 @@ import { gridClasses as c } from '../../constants/gridClasses';
import { DataGridProcessedProps } from '../../models/props/DataGridProps';
import { useGridSelector } from '../../hooks/utils/useGridSelector';
import { useGridPrivateApiContext } from '../../hooks/utils/useGridPrivateApiContext';
-import { gridDimensionsSelector } from '../../hooks/features/dimensions/gridDimensionsSelectors';
+import { GridStateCommunity } from '../../models/gridStateCommunity';
export type OwnerState = DataGridProcessedProps;
@@ -54,6 +54,10 @@ const separatorIconDragStyles = {
const ignoreSsrWarning =
'/* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */';
+const shouldShowBorderTopRightRadiusSelector = (state: GridStateCommunity) =>
+ state.dimensions.hasScrollX &&
+ (!state.dimensions.hasScrollY || state.dimensions.scrollbarSize === 0);
+
export const GridRootStyles = styled('div', {
name: 'MuiDataGrid',
slot: 'Root',
@@ -179,7 +183,10 @@ export const GridRootStyles = styled('div', {
],
})<{ ownerState: OwnerState }>(({ theme: t }) => {
const apiRef = useGridPrivateApiContext();
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
+ const shouldShowBorderTopRightRadius = useGridSelector(
+ apiRef,
+ shouldShowBorderTopRightRadiusSelector,
+ );
const borderColor = getBorderColor(t);
const radius = t.shape.borderRadius;
@@ -367,10 +374,9 @@ export const GridRootStyles = styled('div', {
borderTopLeftRadius: 'calc(var(--unstable_DataGrid-radius) - 1px)',
},
[`&.${c['root--noToolbar']} [aria-rowindex="1"] .${c['columnHeader--last']}`]: {
- borderTopRightRadius:
- dimensions.hasScrollX && (!dimensions.hasScrollY || dimensions.scrollbarSize === 0)
- ? 'calc(var(--unstable_DataGrid-radius) - 1px)'
- : undefined,
+ borderTopRightRadius: shouldShowBorderTopRightRadius
+ ? 'calc(var(--unstable_DataGrid-radius) - 1px)'
+ : undefined,
},
[`& .${c.columnHeaderCheckbox}, & .${c.cellCheckbox}`]: {
padding: 0,
diff --git a/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx b/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx
index be3e3aadabbb8..e09227daa4e17 100644
--- a/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx
+++ b/packages/x-data-grid/src/components/virtualization/GridMainContainer.tsx
@@ -4,7 +4,6 @@ import { forwardRef } from '@mui/x-internals/forwardRef';
import { DataGridProcessedProps } from '../../models/props/DataGridProps';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import { useGridConfiguration } from '../../hooks/utils/useGridConfiguration';
-import { GridDimensions } from '../../hooks/features/dimensions';
import { GridLoadingOverlayVariant } from '../GridLoadingOverlay';
const GridPanelAnchor = styled('div')({
@@ -15,7 +14,8 @@ const GridPanelAnchor = styled('div')({
});
type OwnerState = Pick & {
- dimensions: GridDimensions;
+ hasScrollX: boolean;
+ hasPinnedRight: boolean;
loadingOverlayVariant: GridLoadingOverlayVariant | null;
};
@@ -26,7 +26,7 @@ const Element = styled('div', {
const { ownerState } = props;
return [
styles.main,
- ownerState.dimensions.rightPinnedWidth > 0 && styles['main--hasPinnedRight'],
+ ownerState.hasPinnedRight && styles['main--hasPinnedRight'],
ownerState.loadingOverlayVariant === 'skeleton' && styles['main--hasSkeletonLoadingOverlay'],
];
},
diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx
index 4063e6bc47ae2..e5f569a880d1c 100644
--- a/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx
+++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx
@@ -1,13 +1,17 @@
import * as React from 'react';
import { styled } from '@mui/system';
import composeClasses from '@mui/utils/composeClasses';
+import {
+ gridHasBottomFillerSelector,
+ gridHasScrollXSelector,
+ gridHasScrollYSelector,
+} from '../../hooks/features/dimensions/gridDimensionsSelectors';
import { GridScrollArea } from '../GridScrollArea';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import { useGridApiContext } from '../../hooks/utils/useGridApiContext';
import { useGridSelector } from '../../hooks/utils/useGridSelector';
import { getDataGridUtilityClass } from '../../constants/gridClasses';
import { DataGridProcessedProps } from '../../models/props/DataGridProps';
-import { GridDimensions, gridDimensionsSelector } from '../../hooks/features/dimensions';
import { useGridVirtualScroller } from '../../hooks/features/virtualization/useGridVirtualScroller';
import { useGridOverlays } from '../../hooks/features/overlays/useGridOverlays';
import { GridHeaders } from '../GridHeaders';
@@ -19,21 +23,23 @@ import { GridVirtualScrollerFiller as SpaceFiller } from './GridVirtualScrollerF
import { GridVirtualScrollerRenderZone as RenderZone } from './GridVirtualScrollerRenderZone';
import { GridVirtualScrollbar as Scrollbar } from './GridVirtualScrollbar';
import { GridLoadingOverlayVariant } from '../GridLoadingOverlay';
+import { GridStateCommunity } from '../../models/gridStateCommunity';
type OwnerState = Pick & {
- dimensions: GridDimensions;
+ hasScrollX: boolean;
+ hasPinnedRight: boolean;
loadingOverlayVariant: GridLoadingOverlayVariant | null;
};
const useUtilityClasses = (ownerState: OwnerState) => {
- const { classes, dimensions, loadingOverlayVariant } = ownerState;
+ const { classes, hasScrollX, hasPinnedRight, loadingOverlayVariant } = ownerState;
const slots = {
root: [
'main',
- dimensions.rightPinnedWidth > 0 && 'main--hasPinnedRight',
+ hasPinnedRight && 'main--hasPinnedRight',
loadingOverlayVariant === 'skeleton' && 'main--hasSkeletonLoadingOverlay',
],
- scroller: ['virtualScroller', dimensions.hasScrollX && 'virtualScroller--hasScrollX'],
+ scroller: ['virtualScroller', hasScrollX && 'virtualScroller--hasScrollX'],
};
return composeClasses(slots, getDataGridUtilityClass, classes);
@@ -44,10 +50,7 @@ const Scroller = styled('div', {
slot: 'VirtualScroller',
overridesResolver: (props, styles) => {
const { ownerState } = props;
- return [
- styles.virtualScroller,
- ownerState.dimensions.hasScrollX && styles['virtualScroller--hasScrollX'],
- ];
+ return [styles.virtualScroller, ownerState.hasScrollX && styles['virtualScroller--hasScrollX']];
},
})<{ ownerState: OwnerState }>({
position: 'relative',
@@ -69,6 +72,8 @@ const Scroller = styled('div', {
zIndex: 0,
});
+const hasPinnedRightSelector = (state: GridStateCommunity) => state.dimensions.rightPinnedWidth > 0;
+
export interface GridVirtualScrollerProps {
children?: React.ReactNode;
}
@@ -76,11 +81,15 @@ export interface GridVirtualScrollerProps {
function GridVirtualScroller(props: GridVirtualScrollerProps) {
const apiRef = useGridApiContext();
const rootProps = useGridRootProps();
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
+ const hasScrollY = useGridSelector(apiRef, gridHasScrollYSelector);
+ const hasScrollX = useGridSelector(apiRef, gridHasScrollXSelector);
+ const hasPinnedRight = useGridSelector(apiRef, hasPinnedRightSelector);
+ const hasBottomFiller = useGridSelector(apiRef, gridHasBottomFillerSelector);
const { getOverlay, overlaysProps } = useGridOverlays();
const ownerState = {
classes: rootProps.classes,
- dimensions,
+ hasScrollX,
+ hasPinnedRight,
loadingOverlayVariant: overlaysProps.loadingOverlayVariant,
};
const classes = useUtilityClasses(ownerState);
@@ -94,14 +103,15 @@ function GridVirtualScroller(props: GridVirtualScrollerProps) {
getScrollbarVerticalProps,
getScrollbarHorizontalProps,
getRows,
+ getScrollAreaProps,
} = virtualScroller;
const rows = getRows();
return (
-
-
+
+
{!rootProps.unstable_listView && }
@@ -117,16 +127,15 @@ function GridVirtualScroller(props: GridVirtualScrollerProps) {
-
-
+ {hasBottomFiller && }
- {dimensions.hasScrollX && !rootProps.unstable_listView && (
+ {hasScrollX && !rootProps.unstable_listView && (
)}
- {dimensions.hasScrollY && }
+ {hasScrollY && }
{props.children}
);
diff --git a/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.ts b/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.ts
index d15fa6077945a..ec918959da3cd 100644
--- a/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.ts
+++ b/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridRegisterPipeProcessor.ts
@@ -11,6 +11,7 @@ export const useGridRegisterPipeProcessor = <
apiRef: RefObject,
group: G,
callback: GridPipeProcessor,
+ enabled: boolean = true,
) => {
const cleanup = React.useRef<(() => void) | null>(null);
const id = React.useRef(`mui-${Math.round(Math.random() * 1e9)}`);
@@ -20,14 +21,16 @@ export const useGridRegisterPipeProcessor = <
}, [apiRef, callback, group]);
useFirstRender(() => {
- registerPreProcessor();
+ if (enabled) {
+ registerPreProcessor();
+ }
});
const isFirstRender = React.useRef(true);
React.useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
- } else {
+ } else if (enabled) {
registerPreProcessor();
}
@@ -37,5 +40,5 @@ export const useGridRegisterPipeProcessor = <
cleanup.current = null;
}
};
- }, [registerPreProcessor]);
+ }, [registerPreProcessor, enabled]);
};
diff --git a/packages/x-data-grid/src/hooks/core/useGridStateInitialization.ts b/packages/x-data-grid/src/hooks/core/useGridStateInitialization.ts
index 63cabbe5aa168..cdf14bbf6c33f 100644
--- a/packages/x-data-grid/src/hooks/core/useGridStateInitialization.ts
+++ b/packages/x-data-grid/src/hooks/core/useGridStateInitialization.ts
@@ -12,8 +12,6 @@ export const useGridStateInitialization = >
>({});
- const [, rawForceUpdate] = React.useState();
-
const registerControlState = React.useCallback<
GridStatePrivateApi['registerControlState']
>((controlStateItem) => {
@@ -118,7 +116,9 @@ export const useGridStateInitialization = rawForceUpdate(() => apiRef.current.state), [apiRef]);
+ const forceUpdate = React.useCallback(() => {
+ // @deprecated - do nothing
+ }, []);
const publicStateApi: Omit, 'state'> = {
setState,
diff --git a/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx b/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
index 30a0ab2e02c1f..ff382271dd348 100644
--- a/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
+++ b/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
@@ -9,7 +9,13 @@ import type { GridColumnsRenderContext } from '../../../models/params/gridScroll
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { GridEventListener } from '../../../models/events';
import { GridColumnHeaderItem } from '../../../components/columnHeaders/GridColumnHeaderItem';
-import { gridDimensionsSelector } from '../dimensions';
+import {
+ gridColumnsTotalWidthSelector,
+ gridGroupHeaderHeightSelector,
+ gridHasFillerSelector,
+ gridHeaderHeightSelector,
+ gridVerticalScrollbarWidthSelector,
+} from '../dimensions/gridDimensionsSelectors';
import { gridRenderContextColumnsSelector } from '../virtualization';
import { computeOffsetLeft } from '../virtualization/useGridVirtualScroller';
import { GridColumnGroupHeader } from '../../../components/columnHeaders/GridColumnGroupHeader';
@@ -99,20 +105,17 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
const apiRef = useGridPrivateApiContext();
const rootProps = useGridRootProps();
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
const columnGroupsModel = useGridSelector(apiRef, gridColumnGroupsUnwrappedModelSelector);
const columnPositions = useGridSelector(apiRef, gridColumnPositionsSelector);
const renderContext = useGridSelector(apiRef, gridRenderContextColumnsSelector);
const pinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector);
const columnsLookup = useGridSelector(apiRef, gridColumnLookupSelector);
const offsetLeft = computeOffsetLeft(columnPositions, renderContext, pinnedColumns.left.length);
- const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width;
-
- React.useEffect(() => {
- if (apiRef.current.columnHeadersContainerRef.current) {
- apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0;
- }
- }, [apiRef]);
+ const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector);
+ const gridHasFiller = useGridSelector(apiRef, gridHasFillerSelector);
+ const headerHeight = useGridSelector(apiRef, gridHeaderHeightSelector);
+ const groupHeaderHeight = useGridSelector(apiRef, gridGroupHeaderHeightSelector);
+ const scrollbarWidth = useGridSelector(apiRef, gridVerticalScrollbarWidthSelector);
const handleColumnResizeStart = React.useCallback>(
(params) => setResizeCol(params.field),
@@ -227,13 +230,13 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
const hasFocus = columnHeaderFocus !== null && columnHeaderFocus.field === colDef.field;
const open = columnMenuState.open && columnMenuState.field === colDef.field;
const pinnedPosition = params?.position;
- const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0;
+
const pinnedOffset = getPinnedCellOffset(
pinnedPosition,
colDef.computedWidth,
columnIndex,
columnPositions,
- dimensions.columnsTotalWidth,
+ columnsTotalWidth,
scrollbarWidth,
);
@@ -268,7 +271,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
filterItemsCounter={
filterColumnLookup[colDef.field] && filterColumnLookup[colDef.field].length
}
- headerHeight={dimensions.headerHeight}
+ headerHeight={headerHeight}
isDragging={colDef.field === dragCol}
colDef={colDef}
colIndex={columnIndex}
@@ -402,13 +405,12 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
};
const pinnedPosition = params.position;
- const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0;
const pinnedOffset = getPinnedCellOffset(
pinnedPosition,
headerInfo.width,
columnIndex,
columnPositions,
- dimensions.columnsTotalWidth,
+ columnsTotalWidth,
scrollbarWidth,
);
@@ -430,7 +432,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
depth={depth}
isLastColumn={index === visibleColumnGroupHeader.length - 1}
maxDepth={headerGroupingMaxDepth}
- height={dimensions.groupHeaderHeight}
+ height={groupHeaderHeight}
hasFocus={hasFocus}
tabIndex={tabIndex}
pinnedPosition={pinnedPosition}
diff --git a/packages/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts b/packages/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts
index f252388bc18f0..7c35559f3c62b 100644
--- a/packages/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts
+++ b/packages/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts
@@ -149,22 +149,6 @@ export const gridColumnPositionsSelector = createSelectorMemoized(
},
);
-/**
- * Get the summed width of all the visible columns.
- * @category Visible Columns
- */
-export const gridColumnsTotalWidthSelector = createSelector(
- gridVisibleColumnDefinitionsSelector,
- gridColumnPositionsSelector,
- (visibleColumns, positions) => {
- const colCount = visibleColumns.length;
- if (colCount === 0) {
- return 0;
- }
- return positions[colCount - 1] + visibleColumns[colCount - 1].computedWidth;
- },
-);
-
/**
* Get the filterable columns as an array.
* @category Columns
diff --git a/packages/x-data-grid/src/hooks/features/dimensions/gridDimensionsSelectors.ts b/packages/x-data-grid/src/hooks/features/dimensions/gridDimensionsSelectors.ts
index 2365ce546492f..b117c7c677595 100644
--- a/packages/x-data-grid/src/hooks/features/dimensions/gridDimensionsSelectors.ts
+++ b/packages/x-data-grid/src/hooks/features/dimensions/gridDimensionsSelectors.ts
@@ -1,3 +1,52 @@
import { GridStateCommunity } from '../../../models/gridStateCommunity';
+import { createSelector } from '../../../utils/createSelector';
export const gridDimensionsSelector = (state: GridStateCommunity) => state.dimensions;
+
+/**
+ * Get the summed width of all the visible columns.
+ * @category Visible Columns
+ */
+export const gridColumnsTotalWidthSelector = createSelector(
+ gridDimensionsSelector,
+ (dimensions) => dimensions.columnsTotalWidth,
+);
+
+export const gridRowHeightSelector = (state: GridStateCommunity) => state.dimensions.rowHeight;
+
+export const gridContentHeightSelector = (state: GridStateCommunity) =>
+ state.dimensions.contentSize.height;
+
+export const gridHasScrollXSelector = (state: GridStateCommunity) => state.dimensions.hasScrollX;
+
+export const gridHasScrollYSelector = (state: GridStateCommunity) => state.dimensions.hasScrollY;
+
+export const gridHasFillerSelector = (state: GridStateCommunity) =>
+ state.dimensions.columnsTotalWidth < state.dimensions.viewportOuterSize.width;
+
+export const gridHeaderHeightSelector = (state: GridStateCommunity) =>
+ state.dimensions.headerHeight;
+
+export const gridGroupHeaderHeightSelector = (state: GridStateCommunity) =>
+ state.dimensions.groupHeaderHeight;
+
+export const gridHeaderFilterHeightSelector = (state: GridStateCommunity) =>
+ state.dimensions.headerFilterHeight;
+
+export const gridVerticalScrollbarWidthSelector = (state: GridStateCommunity) =>
+ state.dimensions.hasScrollY ? state.dimensions.scrollbarSize : 0;
+
+export const gridHorizontalScrollbarHeightSelector = (state: GridStateCommunity) =>
+ state.dimensions.hasScrollX ? state.dimensions.scrollbarSize : 0;
+
+export const gridHasBottomFillerSelector = (state: GridStateCommunity) => {
+ const height = state.dimensions.hasScrollX ? state.dimensions.scrollbarSize : 0;
+ const needsLastRowBorder =
+ state.dimensions.viewportOuterSize.height - state.dimensions.minimumSize.height > 0;
+
+ if (height === 0 && !needsLastRowBorder) {
+ return false;
+ }
+
+ return true;
+};
diff --git a/packages/x-data-grid/src/hooks/features/dimensions/index.ts b/packages/x-data-grid/src/hooks/features/dimensions/index.ts
index bc71eeab845d4..8d50106f7e8b3 100644
--- a/packages/x-data-grid/src/hooks/features/dimensions/index.ts
+++ b/packages/x-data-grid/src/hooks/features/dimensions/index.ts
@@ -1,3 +1,3 @@
export type { GridDimensionsState } from './useGridDimensions';
-export * from './gridDimensionsSelectors';
+export { gridDimensionsSelector, gridColumnsTotalWidthSelector } from './gridDimensionsSelectors';
export type { GridDimensions, GridDimensionsApi } from './gridDimensionsApi';
diff --git a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts
index e37f70936cab9..1843b7bfb1325 100644
--- a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts
+++ b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts
@@ -9,16 +9,15 @@ import { throttle } from '@mui/x-internals/throttle';
import { GridEventListener } from '../../../models/events';
import { ElementSize } from '../../../models';
import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity';
-import {
- useGridApiEventHandler,
- useGridApiOptionHandler,
-} from '../../utils/useGridApiEventHandler';
+import { useGridApiOptionHandler } from '../../utils/useGridApiEventHandler';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
+import { createSelector } from '../../../utils/createSelector';
import { useGridLogger } from '../../utils/useGridLogger';
import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { GridDimensions, GridDimensionsApi, GridDimensionsPrivateApi } from './gridDimensionsApi';
import {
- gridColumnsTotalWidthSelector,
+ gridColumnPositionsSelector,
+ gridVisibleColumnDefinitionsSelector,
gridVisiblePinnedColumnDefinitionsSelector,
} from '../columns';
import { gridDimensionsSelector } from './gridDimensionsSelectors';
@@ -27,16 +26,13 @@ import { gridRenderContextSelector } from '../virtualization';
import { useGridSelector } from '../../utils';
import { getVisibleRows } from '../../utils/useGridVisibleRows';
import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector';
-import {
- calculatePinnedRowsHeight,
- getValidRowHeight,
- rowHeightWarning,
-} from '../rows/gridRowsUtils';
+import { getValidRowHeight, rowHeightWarning } from '../rows/gridRowsUtils';
import { getTotalHeaderHeight } from '../columns/gridColumnsUtils';
import { GridStateInitializer } from '../../utils/useGridInitializeState';
import { DATA_GRID_PROPS_DEFAULT_VALUES } from '../../../constants/dataGridPropsDefaultValues';
import { roundToDecimalPlaces } from '../../../utils/roundToDecimalPlaces';
import { isJSDOM } from '../../../utils/isJSDOM';
+import { isDeepEqual } from '../../../utils/utils';
type RootProps = Pick<
DataGridProcessedProps,
@@ -79,58 +75,80 @@ const EMPTY_DIMENSIONS: GridDimensions = {
bottomContainerHeight: 0,
};
-export const dimensionsStateInitializer: GridStateInitializer = (state) => {
+export const dimensionsStateInitializer: GridStateInitializer = (
+ state,
+ props,
+ apiRef,
+) => {
const dimensions = EMPTY_DIMENSIONS;
+ const density = gridDensityFactorSelector(apiRef);
+
return {
...state,
- dimensions,
+ dimensions: {
+ ...dimensions,
+ ...getStaticDimensions(
+ props,
+ apiRef,
+ density,
+ gridVisiblePinnedColumnDefinitionsSelector(apiRef),
+ ),
+ },
};
};
+const columnsTotalWidthSelector = createSelector(
+ gridVisibleColumnDefinitionsSelector,
+ gridColumnPositionsSelector,
+ (visibleColumns, positions) => {
+ const colCount = visibleColumns.length;
+ if (colCount === 0) {
+ return 0;
+ }
+ return roundToDecimalPlaces(
+ positions[colCount - 1] + visibleColumns[colCount - 1].computedWidth,
+ 1,
+ );
+ },
+);
+
export function useGridDimensions(apiRef: RefObject, props: RootProps) {
const logger = useGridLogger(apiRef, 'useResizeContainer');
const errorShown = React.useRef(false);
const rootDimensionsRef = React.useRef(EMPTY_SIZE);
- const dimensionsState = useGridSelector(apiRef, gridDimensionsSelector);
- const rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector);
const pinnedColumns = useGridSelector(apiRef, gridVisiblePinnedColumnDefinitionsSelector);
const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
- const validRowHeight = React.useMemo(
- () =>
- getValidRowHeight(
- props.rowHeight,
- DATA_GRID_PROPS_DEFAULT_VALUES.rowHeight,
- rowHeightWarning,
- ),
- [props.rowHeight],
- );
- const rowHeight = Math.floor(validRowHeight * densityFactor);
- const headerHeight = Math.floor(props.columnHeaderHeight * densityFactor);
- const groupHeaderHeight = Math.floor(
- (props.columnGroupHeaderHeight ?? props.columnHeaderHeight) * densityFactor,
- );
- const headerFilterHeight = Math.floor(
- (props.headerFilterHeight ?? props.columnHeaderHeight) * densityFactor,
- );
- const columnsTotalWidth = roundToDecimalPlaces(gridColumnsTotalWidthSelector(apiRef), 1);
- const headersTotalHeight = getTotalHeaderHeight(apiRef, props);
+ const columnsTotalWidth = useGridSelector(apiRef, columnsTotalWidthSelector);
+ const isFirstSizing = React.useRef(true);
- const leftPinnedWidth = pinnedColumns.left.reduce((w, col) => w + col.computedWidth, 0);
- const rightPinnedWidth = pinnedColumns.right.reduce((w, col) => w + col.computedWidth, 0);
+ const {
+ rowHeight,
+ headerHeight,
+ groupHeaderHeight,
+ headerFilterHeight,
+ headersTotalHeight,
+ leftPinnedWidth,
+ rightPinnedWidth,
+ } = getStaticDimensions(props, apiRef, densityFactor, pinnedColumns);
- const [savedSize, setSavedSize] = React.useState();
- const debouncedSetSavedSize = React.useMemo(
- () => throttle(setSavedSize, props.resizeThrottleMs),
- [props.resizeThrottleMs],
+ const getRootDimensions = React.useCallback(
+ () => gridDimensionsSelector(apiRef.current.state),
+ [apiRef],
);
- React.useEffect(() => debouncedSetSavedSize.clear, [debouncedSetSavedSize]);
- const getRootDimensions = () => apiRef.current.state.dimensions;
-
- const setDimensions = useEventCallback((dimensions: GridDimensions) => {
- apiRef.current.setState((state) => ({ ...state, dimensions }));
- });
+ const setDimensions = React.useCallback(
+ (dimensions: GridDimensions) => {
+ apiRef.current.setState((state) => ({ ...state, dimensions }));
+ if (apiRef.current.rootElementRef.current) {
+ setCSSVariables(
+ apiRef.current.rootElementRef.current,
+ gridDimensionsSelector(apiRef.current.state),
+ );
+ }
+ },
+ [apiRef],
+ );
const getViewportPageSize = React.useCallback(() => {
const dimensions = gridDimensionsSelector(apiRef.current.state);
@@ -138,10 +156,7 @@ export function useGridDimensions(apiRef: RefObject, pr
return 0;
}
- const currentPage = getVisibleRows(apiRef, {
- pagination: props.pagination,
- paginationMode: props.paginationMode,
- });
+ const currentPage = getVisibleRows(apiRef);
// TODO: Use a combination of scrollTop, dimensions.viewportInnerSize.height and rowsMeta.possitions
// to find out the maximum number of rows that can fit in the visible part of the grid
@@ -157,19 +172,22 @@ export function useGridDimensions(apiRef: RefObject, pr
);
return Math.min(maximumPageSizeWithoutScrollBar, currentPage.rows.length);
- }, [apiRef, props.pagination, props.paginationMode, props.getRowHeight, rowHeight]);
+ }, [apiRef, props.getRowHeight, rowHeight]);
const updateDimensions = React.useCallback(() => {
+ if (isFirstSizing.current) {
+ return;
+ }
// All the floating point dimensions should be rounded to .1 decimal places to avoid subpixel rendering issues
// https://github.com/mui/mui-x/issues/9550#issuecomment-1619020477
// https://github.com/mui/mui-x/issues/15721
const rootElement = apiRef.current.rootElementRef.current;
- const pinnedRowsHeight = calculatePinnedRowsHeight(apiRef);
- const scrollbarSize = measureScrollbarSize(rootElement, columnsTotalWidth, props.scrollbarSize);
+ const scrollbarSize = measureScrollbarSize(rootElement, props.scrollbarSize);
- const topContainerHeight = headersTotalHeight + pinnedRowsHeight.top;
- const bottomContainerHeight = pinnedRowsHeight.bottom;
+ const rowsMeta = gridRowsMetaSelector(apiRef.current.state);
+ const topContainerHeight = headersTotalHeight + rowsMeta.pinnedTopRowsTotalHeight;
+ const bottomContainerHeight = rowsMeta.pinnedBottomRowsTotalHeight;
const contentSize = {
width: columnsTotalWidth,
@@ -261,6 +279,11 @@ export function useGridDimensions(apiRef: RefObject, pr
};
const prevDimensions = apiRef.current.state.dimensions;
+
+ if (isDeepEqual(prevDimensions as any, newDimensions)) {
+ return;
+ }
+
setDimensions(newDimensions);
if (!areElementSizesEqual(newDimensions.viewportInnerSize, prevDimensions.viewportInnerSize)) {
@@ -273,7 +296,6 @@ export function useGridDimensions(apiRef: RefObject, pr
setDimensions,
props.scrollbarSize,
props.autoHeight,
- rowsMeta.currentPageTotalHeight,
rowHeight,
headerHeight,
groupHeaderHeight,
@@ -284,6 +306,19 @@ export function useGridDimensions(apiRef: RefObject, pr
rightPinnedWidth,
]);
+ const updateDimensionCallback = useEventCallback(updateDimensions);
+ const debouncedUpdateDimensions = React.useMemo(
+ () =>
+ props.resizeThrottleMs > 0
+ ? throttle(() => {
+ updateDimensionCallback();
+ apiRef.current.publishEvent('debouncedResize', rootDimensionsRef.current!);
+ }, props.resizeThrottleMs)
+ : undefined,
+ [apiRef, props.resizeThrottleMs, updateDimensionCallback],
+ );
+ React.useEffect(() => debouncedUpdateDimensions?.clear, [debouncedUpdateDimensions]);
+
const apiPublic: GridDimensionsApi = {
getRootDimensions,
};
@@ -293,38 +328,17 @@ export function useGridDimensions(apiRef: RefObject, pr
getViewportPageSize,
};
+ useEnhancedEffect(updateDimensions, [updateDimensions]);
useGridApiMethod(apiRef, apiPublic, 'public');
useGridApiMethod(apiRef, apiPrivate, 'private');
- useEnhancedEffect(() => {
- if (savedSize) {
- updateDimensions();
- apiRef.current.publishEvent('debouncedResize', rootDimensionsRef.current!);
- }
- }, [apiRef, savedSize, updateDimensions]);
-
- const root = apiRef.current.rootElementRef.current;
- useEnhancedEffect(() => {
- if (!root) {
- return;
- }
- const set = (k: string, v: string) => root.style.setProperty(k, v);
- set('--DataGrid-width', `${dimensionsState.viewportOuterSize.width}px`);
- set('--DataGrid-hasScrollX', `${Number(dimensionsState.hasScrollX)}`);
- set('--DataGrid-hasScrollY', `${Number(dimensionsState.hasScrollY)}`);
- set('--DataGrid-scrollbarSize', `${dimensionsState.scrollbarSize}px`);
- set('--DataGrid-rowWidth', `${dimensionsState.rowWidth}px`);
- set('--DataGrid-columnsTotalWidth', `${dimensionsState.columnsTotalWidth}px`);
- set('--DataGrid-leftPinnedWidth', `${dimensionsState.leftPinnedWidth}px`);
- set('--DataGrid-rightPinnedWidth', `${dimensionsState.rightPinnedWidth}px`);
- set('--DataGrid-headerHeight', `${dimensionsState.headerHeight}px`);
- set('--DataGrid-headersTotalHeight', `${dimensionsState.headersTotalHeight}px`);
- set('--DataGrid-topContainerHeight', `${dimensionsState.topContainerHeight}px`);
- set('--DataGrid-bottomContainerHeight', `${dimensionsState.bottomContainerHeight}px`);
- set('--height', `${dimensionsState.rowHeight}px`);
- }, [root, dimensionsState]);
+ const handleRootMount = React.useCallback>(
+ (root) => {
+ setCSSVariables(root, gridDimensionsSelector(apiRef.current.state));
+ },
+ [apiRef],
+ );
- const isFirstSizing = React.useRef(true);
const handleResize = React.useCallback>(
(size) => {
rootDimensionsRef.current = size;
@@ -353,40 +367,82 @@ export function useGridDimensions(apiRef: RefObject, pr
errorShown.current = true;
}
- if (isFirstSizing.current) {
+ if (isFirstSizing.current || !debouncedUpdateDimensions) {
// We want to initialize the grid dimensions as soon as possible to avoid flickering
- setSavedSize(size);
isFirstSizing.current = false;
+ updateDimensions();
return;
}
- debouncedSetSavedSize(size);
+ debouncedUpdateDimensions();
},
- [props.autoHeight, debouncedSetSavedSize, logger],
+ [updateDimensions, props.autoHeight, debouncedUpdateDimensions, logger],
);
- useEnhancedEffect(updateDimensions, [updateDimensions]);
-
- useGridApiOptionHandler(apiRef, 'sortedRowsSet', updateDimensions);
- useGridApiOptionHandler(apiRef, 'paginationModelChange', updateDimensions);
- useGridApiOptionHandler(apiRef, 'columnsChange', updateDimensions);
- useGridApiEventHandler(apiRef, 'resize', handleResize);
+ useGridApiOptionHandler(apiRef, 'rootMount', handleRootMount);
+ useGridApiOptionHandler(apiRef, 'resize', handleResize);
useGridApiOptionHandler(apiRef, 'debouncedResize', props.onResize);
}
-function measureScrollbarSize(
- rootElement: Element | null,
- columnsTotalWidth: number,
- scrollbarSize: number | undefined,
+function setCSSVariables(root: HTMLElement, dimensions: GridDimensions) {
+ const set = (k: string, v: string) => root.style.setProperty(k, v);
+ set('--DataGrid-hasScrollX', `${Number(dimensions.hasScrollX)}`);
+ set('--DataGrid-hasScrollY', `${Number(dimensions.hasScrollY)}`);
+ set('--DataGrid-scrollbarSize', `${dimensions.scrollbarSize}px`);
+ set('--DataGrid-rowWidth', `${dimensions.rowWidth}px`);
+ set('--DataGrid-columnsTotalWidth', `${dimensions.columnsTotalWidth}px`);
+ set('--DataGrid-leftPinnedWidth', `${dimensions.leftPinnedWidth}px`);
+ set('--DataGrid-rightPinnedWidth', `${dimensions.rightPinnedWidth}px`);
+ set('--DataGrid-headerHeight', `${dimensions.headerHeight}px`);
+ set('--DataGrid-headersTotalHeight', `${dimensions.headersTotalHeight}px`);
+ set('--DataGrid-topContainerHeight', `${dimensions.topContainerHeight}px`);
+ set('--DataGrid-bottomContainerHeight', `${dimensions.bottomContainerHeight}px`);
+ set('--height', `${dimensions.rowHeight}px`);
+}
+
+function getStaticDimensions(
+ props: RootProps,
+ apiRef: RefObject,
+ density: number,
+ pinnedColumnns: ReturnType,
) {
+ const validRowHeight = getValidRowHeight(
+ props.rowHeight,
+ DATA_GRID_PROPS_DEFAULT_VALUES.rowHeight,
+ rowHeightWarning,
+ );
+
+ return {
+ rowHeight: Math.floor(validRowHeight * density),
+ headerHeight: Math.floor(props.columnHeaderHeight * density),
+ groupHeaderHeight: Math.floor(
+ (props.columnGroupHeaderHeight ?? props.columnHeaderHeight) * density,
+ ),
+ headerFilterHeight: Math.floor(
+ (props.headerFilterHeight ?? props.columnHeaderHeight) * density,
+ ),
+ columnsTotalWidth: columnsTotalWidthSelector(apiRef),
+ headersTotalHeight: getTotalHeaderHeight(apiRef, props),
+ leftPinnedWidth: pinnedColumnns.left.reduce((w, col) => w + col.computedWidth, 0),
+ rightPinnedWidth: pinnedColumnns.right.reduce((w, col) => w + col.computedWidth, 0),
+ };
+}
+
+const scrollbarSizeCache = new WeakMap();
+function measureScrollbarSize(rootElement: Element | null, scrollbarSize: number | undefined) {
if (scrollbarSize !== undefined) {
return scrollbarSize;
}
- if (rootElement === null || columnsTotalWidth === 0) {
+ if (rootElement === null) {
return 0;
}
+ const cachedSize = scrollbarSizeCache.get(rootElement);
+ if (cachedSize !== undefined) {
+ return cachedSize;
+ }
+
const doc = ownerDocument(rootElement);
const scrollDiv = doc.createElement('div');
scrollDiv.style.width = '99px';
@@ -397,6 +453,9 @@ function measureScrollbarSize(
rootElement.appendChild(scrollDiv);
const size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
rootElement.removeChild(scrollDiv);
+
+ scrollbarSizeCache.set(rootElement, size);
+
return size;
}
diff --git a/packages/x-data-grid/src/hooks/features/editing/gridEditingSelectors.ts b/packages/x-data-grid/src/hooks/features/editing/gridEditingSelectors.ts
index 988fe7f81fd92..564b5cc920d4e 100644
--- a/packages/x-data-grid/src/hooks/features/editing/gridEditingSelectors.ts
+++ b/packages/x-data-grid/src/hooks/features/editing/gridEditingSelectors.ts
@@ -1,6 +1,7 @@
import { createSelector } from '../../../utils/createSelector';
import { GridStateCommunity } from '../../../models/gridStateCommunity';
import { GridRowId } from '../../../models/gridRows';
+import { GridEditModes, GridEditMode } from '../../../models/gridEditRowModel';
/**
* Select the row editing state.
@@ -9,7 +10,8 @@ export const gridEditRowsStateSelector = (state: GridStateCommunity) => state.ed
export const gridRowIsEditingSelector = createSelector(
gridEditRowsStateSelector,
- (editRows, rowId: GridRowId) => Boolean(editRows[rowId]),
+ (editRows, { rowId, editMode }: { rowId: GridRowId; editMode: GridEditMode }) =>
+ editMode === GridEditModes.Row && Boolean(editRows[rowId]),
);
export const gridEditCellStateSelector = createSelector(
diff --git a/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts b/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts
index 62f53ac3ff5a0..1302c4d1cc9f9 100644
--- a/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts
+++ b/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts
@@ -328,10 +328,10 @@ export const useGridRowEditing = (
const getRowMode = React.useCallback(
(id) => {
- if (props.editMode === GridEditModes.Cell) {
- return GridRowModes.View;
- }
- const isEditing = gridRowIsEditingSelector(apiRef.current.state, id);
+ const isEditing = gridRowIsEditingSelector(apiRef.current.state, {
+ rowId: id,
+ editMode: props.editMode,
+ });
return isEditing ? GridRowModes.Edit : GridRowModes.View;
},
[apiRef, props.editMode],
diff --git a/packages/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts b/packages/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts
index 218bb839348ca..559294471f34c 100644
--- a/packages/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts
+++ b/packages/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts
@@ -1,3 +1,4 @@
+import { isObjectEmpty } from '@mui/x-internals/isObjectEmpty';
import { createSelector, createSelectorMemoized } from '../../../utils/createSelector';
import { GridRowId } from '../../../models/gridRows';
import { GridFilterItem } from '../../../models/gridFilterItem';
@@ -70,11 +71,8 @@ export const gridFilteredDescendantCountLookupSelector = createSelector(
export const gridExpandedSortedRowEntriesSelector = createSelectorMemoized(
gridVisibleRowsLookupSelector,
gridSortedRowEntriesSelector,
- gridRowMaximumTreeDepthSelector,
- gridFilterModelSelector,
- gridQuickFilterValuesSelector,
- (visibleRowsLookup, sortedRows, maxDepth, filterModel, quickFilterValues) => {
- if (maxDepth < 2 && !filterModel.items.length && !quickFilterValues?.length) {
+ (visibleRowsLookup, sortedRows) => {
+ if (isObjectEmpty(visibleRowsLookup)) {
return sortedRows;
}
return sortedRows.filter((row) => visibleRowsLookup[row.id] !== false);
@@ -100,7 +98,9 @@ export const gridFilteredSortedRowEntriesSelector = createSelectorMemoized(
gridFilteredRowsLookupSelector,
gridSortedRowEntriesSelector,
(filteredRowsLookup, sortedRows) =>
- sortedRows.filter((row) => filteredRowsLookup[row.id] !== false),
+ isObjectEmpty(filteredRowsLookup)
+ ? sortedRows
+ : sortedRows.filter((row) => filteredRowsLookup[row.id] !== false),
);
/**
diff --git a/packages/x-data-grid/src/hooks/features/filter/gridFilterState.ts b/packages/x-data-grid/src/hooks/features/filter/gridFilterState.ts
index c48354461cf09..4a6fb5d892e67 100644
--- a/packages/x-data-grid/src/hooks/features/filter/gridFilterState.ts
+++ b/packages/x-data-grid/src/hooks/features/filter/gridFilterState.ts
@@ -5,6 +5,12 @@ import { GridRowId, GridValidRowModel } from '../../../models/gridRows';
export type GridFilterItemResult = { [key: Required['id']]: boolean };
export type GridQuickFilterValueResult = { [key: string]: boolean };
+export const defaultGridFilterLookup = {
+ filteredRowsLookup: {},
+ filteredChildrenCountLookup: {},
+ filteredDescendantCountLookup: {},
+};
+
export const getDefaultGridFilterModel: () => GridFilterModel = () => ({
items: [],
logicOperator: GridLogicOperator.And,
@@ -17,10 +23,10 @@ export interface GridFilterState {
/**
* Filtering status for each row.
* A row is filtered if it is passing the filters, whether its parents are expanded or not.
- * If a row is not registered in this lookup, it is filtered.
+ * All the rows are filtered except the ones registered in this lookup with `false` values.
* This is the equivalent of the `visibleRowsLookup` if all the groups were expanded.
*/
- filteredRowsLookup: Record;
+ filteredRowsLookup: Record;
/**
* Amount of children that are passing the filters or have children that are passing the filter (does not count grand children).
* If a row is not registered in this lookup, it is supposed to have no descendant passing the filters.
@@ -67,4 +73,4 @@ export type GridFilteringMethodValue = Omit;
* A row is visible if it is passing the filters AND if its parents are expanded.
* If a row is not registered in this lookup, it is visible.
*/
-export type GridVisibleRowsLookupState = Record;
+export type GridVisibleRowsLookupState = Record;
diff --git a/packages/x-data-grid/src/hooks/features/filter/useGridFilter.tsx b/packages/x-data-grid/src/hooks/features/filter/useGridFilter.tsx
index e0c5edc2c4a81..c07297f02d806 100644
--- a/packages/x-data-grid/src/hooks/features/filter/useGridFilter.tsx
+++ b/packages/x-data-grid/src/hooks/features/filter/useGridFilter.tsx
@@ -15,7 +15,7 @@ import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridColumnLookupSelector } from '../columns/gridColumnsSelector';
import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue';
-import { getDefaultGridFilterModel } from './gridFilterState';
+import { defaultGridFilterLookup, getDefaultGridFilterModel } from './gridFilterState';
import { gridFilterModelSelector } from './gridFilterSelector';
import { useFirstRender } from '../../utils/useFirstRender';
import { gridRowsLookupSelector } from '../rows';
@@ -46,9 +46,7 @@ export const filterStateInitializer: GridStateInitializer<
...state,
filter: {
filterModel: sanitizeFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef),
- filteredRowsLookup: {},
- filteredChildrenCountLookup: {},
- filteredDescendantCountLookup: {},
+ ...defaultGridFilterLookup,
},
visibleRowsLookup: {},
};
@@ -423,12 +421,12 @@ export const useGridFilter = (
const flatFilteringMethod = React.useCallback>(
(params) => {
- if (props.filterMode !== 'client' || !params.isRowMatchingFilters) {
- return {
- filteredRowsLookup: {},
- filteredChildrenCountLookup: {},
- filteredDescendantCountLookup: {},
- };
+ if (
+ props.filterMode !== 'client' ||
+ !params.isRowMatchingFilters ||
+ (!params.filterModel.items.length && !params.filterModel.quickFilterValues?.length)
+ ) {
+ return defaultGridFilterLookup;
}
const dataRowIdToModelLookup = gridRowsLookupSelector(apiRef);
@@ -456,7 +454,9 @@ export const useGridFilter = (
filterCache,
);
- filteredRowsLookup[id] = isRowPassing;
+ if (!isRowPassing) {
+ filteredRowsLookup[id] = isRowPassing;
+ }
}
const footerId = 'auto-generated-group-footer-root';
diff --git a/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts b/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts
index 9c6c627a15501..7de8573b9b2ac 100644
--- a/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts
+++ b/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts
@@ -2,7 +2,6 @@ import { RefObject } from '@mui/x-internals/types';
import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity';
import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { GridStateInitializer } from '../../utils/useGridInitializeState';
-
import {
throwIfPageSizeExceedsTheLimit,
getDefaultGridPaginationModel,
@@ -31,7 +30,10 @@ export const paginationStateInitializer: GridStateInitializer<
throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, props.signature);
- const rowCount = props.rowCount ?? props.initialState?.pagination?.rowCount;
+ const rowCount =
+ props.rowCount ??
+ props.initialState?.pagination?.rowCount ??
+ (props.paginationMode === 'client' ? state.rows?.totalRowCount : undefined);
const meta = props.paginationMeta ?? props.initialState?.pagination?.meta ?? {};
return {
...state,
diff --git a/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts b/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts
index bd3e4270c606a..3f46ce0f78080 100644
--- a/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts
+++ b/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts
@@ -463,7 +463,7 @@ export const useGridRowSelection = (
if (props.filterMode === 'server') {
return !rowsLookup[id];
}
- return filteredRowsLookup[id] !== true;
+ return !rowsLookup[id] || filteredRowsLookup[id] === false;
};
let hasChanged = false;
diff --git a/packages/x-data-grid/src/hooks/features/rowSelection/utils.ts b/packages/x-data-grid/src/hooks/features/rowSelection/utils.ts
index 76dacf2c0cd5b..91e2323e91507 100644
--- a/packages/x-data-grid/src/hooks/features/rowSelection/utils.ts
+++ b/packages/x-data-grid/src/hooks/features/rowSelection/utils.ts
@@ -145,7 +145,7 @@ const getFilteredRowNodeSiblings = (
const parentNode = tree[parent] as GridGroupNode;
- return parentNode.children.filter((childId) => childId !== id && filteredRows[childId]);
+ return parentNode.children.filter((childId) => childId !== id && filteredRows[childId] !== false);
};
export const findRowsToSelect = (
diff --git a/packages/x-data-grid/src/hooks/features/rows/gridRowsMetaState.ts b/packages/x-data-grid/src/hooks/features/rows/gridRowsMetaState.ts
index a7d8d440db6c7..0d614814a3ce9 100644
--- a/packages/x-data-grid/src/hooks/features/rows/gridRowsMetaState.ts
+++ b/packages/x-data-grid/src/hooks/features/rows/gridRowsMetaState.ts
@@ -10,4 +10,12 @@ export interface GridRowsMetaState {
* The grid rows positions.
*/
positions: number[];
+ /**
+ * The total height of the pinned top rows.
+ */
+ pinnedTopRowsTotalHeight: number;
+ /**
+ * The total height of the pinned bottom rows.
+ */
+ pinnedBottomRowsTotalHeight: number;
}
diff --git a/packages/x-data-grid/src/hooks/features/rows/gridRowsUtils.ts b/packages/x-data-grid/src/hooks/features/rows/gridRowsUtils.ts
index 2b10527aa0681..09c9d5e5a2bd8 100644
--- a/packages/x-data-grid/src/hooks/features/rows/gridRowsUtils.ts
+++ b/packages/x-data-grid/src/hooks/features/rows/gridRowsUtils.ts
@@ -23,7 +23,6 @@ import {
GridRowIdToModelLookup,
GridRowsPartialUpdateAction,
} from './gridRowsInterfaces';
-import { gridPinnedRowsSelector } from './gridRowsSelector';
export const GRID_ROOT_GROUP_ID: GridRowId = `auto-generated-group-node-root`;
export const GRID_ID_AUTOGENERATED = Symbol('mui.id_autogenerated');
@@ -370,26 +369,6 @@ export const updateCacheWithNewRows = ({
};
};
-export function calculatePinnedRowsHeight(apiRef: RefObject) {
- const pinnedRows = gridPinnedRowsSelector(apiRef);
- const topPinnedRowsHeight =
- pinnedRows?.top?.reduce((acc, value) => {
- acc += apiRef.current.unstable_getRowHeight(value.id);
- return acc;
- }, 0) || 0;
-
- const bottomPinnedRowsHeight =
- pinnedRows?.bottom?.reduce((acc, value) => {
- acc += apiRef.current.unstable_getRowHeight(value.id);
- return acc;
- }, 0) || 0;
-
- return {
- top: topPinnedRowsHeight,
- bottom: bottomPinnedRowsHeight,
- };
-}
-
export const minimalContentHeight = 'var(--DataGrid-overlayHeight, calc(var(--height) * 2))';
export function computeRowsUpdates(
diff --git a/packages/x-data-grid/src/hooks/features/rows/useGridRows.ts b/packages/x-data-grid/src/hooks/features/rows/useGridRows.ts
index b7585843d04eb..01186dd1d7231 100644
--- a/packages/x-data-grid/src/hooks/features/rows/useGridRows.ts
+++ b/packages/x-data-grid/src/hooks/features/rows/useGridRows.ts
@@ -1,6 +1,7 @@
import * as React from 'react';
import { RefObject } from '@mui/x-internals/types';
import useLazyRef from '@mui/utils/useLazyRef';
+import { isObjectEmpty } from '@mui/x-internals/isObjectEmpty';
import { GridEventListener } from '../../../models/events';
import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity';
@@ -329,7 +330,9 @@ export const useGridRows = (
if (applyFiltering) {
const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
- children = children.filter((childId) => filteredRowsLookup[childId] !== false);
+ children = isObjectEmpty(filteredRowsLookup)
+ ? children
+ : children.filter((childId) => filteredRowsLookup[childId] !== false);
}
return children;
diff --git a/packages/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts b/packages/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts
index a57c294b2fa9c..ebd1318ea31c2 100644
--- a/packages/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts
+++ b/packages/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts
@@ -12,13 +12,14 @@ import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { GridRowEntry } from '../../../models/gridRows';
import { useGridSelector } from '../../utils/useGridSelector';
import { gridDensityFactorSelector } from '../density/densitySelector';
-import { gridFilterModelSelector } from '../filter/gridFilterSelector';
import { gridPaginationSelector } from '../pagination/gridPaginationSelector';
-import { gridSortModelSelector } from '../sorting/gridSortingSelector';
import { GridStateInitializer } from '../../utils/useGridInitializeState';
import { useGridRegisterPipeApplier } from '../../core/pipeProcessing';
-import { gridPinnedRowsSelector } from './gridRowsSelector';
-import { gridDimensionsSelector } from '../dimensions/gridDimensionsSelectors';
+import { gridPinnedRowsSelector, gridRowCountSelector } from './gridRowsSelector';
+import {
+ gridDimensionsSelector,
+ gridRowHeightSelector,
+} from '../dimensions/gridDimensionsSelectors';
import { getValidRowHeight, getRowHeightWarning } from './gridRowsUtils';
import type { HeightEntry } from './gridRowsMetaInterfaces';
@@ -29,11 +30,21 @@ export const rowsMetaStateInitializer: GridStateInitializer = (state, props, api
heights: new Map(),
};
+ const baseRowHeight = gridRowHeightSelector(apiRef.current.state);
+ const dataRowCount = gridRowCountSelector(apiRef.current.state);
+ const pagination = gridPaginationSelector(apiRef.current.state);
+ const rowCount = Math.min(
+ pagination.enabled ? pagination.paginationModel.pageSize : dataRowCount,
+ dataRowCount,
+ );
+
return {
...state,
rowsMeta: {
- currentPageTotalHeight: 0,
- positions: [],
+ currentPageTotalHeight: rowCount * baseRowHeight,
+ positions: Array.from({ length: rowCount }, (_, i) => i * baseRowHeight),
+ pinnedTopRowsTotalHeight: 0,
+ pinnedBottomRowsTotalHeight: 0,
},
};
};
@@ -62,15 +73,9 @@ export const useGridRowsMeta = (
const isHeightMetaValid = React.useRef(false);
const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
- const filterModel = useGridSelector(apiRef, gridFilterModelSelector);
- const paginationState = useGridSelector(apiRef, gridPaginationSelector);
- const sortModel = useGridSelector(apiRef, gridSortModelSelector);
const currentPage = useGridVisibleRows(apiRef, props);
const pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
- const rowHeight = useGridSelector(
- apiRef,
- () => gridDimensionsSelector(apiRef.current.state).rowHeight,
- );
+ const rowHeight = useGridSelector(apiRef, gridRowHeightSelector);
const getRowHeightEntry: GridRowsMetaPrivateApi['getRowHeightEntry'] = (rowId) => {
let entry = heightCache.get(rowId);
@@ -158,8 +163,15 @@ export const useGridRowsMeta = (
const hydrateRowsMeta = React.useCallback(() => {
hasRowWithAutoHeight.current = false;
- pinnedRows.top.forEach(processHeightEntry);
- pinnedRows.bottom.forEach(processHeightEntry);
+ const pinnedTopRowsTotalHeight = pinnedRows.top.reduce((acc, row) => {
+ const entry = processHeightEntry(row);
+ return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
+ }, 0);
+
+ const pinnedBottomRowsTotalHeight = pinnedRows.bottom.reduce((acc, row) => {
+ const entry = processHeightEntry(row);
+ return acc + entry.content + entry.spacingTop + entry.spacingBottom + entry.detail;
+ }, 0);
const positions: number[] = [];
const currentPageTotalHeight = currentPage.rows.reduce((acc, row) => {
@@ -176,16 +188,29 @@ export const useGridRowsMeta = (
lastMeasuredRowIndex.current = Infinity;
}
+ const didHeightsChange =
+ pinnedTopRowsTotalHeight !== apiRef.current.state.rowsMeta.pinnedTopRowsTotalHeight ||
+ pinnedBottomRowsTotalHeight !== apiRef.current.state.rowsMeta.pinnedBottomRowsTotalHeight ||
+ currentPageTotalHeight !== apiRef.current.state.rowsMeta.currentPageTotalHeight;
+
+ const rowsMeta = {
+ currentPageTotalHeight,
+ positions,
+ pinnedTopRowsTotalHeight,
+ pinnedBottomRowsTotalHeight,
+ };
+
apiRef.current.setState((state) => {
return {
...state,
- rowsMeta: {
- currentPageTotalHeight,
- positions,
- },
+ rowsMeta,
};
});
+ if (didHeightsChange) {
+ apiRef.current.updateDimensions();
+ }
+
isHeightMetaValid.current = true;
}, [apiRef, pinnedRows, currentPage.rows, processHeightEntry]);
@@ -258,7 +283,7 @@ export const useGridRowsMeta = (
// Because of variable row height this is needed for the virtualization
useEnhancedEffect(() => {
hydrateRowsMeta();
- }, [filterModel, paginationState, sortModel, hydrateRowsMeta]);
+ }, [hydrateRowsMeta]);
const rowsMetaApi: GridRowsMetaApi = {
unstable_getRowHeight: getRowHeight,
diff --git a/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts b/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts
index 1022a5ee6f2c7..2751c5e2a82bd 100644
--- a/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts
+++ b/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts
@@ -33,10 +33,11 @@ export const gridSortedRowEntriesSelector = createSelectorMemoized(
const model = idRowsLookup[id];
if (model) {
acc.push({ id, model });
- }
- const rowNode = rowTree[id];
- if (rowNode && isAutogeneratedRowNode(rowNode)) {
- acc.push({ id, model: { [GRID_ID_AUTOGENERATED]: id } });
+ } else {
+ const rowNode = rowTree[id];
+ if (rowNode && isAutogeneratedRowNode(rowNode)) {
+ acc.push({ id, model: { [GRID_ID_AUTOGENERATED]: id } });
+ }
}
return acc;
}, [] as GridRowEntry[]),
diff --git a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx
index 8be34f61cb08c..b87bd89d4b9c3 100644
--- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx
+++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx
@@ -9,6 +9,14 @@ import useLazyRef from '@mui/utils/useLazyRef';
import useTimeout from '@mui/utils/useTimeout';
import { useRtl } from '@mui/system/RtlProvider';
import reactMajor from '@mui/x-internals/reactMajor';
+import {
+ gridDimensionsSelector,
+ gridColumnsTotalWidthSelector,
+ gridContentHeightSelector,
+ gridHasFillerSelector,
+ gridRowHeightSelector,
+ gridVerticalScrollbarWidthSelector,
+} from '../dimensions/gridDimensionsSelectors';
import type { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity';
import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
import { useGridRootProps } from '../../utils/useGridRootProps';
@@ -20,11 +28,10 @@ import {
gridColumnPositionsSelector,
gridHasColSpanSelector,
} from '../columns/gridColumnsSelector';
-import { gridDimensionsSelector } from '../dimensions/gridDimensionsSelectors';
import { gridPinnedRowsSelector } from '../rows/gridRowsSelector';
import { GridPinnedRowsPosition } from '../rows/gridRowsInterfaces';
import { useGridVisibleRows, getVisibleRows } from '../../utils/useGridVisibleRows';
-import { useGridApiEventHandler } from '../../utils';
+import { useGridApiOptionHandler } from '../../utils';
import * as platform from '../../../utils/platform';
import { clamp, range } from '../../../utils/utils';
import {
@@ -51,6 +58,7 @@ import { EMPTY_PINNED_COLUMN_FIELDS, GridPinnedColumns } from '../columns';
import { gridFocusedVirtualCellSelector } from './gridFocusedVirtualCellSelector';
import { roundToDecimalPlaces } from '../../../utils/roundToDecimalPlaces';
import { isJSDOM } from '../../../utils/isJSDOM';
+import { GridStateCommunity } from '../../../models/gridStateCommunity';
const MINIMUM_COLUMN_WIDTH = 50;
@@ -104,8 +112,7 @@ export const useGridVirtualScroller = () => {
const enabledForRows = useGridSelector(apiRef, gridVirtualizationRowEnabledSelector) && !isJSDOM;
const enabledForColumns =
useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector) && !isJSDOM;
- const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
- const outerSize = dimensions.viewportOuterSize;
+
const pinnedRows = useGridSelector(apiRef, gridPinnedRowsSelector);
const pinnedColumnDefinitions = gridVisiblePinnedColumnDefinitionsSelector(apiRef);
const pinnedColumns = listView
@@ -121,11 +128,16 @@ export const useGridVirtualScroller = () => {
const scrollerRef = apiRef.current.virtualScrollerRef;
const scrollbarVerticalRef = apiRef.current.virtualScrollbarVerticalRef;
const scrollbarHorizontalRef = apiRef.current.virtualScrollbarHorizontalRef;
- const contentHeight = dimensions.contentSize.height;
- const columnsTotalWidth = dimensions.columnsTotalWidth;
const hasColSpan = useGridSelector(apiRef, gridHasColSpanSelector);
const isRenderContextReady = React.useRef(false);
+ const rowHeight = useGridSelector(apiRef, gridRowHeightSelector);
+ const contentHeight = useGridSelector(apiRef, gridContentHeightSelector);
+ const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector);
+ const needsHorizontalScrollbar = useGridSelector(apiRef, needsHorizontalScrollbarSelector);
+ const verticalScrollbarWidth = useGridSelector(apiRef, gridVerticalScrollbarWidthSelector);
+ const gridHasFiller = useGridSelector(apiRef, gridHasFillerSelector);
+
const previousSize = React.useRef<{ width: number; height: number }>(null);
const mainRefCallback = React.useCallback(
@@ -217,7 +229,7 @@ export const useGridVirtualScroller = () => {
isRtl,
rootProps.rowBufferPx,
rootProps.columnBufferPx,
- dimensions.rowHeight * 15,
+ rowHeight * 15,
MINIMUM_COLUMN_WIDTH * 6,
),
).current;
@@ -245,16 +257,17 @@ export const useGridVirtualScroller = () => {
});
// The lazy-loading hook is listening to `renderedRowsIntervalChange`,
- // but only does something if the dimensions are also available.
- // So we wait until we have valid dimensions before publishing the first event.
- if (dimensions.isReady && didRowsIntervalChange) {
+ // but only does something if we already have a render context, because
+ // otherwise we would call an update directly on mount
+ const isReady = gridDimensionsSelector(apiRef.current.state).isReady;
+ if (isReady && didRowsIntervalChange) {
previousRowContext.current = nextRenderContext;
apiRef.current.publishEvent('renderedRowsIntervalChange', nextRenderContext);
}
previousContextScrollPosition.current = scrollPosition.current;
},
- [apiRef, dimensions.isReady],
+ [apiRef],
);
const triggerUpdateRenderContext = useEventCallback(() => {
@@ -263,6 +276,7 @@ export const useGridVirtualScroller = () => {
return undefined;
}
+ const dimensions = gridDimensionsSelector(apiRef.current.state);
const maxScrollTop = Math.ceil(
dimensions.minimumSize.height - dimensions.viewportOuterSize.height,
);
@@ -296,8 +310,7 @@ export const useGridVirtualScroller = () => {
);
// PERF: use the computed minimum column width instead of a static one
- const didCrossThreshold =
- rowScroll >= dimensions.rowHeight || columnScroll >= MINIMUM_COLUMN_WIDTH;
+ const didCrossThreshold = rowScroll >= rowHeight || columnScroll >= MINIMUM_COLUMN_WIDTH;
const didChangeDirection = scrollCache.direction !== direction;
const shouldUpdate = didCrossThreshold || didChangeDirection;
@@ -326,7 +339,7 @@ export const useGridVirtualScroller = () => {
direction,
rootProps.rowBufferPx,
rootProps.columnBufferPx,
- dimensions.rowHeight * 15,
+ rowHeight * 15,
MINIMUM_COLUMN_WIDTH * 6,
);
@@ -344,6 +357,13 @@ export const useGridVirtualScroller = () => {
});
const forceUpdateRenderContext = () => {
+ // skip update if dimensions are not ready and virtualization is enabled
+ if (
+ !gridDimensionsSelector(apiRef.current.state).isReady &&
+ (enabledForRows || enabledForColumns)
+ ) {
+ return;
+ }
const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns);
const nextRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache);
// Reset the frozen context when the render context changes, see the illustration in https://github.com/mui/mui-x/pull/12353
@@ -385,7 +405,13 @@ export const useGridVirtualScroller = () => {
return [];
}
- const baseRenderContext = params.renderContext ?? renderContext;
+ let baseRenderContext = renderContext;
+ if (params.renderContext) {
+ baseRenderContext = params.renderContext as GridRenderContext;
+
+ baseRenderContext.firstColumnIndex = renderContext.firstColumnIndex;
+ baseRenderContext.lastColumnIndex = renderContext.lastColumnIndex;
+ }
const isLastSection =
(!hasBottomPinnedRows && params.position === undefined) ||
@@ -500,7 +526,6 @@ export const useGridVirtualScroller = () => {
let currentRenderContext = baseRenderContext;
if (
- !isPinnedSection &&
frozenContext.current &&
rowIndexInPage >= frozenContext.current.firstRowIndex &&
rowIndexInPage < frozenContext.current.lastRowIndex
@@ -529,7 +554,7 @@ export const useGridVirtualScroller = () => {
index={rowIndex}
selected={isSelected}
offsetLeft={offsetLeft}
- columnsTotalWidth={dimensions.columnsTotalWidth}
+ columnsTotalWidth={columnsTotalWidth}
rowHeight={baseRowHeight}
pinnedColumns={pinnedColumns}
visibleColumns={visibleColumns}
@@ -540,8 +565,8 @@ export const useGridVirtualScroller = () => {
isLastVisible={isLastVisible}
isNotVisible={isVirtualFocusRow}
showBottomBorder={showBottomBorder}
- scrollbarWidth={dimensions.hasScrollY ? dimensions.scrollbarSize : 0}
- gridHasFiller={dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width}
+ scrollbarWidth={verticalScrollbarWidth}
+ gridHasFiller={gridHasFiller}
{...rowProps}
/>,
);
@@ -561,8 +586,6 @@ export const useGridVirtualScroller = () => {
return rows;
};
- const needsHorizontalScrollbar = outerSize.width && columnsTotalWidth > outerSize.width;
-
const scrollerStyle = React.useMemo(
() =>
({
@@ -612,16 +635,11 @@ export const useGridVirtualScroller = () => {
}
}, [listView, scrollerRef]);
- useRunOnce(outerSize.width !== 0, () => {
- const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns);
-
- const initialRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache);
- updateRenderContext(initialRenderContext);
-
+ useRunOnce(renderContext !== EMPTY_RENDER_CONTEXT, () => {
apiRef.current.publishEvent('scrollPositionChange', {
top: scrollPosition.current.top,
left: scrollPosition.current.left,
- renderContext: initialRenderContext,
+ renderContext,
});
isRenderContextReady.current = true;
@@ -680,9 +698,9 @@ export const useGridVirtualScroller = () => {
updateRenderContext: forceUpdateRenderContext,
});
- useGridApiEventHandler(apiRef, 'columnsChange', forceUpdateRenderContext);
- useGridApiEventHandler(apiRef, 'filteredRowsSet', forceUpdateRenderContext);
- useGridApiEventHandler(apiRef, 'rowExpansionChange', forceUpdateRenderContext);
+ useGridApiOptionHandler(apiRef, 'sortedRowsSet', forceUpdateRenderContext);
+ useGridApiOptionHandler(apiRef, 'paginationModelChange', forceUpdateRenderContext);
+ useGridApiOptionHandler(apiRef, 'columnsChange', forceUpdateRenderContext);
return {
renderContext,
@@ -718,6 +736,9 @@ export const useGridVirtualScroller = () => {
role: 'presentation',
scrollPosition,
}),
+ getScrollAreaProps: () => ({
+ scrollPosition,
+ }),
};
};
@@ -745,6 +766,14 @@ type RenderContextInputs = {
virtualizeColumnsWithAutoRowHeight: DataGridProcessedProps['virtualizeColumnsWithAutoRowHeight'];
};
+// dimension selectors
+function needsHorizontalScrollbarSelector(state: GridStateCommunity) {
+ return (
+ state.dimensions.viewportOuterSize.width > 0 &&
+ state.dimensions.columnsTotalWidth > state.dimensions.viewportOuterSize.width
+ );
+}
+
function inputsSelector(
apiRef: RefObject,
rootProps: ReturnType,
diff --git a/packages/x-data-grid/src/hooks/utils/useGridSelector.ts b/packages/x-data-grid/src/hooks/utils/useGridSelector.ts
index 6680e010ecb00..20962d950fbf2 100644
--- a/packages/x-data-grid/src/hooks/utils/useGridSelector.ts
+++ b/packages/x-data-grid/src/hooks/utils/useGridSelector.ts
@@ -2,10 +2,10 @@ import * as React from 'react';
import { RefObject } from '@mui/x-internals/types';
import { fastObjectShallowCompare } from '@mui/x-internals/fastObjectShallowCompare';
import { warnOnce } from '@mui/x-internals/warning';
+import { useSyncExternalStore } from 'use-sync-external-store/shim';
import type { GridApiCommon } from '../../models/api/gridApiCommon';
import type { OutputSelector } from '../../utils/createSelector';
import { useLazyRef } from './useLazyRef';
-import { useOnMount } from './useOnMount';
import type { GridCoreApi } from '../../models/api/gridCoreApi';
function isOutputSelector(
@@ -50,7 +50,19 @@ export const argsEqual = (prev: any, curr: any) => {
return fn(prev, curr);
};
-const createRefs = () => ({ state: null, equals: null, selector: null, args: null }) as any;
+const createRefs = () => ({ state: null, equals: null, selector: null, args: undefined }) as any;
+
+const EMPTY = [] as unknown[];
+
+type Refs = {
+ state: T;
+ equals: (a: U, b: U) => boolean;
+ selector: Selector;
+ args: any;
+ subscription: undefined | (() => void);
+};
+
+const emptyGetSnapshot = () => null;
export const useGridSelector = (
apiRef: RefObject,
@@ -67,15 +79,7 @@ export const useGridSelector = (
}
}
- const refs = useLazyRef<
- {
- state: T;
- equals: typeof equals;
- selector: typeof selector;
- args: typeof args;
- },
- never
- >(createRefs);
+ const refs = useLazyRef, never>(createRefs);
const didInit = refs.current.selector !== null;
const [state, setState] = React.useState(
@@ -102,20 +106,43 @@ export const useGridSelector = (
}
}
- useOnMount(() => {
- return apiRef.current.store.subscribe(() => {
- const newState = applySelector(
- apiRef,
- refs.current.selector,
- refs.current.args,
- apiRef.current.instanceId,
- ) as T;
- if (!refs.current.equals(refs.current.state, newState)) {
- refs.current.state = newState;
- setState(newState);
+ const subscribe = React.useCallback(
+ () => {
+ if (refs.current.subscription) {
+ return null;
}
- });
- });
+
+ refs.current.subscription = apiRef.current.store.subscribe(() => {
+ const newState = applySelector(
+ apiRef,
+ refs.current.selector,
+ refs.current.args,
+ apiRef.current.instanceId,
+ ) as T;
+
+ if (!refs.current.equals(refs.current.state, newState)) {
+ refs.current.state = newState;
+ setState(newState);
+ }
+ });
+
+ return null;
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ EMPTY,
+ );
+
+ const unsubscribe = React.useCallback(() => {
+ return () => {
+ if (refs.current.subscription) {
+ refs.current.subscription();
+ refs.current.subscription = undefined;
+ }
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, EMPTY);
+
+ useSyncExternalStore(unsubscribe, subscribe, emptyGetSnapshot);
return state;
};
diff --git a/packages/x-data-grid/src/hooks/utils/useIsSSR.ts b/packages/x-data-grid/src/hooks/utils/useIsSSR.ts
new file mode 100644
index 0000000000000..f40364e068971
--- /dev/null
+++ b/packages/x-data-grid/src/hooks/utils/useIsSSR.ts
@@ -0,0 +1,7 @@
+import { useSyncExternalStore } from 'use-sync-external-store/shim';
+
+const emptySubscribe = () => () => {};
+const clientSnapshot = () => false;
+const serverSnapshot = () => true;
+
+export const useIsSSR = () => useSyncExternalStore(emptySubscribe, clientSnapshot, serverSnapshot);
diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts
index b65b0de05fa23..96eb78a72a688 100644
--- a/packages/x-data-grid/src/internals/index.ts
+++ b/packages/x-data-grid/src/internals/index.ts
@@ -61,6 +61,7 @@ export { useGridDensity, densityStateInitializer } from '../hooks/features/densi
export { useGridCsvExport } from '../hooks/features/export/useGridCsvExport';
export { useGridPrintExport } from '../hooks/features/export/useGridPrintExport';
export { useGridFilter, filterStateInitializer } from '../hooks/features/filter/useGridFilter';
+export { defaultGridFilterLookup } from '../hooks/features/filter/gridFilterState';
export { passFilterLogic } from '../hooks/features/filter/gridFilterUtils';
export {
gridFilteredChildrenCountLookupSelector,
@@ -114,7 +115,6 @@ export {
headerFilteringStateInitializer,
useGridHeaderFiltering,
} from '../hooks/features/headerFiltering/useGridHeaderFiltering';
-export { calculatePinnedRowsHeight } from '../hooks/features/rows/gridRowsUtils';
export {
useGridRowSelection,
rowSelectionStateInitializer,
@@ -129,6 +129,7 @@ export {
dimensionsStateInitializer,
useGridDimensions,
} from '../hooks/features/dimensions/useGridDimensions';
+export * from '../hooks/features/dimensions/gridDimensionsSelectors';
export { useGridStatePersistence } from '../hooks/features/statePersistence/useGridStatePersistence';
export type { GridRestoreStatePreProcessingContext } from '../hooks/features/statePersistence/gridStatePersistenceInterface';
export {
diff --git a/packages/x-data-grid/src/models/api/gridStateApi.ts b/packages/x-data-grid/src/models/api/gridStateApi.ts
index a1c88c022edec..40a0cc98e7659 100644
--- a/packages/x-data-grid/src/models/api/gridStateApi.ts
+++ b/packages/x-data-grid/src/models/api/gridStateApi.ts
@@ -9,6 +9,7 @@ export interface GridStateApi {
state: State;
/**
* Forces the grid to rerender. It's often used after a state update.
+ * @deprecated no longer needed.
*/
forceUpdate: () => void;
/**
diff --git a/packages/x-data-grid/src/models/events/gridEventLookup.ts b/packages/x-data-grid/src/models/events/gridEventLookup.ts
index 80198a32f8481..9f37ed2ede1d4 100644
--- a/packages/x-data-grid/src/models/events/gridEventLookup.ts
+++ b/packages/x-data-grid/src/models/events/gridEventLookup.ts
@@ -399,6 +399,10 @@ export interface GridEventLookup
GridColumnGroupHeaderEventLookup,
GridCellEventLookup,
GridControlledStateEventLookup {
+ /**
+ * Fired when rootElementRef.current becomes available.
+ */
+ rootMount: { params: HTMLElement };
/**
* Fired when the grid is unmounted.
*/
diff --git a/packages/x-internals/src/useResizeObserver/useResizeObserver.ts b/packages/x-internals/src/useResizeObserver/useResizeObserver.ts
index 19e4d5bb89a19..452ece1dbe34a 100644
--- a/packages/x-internals/src/useResizeObserver/useResizeObserver.ts
+++ b/packages/x-internals/src/useResizeObserver/useResizeObserver.ts
@@ -1,7 +1,7 @@
import * as React from 'react';
import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
-const isDevEnvironment = process.env.NODE_ENV === 'development';
+const isDevEnvironment = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';
const noop = () => {};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 27adae5763e45..6fb03bc5b4103 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1000,6 +1000,9 @@ importers:
reselect:
specifier: ^5.1.1
version: 5.1.1
+ use-sync-external-store:
+ specifier: ^1.0.0
+ version: 1.4.0(react@19.0.0)
devDependencies:
'@mui/internal-test-utils':
specifier: ^1.0.26
@@ -1016,6 +1019,9 @@ importers:
'@types/prop-types':
specifier: ^15.7.14
version: 15.7.14
+ '@types/use-sync-external-store':
+ specifier: ^0.0.6
+ version: 0.0.6
react:
specifier: ^19.0.0
version: 19.0.0