diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json index 092a70faf262..06218ff0ab86 100644 --- a/docs/pages/x/api/data-grid/selectors.json +++ b/docs/pages/x/api/data-grid/selectors.json @@ -518,16 +518,24 @@ "name": "gridVirtualizationColumnEnabledSelector", "returnType": "boolean", "category": "Virtualization", - "description": "Get the enabled state for virtualization", + "description": "Get the enabled state for column virtualization", "supportsApiRef": true }, { "name": "gridVirtualizationEnabledSelector", "returnType": "boolean", "category": "Virtualization", + "deprecated": "Use `gridVirtualizationColumnEnabledSelector` and `gridVirtualizationRowEnabledSelector`", "description": "Get the enabled state for virtualization", "supportsApiRef": true }, + { + "name": "gridVirtualizationRowEnabledSelector", + "returnType": "boolean", + "category": "Virtualization", + "description": "Get the enabled state for row virtualization", + "supportsApiRef": true + }, { "name": "gridVirtualizationSelector", "returnType": "GridVirtualizationState", diff --git a/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts b/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts index 17dc3004bcf3..91ee34c84ee4 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts +++ b/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts @@ -11,6 +11,7 @@ export const gridVirtualizationSelector = (state: GridStateCommunity) => state.v /** * Get the enabled state for virtualization * @category Virtualization + * @deprecated Use `gridVirtualizationColumnEnabledSelector` and `gridVirtualizationRowEnabledSelector` */ export const gridVirtualizationEnabledSelector = createSelector( gridVirtualizationSelector, @@ -18,7 +19,7 @@ export const gridVirtualizationEnabledSelector = createSelector( ); /** - * Get the enabled state for virtualization + * Get the enabled state for column virtualization * @category Virtualization */ export const gridVirtualizationColumnEnabledSelector = createSelector( @@ -26,6 +27,15 @@ export const gridVirtualizationColumnEnabledSelector = createSelector( (state) => state.enabledForColumns, ); +/** + * Get the enabled state for row virtualization + * @category Virtualization + */ +export const gridVirtualizationRowEnabledSelector = createSelector( + gridVirtualizationSelector, + (state) => state.enabledForRows, +); + /** * Get the render context * @category Virtualization 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 4c12b9b87a56..9e1ae101cc45 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -41,7 +41,7 @@ import { GridRowProps } from '../../../components/GridRow'; import { GridInfiniteLoaderPrivateApi } from '../../../models/api/gridInfiniteLoaderApi'; import { gridRenderContextSelector, - gridVirtualizationEnabledSelector, + gridVirtualizationRowEnabledSelector, gridVirtualizationColumnEnabledSelector, } from './gridVirtualizationSelectors'; import { EMPTY_RENDER_CONTEXT } from './useGridVirtualization'; @@ -99,7 +99,7 @@ export const useGridVirtualScroller = () => { const apiRef = useGridPrivateApiContext() as React.MutableRefObject; const rootProps = useGridRootProps(); const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); - const enabled = useGridSelector(apiRef, gridVirtualizationEnabledSelector) && !isJSDOM; + const enabledForRows = useGridSelector(apiRef, gridVirtualizationRowEnabledSelector) && !isJSDOM; const enabledForColumns = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector) && !isJSDOM; const dimensions = useGridSelector(apiRef, gridDimensionsSelector); @@ -262,7 +262,7 @@ export const useGridVirtualScroller = () => { MINIMUM_COLUMN_WIDTH * 6, ); - const inputs = inputsSelector(apiRef, rootProps, enabled, enabledForColumns); + const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns); const nextRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache); // Prevents batching render context changes @@ -276,7 +276,7 @@ export const useGridVirtualScroller = () => { }; const forceUpdateRenderContext = () => { - const inputs = inputsSelector(apiRef, rootProps, enabled, enabledForColumns); + 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 frozenContext.current = undefined; @@ -551,15 +551,21 @@ export const useGridVirtualScroller = () => { }, [apiRef, rowsMeta.currentPageTotalHeight]); useEnhancedEffect(() => { - if (enabled) { - // TODO a scroll reset should not be necessary + // TODO a scroll reset should not be necessary + if (enabledForColumns) { scrollerRef.current!.scrollLeft = 0; + } + }, [enabledForColumns, gridRootRef, scrollerRef]); + + useEnhancedEffect(() => { + // TODO a scroll reset should not be necessary + if (enabledForRows) { scrollerRef.current!.scrollTop = 0; } - }, [enabled, gridRootRef, scrollerRef]); + }, [enabledForRows, gridRootRef, scrollerRef]); useRunOnce(outerSize.width !== 0, () => { - const inputs = inputsSelector(apiRef, rootProps, enabled, enabledForColumns); + const inputs = inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns); const initialRenderContext = computeRenderContext(inputs, scrollPosition.current, scrollCache); updateRenderContext(initialRenderContext); @@ -608,7 +614,7 @@ export const useGridVirtualScroller = () => { }; type RenderContextInputs = { - enabled: boolean; + enabledForRows: boolean; enabledForColumns: boolean; apiRef: React.MutableRefObject; autoHeight: boolean; @@ -631,7 +637,7 @@ type RenderContextInputs = { function inputsSelector( apiRef: React.MutableRefObject, rootProps: ReturnType, - enabled: boolean, + enabledForRows: boolean, enabledForColumns: boolean, ): RenderContextInputs { const dimensions = gridDimensionsSelector(apiRef.current.state); @@ -640,7 +646,7 @@ function inputsSelector( const lastRowId = apiRef.current.state.rows.dataRowIds.at(-1); const lastColumn = visibleColumns.at(-1); return { - enabled, + enabledForRows, enabledForColumns, apiRef, autoHeight: rootProps.autoHeight, @@ -666,19 +672,17 @@ function computeRenderContext( scrollPosition: ScrollPosition, scrollCache: ScrollCache, ) { - let renderContext: GridRenderContext; - - if (!inputs.enabled) { - renderContext = { - firstRowIndex: 0, - lastRowIndex: inputs.rows.length, - firstColumnIndex: 0, - lastColumnIndex: inputs.visibleColumns.length, - }; - } else { - const { top, left } = scrollPosition; - const realLeft = Math.abs(left) + inputs.leftPinnedWidth; + const renderContext: GridRenderContext = { + firstRowIndex: 0, + lastRowIndex: inputs.rows.length, + firstColumnIndex: 0, + lastColumnIndex: inputs.visibleColumns.length, + }; + + const { top, left } = scrollPosition; + const realLeft = Math.abs(left) + inputs.leftPinnedWidth; + if (inputs.enabledForRows) { // Clamp the value because the search may return an index out of bounds. // In the last index, this is not needed because Array.slice doesn't include it. const firstRowIndex = Math.min( @@ -694,46 +698,42 @@ function computeRenderContext( ? firstRowIndex + inputs.rows.length : getNearestIndexToRender(inputs, top + inputs.viewportInnerHeight); + renderContext.firstRowIndex = firstRowIndex; + renderContext.lastRowIndex = lastRowIndex; + } + + if (inputs.enabledForColumns) { let firstColumnIndex = 0; let lastColumnIndex = inputs.columnPositions.length; - if (inputs.enabledForColumns) { - let hasRowWithAutoHeight = false; - - const [firstRowToRender, lastRowToRender] = getIndexesToRender({ - firstIndex: firstRowIndex, - lastIndex: lastRowIndex, - minFirstIndex: 0, - maxLastIndex: inputs.rows.length, - bufferBefore: scrollCache.buffer.rowBefore, - bufferAfter: scrollCache.buffer.rowAfter, - positions: inputs.rowsMeta.positions, - lastSize: inputs.lastRowHeight, - }); + let hasRowWithAutoHeight = false; + + const [firstRowToRender, lastRowToRender] = getIndexesToRender({ + firstIndex: renderContext.firstRowIndex, + lastIndex: renderContext.lastRowIndex, + minFirstIndex: 0, + maxLastIndex: inputs.rows.length, + bufferBefore: scrollCache.buffer.rowBefore, + bufferAfter: scrollCache.buffer.rowAfter, + positions: inputs.rowsMeta.positions, + lastSize: inputs.lastRowHeight, + }); - for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { - const row = inputs.rows[i]; - hasRowWithAutoHeight = inputs.apiRef.current.rowHasAutoHeight(row.id); - } + for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { + const row = inputs.rows[i]; + hasRowWithAutoHeight = inputs.apiRef.current.rowHasAutoHeight(row.id); + } - if (!hasRowWithAutoHeight) { - firstColumnIndex = binarySearch(realLeft, inputs.columnPositions, { - atStart: true, - lastPosition: inputs.columnsTotalWidth, - }); - lastColumnIndex = binarySearch( - realLeft + inputs.viewportInnerWidth, - inputs.columnPositions, - ); - } + if (!hasRowWithAutoHeight) { + firstColumnIndex = binarySearch(realLeft, inputs.columnPositions, { + atStart: true, + lastPosition: inputs.columnsTotalWidth, + }); + lastColumnIndex = binarySearch(realLeft + inputs.viewportInnerWidth, inputs.columnPositions); } - renderContext = { - firstRowIndex, - lastRowIndex, - firstColumnIndex, - lastColumnIndex, - }; + renderContext.firstColumnIndex = firstColumnIndex; + renderContext.lastColumnIndex = lastColumnIndex; } const actualRenderContext = deriveRenderContext(inputs, renderContext, scrollCache); diff --git a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx index eceeeeb7b3b0..c02c4d35b9dd 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx @@ -10,6 +10,7 @@ type RootProps = Pick = (state, props) => { + const { disableVirtualization, autoHeight } = props; + const virtualization = { - enabled: !props.disableVirtualization && !props.autoHeight, - enabledForColumns: true, + enabled: !disableVirtualization, + enabledForColumns: !disableVirtualization, + enabledForRows: !disableVirtualization && !autoHeight, renderContext: EMPTY_RENDER_CONTEXT, }; @@ -47,6 +51,8 @@ export function useGridVirtualization( virtualization: { ...state.virtualization, enabled, + enabledForColumns: enabled, + enabledForRows: enabled && !props.autoHeight, }, })); }; @@ -61,9 +67,20 @@ export function useGridVirtualization( })); }; + const setRowVirtualization = (enabled: boolean) => { + apiRef.current.setState((state) => ({ + ...state, + virtualization: { + ...state.virtualization, + enabledForRows: enabled, + }, + })); + }; + const api = { unstable_setVirtualization: setVirtualization, unstable_setColumnVirtualization: setColumnVirtualization, + unstable_setRowVirtualization: setRowVirtualization, }; useGridApiMethod(apiRef, api, 'public'); @@ -74,7 +91,7 @@ export function useGridVirtualization( /* eslint-disable react-hooks/exhaustive-deps */ React.useEffect(() => { - setVirtualization(!props.disableVirtualization && !props.autoHeight); + setVirtualization(!props.disableVirtualization); }, [props.disableVirtualization, props.autoHeight]); /* eslint-enable react-hooks/exhaustive-deps */ } diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index ac6c300397a0..d03a0824d1d2 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -620,6 +620,7 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" }, + { "name": "gridVirtualizationRowEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationSelector", "kind": "Variable" }, { "name": "GridVirtualizationState", "kind": "TypeAlias" }, { "name": "GridVisibilityOffIcon", "kind": "Variable" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index e9620df666a0..a99c16f0e177 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -574,6 +574,7 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" }, + { "name": "gridVirtualizationRowEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationSelector", "kind": "Variable" }, { "name": "GridVirtualizationState", "kind": "TypeAlias" }, { "name": "GridVisibilityOffIcon", "kind": "Variable" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index ac66be0724d0..56013ba8ab98 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -524,6 +524,7 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" }, + { "name": "gridVirtualizationRowEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationSelector", "kind": "Variable" }, { "name": "GridVirtualizationState", "kind": "TypeAlias" }, { "name": "GridVisibilityOffIcon", "kind": "Variable" },