Skip to content

Commit

Permalink
separate row and column virtualization state
Browse files Browse the repository at this point in the history
  • Loading branch information
KenanYusuf committed Aug 20, 2024
1 parent 30fe934 commit b6555c2
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 61 deletions.
10 changes: 9 additions & 1 deletion docs/pages/x/api/data-grid/selectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,31 @@ 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,
(state) => state.enabled,
);

/**
* Get the enabled state for virtualization
* Get the enabled state for column virtualization
* @category Virtualization
*/
export const gridVirtualizationColumnEnabledSelector = createSelector(
gridVirtualizationSelector,
(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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -99,7 +99,7 @@ export const useGridVirtualScroller = () => {
const apiRef = useGridPrivateApiContext() as React.MutableRefObject<PrivateApiWithInfiniteLoader>;
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);
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -608,7 +614,7 @@ export const useGridVirtualScroller = () => {
};

type RenderContextInputs = {
enabled: boolean;
enabledForRows: boolean;
enabledForColumns: boolean;
apiRef: React.MutableRefObject<GridPrivateApiCommunity>;
autoHeight: boolean;
Expand All @@ -631,7 +637,7 @@ type RenderContextInputs = {
function inputsSelector(
apiRef: React.MutableRefObject<GridPrivateApiCommunity>,
rootProps: ReturnType<typeof useGridRootProps>,
enabled: boolean,
enabledForRows: boolean,
enabledForColumns: boolean,
): RenderContextInputs {
const dimensions = gridDimensionsSelector(apiRef.current.state);
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type RootProps = Pick<DataGridProcessedProps, 'disableVirtualization' | 'autoHei
export type GridVirtualizationState = {
enabled: boolean;
enabledForColumns: boolean;
enabledForRows: boolean;
renderContext: GridRenderContext;
};

Expand All @@ -21,9 +22,12 @@ export const EMPTY_RENDER_CONTEXT = {
};

export const virtualizationStateInitializer: GridStateInitializer<RootProps> = (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,
};

Expand All @@ -47,6 +51,8 @@ export function useGridVirtualization(
virtualization: {
...state.virtualization,
enabled,
enabledForColumns: enabled,
enabledForRows: enabled && !props.autoHeight,
},
}));
};
Expand All @@ -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');
Expand All @@ -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 */
}
1 change: 1 addition & 0 deletions scripts/x-data-grid-premium.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
1 change: 1 addition & 0 deletions scripts/x-data-grid-pro.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
1 change: 1 addition & 0 deletions scripts/x-data-grid.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down

0 comments on commit b6555c2

Please sign in to comment.