From 00c0b2b12045eb36e5d53f2ace4236757bd473cc Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Sat, 23 Nov 2024 14:04:44 +0100 Subject: [PATCH] [DataGrid] Support column virtualization with dynamic row height (#15541) --- .../VirtualizeColumnsWithAutoRowHeight.js | 49 +++++++++++++++ .../VirtualizeColumnsWithAutoRowHeight.tsx | 59 +++++++++++++++++++ ...ualizeColumnsWithAutoRowHeight.tsx.preview | 5 ++ docs/data/data-grid/row-height/row-height.md | 12 +++- .../virtualization/virtualization.md | 7 ++- .../x/api/data-grid/data-grid-premium.json | 3 +- docs/pages/x/api/data-grid/data-grid-pro.json | 3 +- docs/pages/x/api/data-grid/data-grid.json | 3 +- .../data-grid-premium/data-grid-premium.json | 3 + .../data-grid-pro/data-grid-pro.json | 3 + .../data-grid/data-grid/data-grid.json | 3 + .../src/DataGridPremium/DataGridPremium.tsx | 8 +++ .../src/DataGridPro/DataGridPro.tsx | 8 +++ .../x-data-grid/src/DataGrid/DataGrid.tsx | 8 +++ .../src/components/cell/GridCell.tsx | 1 + .../constants/dataGridPropsDefaultValues.ts | 1 + .../virtualization/useGridVirtualScroller.tsx | 13 ++-- .../src/models/props/DataGridProps.ts | 8 +++ 18 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js create mode 100644 docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx create mode 100644 docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview diff --git a/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js new file mode 100644 index 000000000000..9d01cdcaf506 --- /dev/null +++ b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +function useData(rowLength, columnLength) { + const [data, setData] = React.useState({ columns: [], rows: [] }); + + React.useEffect(() => { + const rows = []; + + for (let i = 0; i < rowLength; i += 1) { + const row = { + id: i, + }; + + for (let j = 1; j <= columnLength; j += 1) { + row[`price${j}M`] = `${i.toString()}, ${j} `; + } + + rows.push(row); + } + + const columns = []; + + for (let j = 1; j <= columnLength; j += 1) { + columns.push({ field: `price${j}M`, headerName: `${j}M`, width: 55 }); + } + + setData({ + rows, + columns, + }); + }, [rowLength, columnLength]); + + return data; +} + +export default function VirtualizeColumnsWithAutoRowHeight() { + const data = useData(100, 100); + + return ( +
+ 'auto'} + virtualizeColumnsWithAutoRowHeight + /> +
+ ); +} diff --git a/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx new file mode 100644 index 000000000000..1a76b22ee5c9 --- /dev/null +++ b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import { DataGrid, GridColDef, GridRowId } from '@mui/x-data-grid'; + +export interface DataRowModel { + id: GridRowId; + [price: string]: number | string; +} + +export interface GridData { + columns: GridColDef[]; + rows: DataRowModel[]; +} + +function useData(rowLength: number, columnLength: number) { + const [data, setData] = React.useState({ columns: [], rows: [] }); + + React.useEffect(() => { + const rows: DataRowModel[] = []; + + for (let i = 0; i < rowLength; i += 1) { + const row: DataRowModel = { + id: i, + }; + + for (let j = 1; j <= columnLength; j += 1) { + row[`price${j}M`] = `${i.toString()}, ${j} `; + } + + rows.push(row); + } + + const columns: GridColDef[] = []; + + for (let j = 1; j <= columnLength; j += 1) { + columns.push({ field: `price${j}M`, headerName: `${j}M`, width: 55 }); + } + + setData({ + rows, + columns, + }); + }, [rowLength, columnLength]); + + return data; +} + +export default function VirtualizeColumnsWithAutoRowHeight() { + const data = useData(100, 100); + + return ( +
+ 'auto'} + virtualizeColumnsWithAutoRowHeight + /> +
+ ); +} diff --git a/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview new file mode 100644 index 000000000000..1f3efbd9e944 --- /dev/null +++ b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview @@ -0,0 +1,5 @@ + 'auto'} + virtualizeColumnsWithAutoRowHeight +/> \ No newline at end of file diff --git a/docs/data/data-grid/row-height/row-height.md b/docs/data/data-grid/row-height/row-height.md index 0d5790977d89..fa5a34a72a4c 100644 --- a/docs/data/data-grid/row-height/row-height.md +++ b/docs/data/data-grid/row-height/row-height.md @@ -54,7 +54,6 @@ This side effect happens because a row height estimation is used while a row is You can configure the estimated value used by passing a function to the `getEstimatedRowHeight` prop. If not provided, the default row height of `52px` is used as estimation. It's recommended to pass this prop if the content deviates too much from the default value. -Note that, due to the implementation adopted, the virtualization of the columns is also disabled to force all columns to be rendered at the same time. ```tsx 'auto'} getEstimatedRowHeight={() => 200} /> @@ -78,6 +77,17 @@ Add padding to the cells to increase the space between the content and the cell ::: +### Column virtualization + +By default, the virtualization of the columns is disabled to force all columns to be rendered at the same time and calculate the row height correctly. +However, this can lead to poor performance when rendering a lot of columns. + +If you need column virtualization, you can set the `virtualizeColumnsWithAutoRowHeight` prop to `true`. +With this approach, the Data Grid measures the row height based on the visible columns. +However, the row height might change during horizontal scrolling. + +{{"demo": "VirtualizeColumnsWithAutoRowHeight.js", "bg": "inline" }} + ## Row density Give your users the option to change the default row density to match their preferences—compact, standard, or comfortable. diff --git a/docs/data/data-grid/virtualization/virtualization.md b/docs/data/data-grid/virtualization/virtualization.md index 46efb8f1de52..3dc949818c95 100644 --- a/docs/data/data-grid/virtualization/virtualization.md +++ b/docs/data/data-grid/virtualization/virtualization.md @@ -31,7 +31,12 @@ By default, columns coming under 150 pixels region are rendered outside of the v {{"demo": "ColumnVirtualizationGrid.js", "bg": "inline"}} -You can disable column virtualization by calling `apiRef.current.unstable_setColumnVirtualization(false)`, or by setting the column buffer to the number of total columns. +You can disable column virtualization by calling `apiRef.current.unstable_setColumnVirtualization(false)`, or by setting the [`columnBufferPx`](/x/api/data-grid/data-grid/#data-grid-prop-columnBufferPx) to a high value. + +:::info +Column virtualization is disabled when dynamic row height is enabled. +See [dynamic row height and column virtualization](/x/react-data-grid/row-height/#column-virtualization) to learn more. +::: ## Disable virtualization diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index 478161ccf967..1f5926d0df76 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -656,7 +656,8 @@ } }, "unstable_listView": { "type": { "name": "bool" } }, - "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } + "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" }, + "virtualizeColumnsWithAutoRowHeight": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGridPremium", "imports": [ diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 2852fe9fa58e..68cdcb294447 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -587,7 +587,8 @@ } }, "unstable_listView": { "type": { "name": "bool" } }, - "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } + "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" }, + "virtualizeColumnsWithAutoRowHeight": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGridPro", "imports": [ diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index a60172989398..d681d91e252e 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -485,7 +485,8 @@ }, "additionalInfo": { "sx": true } }, - "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } + "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" }, + "virtualizeColumnsWithAutoRowHeight": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGrid", "imports": [ diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index f32970f0f23a..f28eb0895d65 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -669,6 +669,9 @@ }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." + }, + "virtualizeColumnsWithAutoRowHeight": { + "description": "If true, the Data Grid enables column virtualization when getRowHeight is set to () => 'auto'. By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. For datasets with a large number of columns, this can cause performance issues. The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally." } }, "classDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index c946f52e992b..c39f3dd3c28e 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -607,6 +607,9 @@ }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." + }, + "virtualizeColumnsWithAutoRowHeight": { + "description": "If true, the Data Grid enables column virtualization when getRowHeight is set to () => 'auto'. By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. For datasets with a large number of columns, this can cause performance issues. The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally." } }, "classDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index dc476f345373..d0a980e2258c 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -487,6 +487,9 @@ }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." + }, + "virtualizeColumnsWithAutoRowHeight": { + "description": "If true, the Data Grid enables column virtualization when getRowHeight is set to () => 'auto'. By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. For datasets with a large number of columns, this can cause performance issues. The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally." } }, "classDescriptions": { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 0f05ca94e442..0566094f468a 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -1127,6 +1127,14 @@ DataGridPremiumRaw.propTypes = { * @default false */ unstable_rowSpanning: PropTypes.bool, + /** + * If `true`, the Data Grid enables column virtualization when `getRowHeight` is set to `() => 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: PropTypes.bool, } as any; interface DataGridPremiumComponent { diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index cf10a788ce68..206d7489f3dd 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -1026,4 +1026,12 @@ DataGridProRaw.propTypes = { * @default false */ unstable_rowSpanning: PropTypes.bool, + /** + * If `true`, the Data Grid enables column virtualization when `getRowHeight` is set to `() => 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: PropTypes.bool, } as any; diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index ccda733ed0de..2c33f6ec8e70 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -821,4 +821,12 @@ DataGridRaw.propTypes = { * @default false */ unstable_rowSpanning: PropTypes.bool, + /** + * If `true`, the Data Grid enables column virtualization when `getRowHeight` is set to `() => 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: PropTypes.bool, } as any; diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index f81ba9e89e3d..54511abba199 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -343,6 +343,7 @@ const GridCell = React.forwardRef(function GridCe padding: 0, opacity: 0, width: 0, + height: 0, border: 0, }; } diff --git a/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts b/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts index 689aa164e7a0..4c69790cebb6 100644 --- a/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts +++ b/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts @@ -59,4 +59,5 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { sortingOrder: ['asc' as const, 'desc' as const, null], throttleRowsMs: 0, unstable_rowSpanning: false, + virtualizeColumnsWithAutoRowHeight: false, }; 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 86435660917e..ccd1707d75e6 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -33,6 +33,7 @@ import type { GridRowEntry, GridRowId, } from '../../../models'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { selectedIdsLookupSelector } from '../rowSelection/gridRowSelectionSelector'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils'; @@ -640,6 +641,7 @@ type RenderContextInputs = { visibleColumns: ReturnType; hiddenCellsOriginMap: ReturnType; listView: boolean; + virtualizeColumnsWithAutoRowHeight: DataGridProcessedProps['virtualizeColumnsWithAutoRowHeight']; }; function inputsSelector( @@ -677,6 +679,7 @@ function inputsSelector( visibleColumns, hiddenCellsOriginMap, listView: rootProps.unstable_listView ?? false, + virtualizeColumnsWithAutoRowHeight: rootProps.virtualizeColumnsWithAutoRowHeight, }; } @@ -740,12 +743,14 @@ function computeRenderContext( lastSize: inputs.lastRowHeight, }); - for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { - const row = inputs.rows[i]; - hasRowWithAutoHeight = inputs.apiRef.current.rowHasAutoHeight(row.id); + if (!inputs.virtualizeColumnsWithAutoRowHeight) { + for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { + const row = inputs.rows[i]; + hasRowWithAutoHeight = inputs.apiRef.current.rowHasAutoHeight(row.id); + } } - if (!hasRowWithAutoHeight) { + if (!hasRowWithAutoHeight || inputs.virtualizeColumnsWithAutoRowHeight) { firstColumnIndex = binarySearch(realLeft, inputs.columnPositions, { atStart: true, lastPosition: inputs.columnsTotalWidth, diff --git a/packages/x-data-grid/src/models/props/DataGridProps.ts b/packages/x-data-grid/src/models/props/DataGridProps.ts index b35fa21f220e..abf591bedcd5 100644 --- a/packages/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/x-data-grid/src/models/props/DataGridProps.ts @@ -396,6 +396,14 @@ export interface DataGridPropsWithDefaultValues 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: boolean; } /**