From 0d0d88cacf6f0a57e8bd798306cc8fb87f4c4b06 Mon Sep 17 00:00:00 2001 From: lauri865 Date: Thu, 28 Nov 2024 22:52:44 +0100 Subject: [PATCH 1/6] fix memo (grid header, cells, rows) fix memo (grid header, cells, rows) fix tests fix row re-rendering on cell focus change fix gridheaders re-rendering on each focus change move grid headers, body and footer to GridRoot, and memoize GridRoot fix VirtualScroller re-rendering on each focus change fix virtualRow in pinned rows fix focusedVirtualCell potentially depending on stale rendercontext undo some testing changes docs:api whoopsie fix pinned skeleton overlays move PinnedPosition enum to internals rename to PinnedColumnPosition rebuild docs api Update packages/x-data-grid/src/components/containers/GridRoot.tsx Co-authored-by: Rom Grk Signed-off-by: Lauri fastmemo undo erroneous change gridheaderfilter styles change reduce lookup cost alternative lookup with maps fix useGridVisibleRows memoization don't filter rows is there's no depth fix filter selector make it nicer test with row lookupMap unit tests passing + clean up a bit docs:api attachPinnedStyle lint fix up virtual focus cell selector pass only necessary dimensions into the GridRow rebase and refactor #15929 proptypes fix double-rendering of pinned columns introduced by #15116 fix tests re-evaluate renderContext on toggling virtualization to avoid manual overrides to renderContext fix tests lint --- docs/pages/x/api/data-grid/selectors.json | 7 + .../src/DataGridPremium/DataGridPremium.tsx | 15 +- .../useDataGridPremiumComponent.tsx | 2 +- .../src/DataGridPro/DataGridPro.tsx | 15 +- .../DataGridPro/useDataGridProComponent.tsx | 2 +- .../headerFiltering/GridHeaderFilterCell.tsx | 60 ++++---- .../columnHeaders/useGridColumnHeaders.tsx | 40 +++-- .../x-data-grid/src/DataGrid/DataGrid.tsx | 8 +- .../src/DataGrid/useDataGridComponent.tsx | 2 +- .../src/components/GridHeaders.tsx | 8 +- .../x-data-grid/src/components/GridRow.tsx | 127 +++++---------- .../components/GridSkeletonLoadingOverlay.tsx | 66 ++++---- .../src/components/cell/GridCell.tsx | 69 +++------ .../columnHeaders/GridColumnGroupHeader.tsx | 44 +++--- .../columnHeaders/GridColumnHeaderItem.tsx | 50 +++--- .../GridCellCheckboxRenderer.tsx | 9 +- .../src/components/containers/GridRoot.tsx | 14 +- .../columnHeaders/useGridColumnHeaders.tsx | 144 +++++++----------- .../features/filter/gridFilterSelector.ts | 11 +- .../pagination/gridPaginationInterfaces.ts | 3 + .../pagination/gridPaginationSelector.ts | 55 +++++++ .../features/pagination/useGridPagination.ts | 5 + .../pagination/useGridPaginationModel.ts | 32 +++- .../hooks/features/rows/gridRowsSelector.ts | 4 +- .../src/hooks/features/rows/useGridRows.ts | 4 +- .../gridFocusedVirtualCellSelector.ts | 74 +++++++++ .../virtualization/useGridVirtualScroller.tsx | 90 +++++------ .../src/hooks/utils/useGridVisibleRows.ts | 45 ++---- .../x-data-grid/src/internals/constants.ts | 6 + packages/x-data-grid/src/internals/index.ts | 1 + .../src/internals/utils/attachPinnedStyle.ts | 11 ++ .../internals/utils/getPinnedCellOffset.ts | 27 ++-- .../x-data-grid/src/internals/utils/index.ts | 1 + .../x-data-grid/src/utils/cellBorderUtils.ts | 14 +- packages/x-data-grid/src/utils/rtlFlipSide.ts | 27 ++++ .../x-license/src/Watermark/Watermark.tsx | 6 +- packages/x-license/tsconfig.build.json | 1 + scripts/x-data-grid-premium.exports.json | 2 + scripts/x-data-grid-pro.exports.json | 2 + scripts/x-data-grid.exports.json | 2 + scripts/x-license.exports.json | 2 +- 41 files changed, 600 insertions(+), 507 deletions(-) create mode 100644 packages/x-data-grid/src/hooks/features/virtualization/gridFocusedVirtualCellSelector.ts create mode 100644 packages/x-data-grid/src/internals/utils/attachPinnedStyle.ts create mode 100644 packages/x-data-grid/src/utils/rtlFlipSide.ts diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json index 9ca1f13812d29..d3ffa8cabc06d 100644 --- a/docs/pages/x/api/data-grid/selectors.json +++ b/docs/pages/x/api/data-grid/selectors.json @@ -552,6 +552,13 @@ "description": "Get the visible pinned columns.", "supportsApiRef": true }, + { + "name": "gridVisibleRowsSelector", + "returnType": "{ rows: GridRowEntry[]; range: { firstRowIndex: number; lastRowIndex: number } | null; rowToIndexMap: Map }", + "category": "Pagination", + "description": "Get the rows, range and rowIndex lookup map after filtering and sorting.\nDoes not contain the collapsed children.", + "supportsApiRef": true + }, { "name": "selectedGridRowsCountSelector", "returnType": "number", diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 6160575fb7ff9..c744c98726147 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -2,14 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useLicenseVerifier, Watermark } from '@mui/x-license'; -import { - GridBody, - GridFooterPlaceholder, - GridHeader, - GridRoot, - GridContextProvider, - GridValidRowModel, -} from '@mui/x-data-grid-pro'; +import { GridRoot, GridContextProvider, GridValidRowModel } from '@mui/x-data-grid-pro'; import { propValidatorsDataGrid, propValidatorsDataGridPro, @@ -63,11 +56,7 @@ const DataGridPremiumRaw = forwardRef(function DataGridPremium - - - - - + ); diff --git a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx index 4e357096df17f..d357458259595 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx @@ -132,6 +132,7 @@ export const useDataGridPremiumComponent = ( useGridInitializeState(columnPinningStateInitializer, apiRef, props); useGridInitializeState(columnsStateInitializer, apiRef, props); useGridInitializeState(rowPinningStateInitializer, apiRef, props); + useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsStateInitializer, apiRef, props); useGridInitializeState(editingStateInitializer, apiRef, props); useGridInitializeState(focusStateInitializer, apiRef, props); @@ -142,7 +143,6 @@ export const useDataGridPremiumComponent = ( useGridInitializeState(densityStateInitializer, apiRef, props); useGridInitializeState(columnReorderStateInitializer, apiRef, props); useGridInitializeState(columnResizeStateInitializer, apiRef, props); - useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsMetaStateInitializer, apiRef, props); useGridInitializeState(columnMenuStateInitializer, apiRef, props); useGridInitializeState(columnGroupsStateInitializer, apiRef, props); diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index bbb6ac6472ec9..206136b43bc68 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -2,14 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useLicenseVerifier, Watermark } from '@mui/x-license'; -import { - GridBody, - GridFooterPlaceholder, - GridHeader, - GridRoot, - GridContextProvider, - GridValidRowModel, -} from '@mui/x-data-grid'; +import { GridRoot, GridContextProvider, GridValidRowModel } from '@mui/x-data-grid'; import { validateProps } from '@mui/x-data-grid/internals'; import { forwardRef } from '@mui/x-internals/forwardRef'; import { useDataGridProComponent } from './useDataGridProComponent'; @@ -50,11 +43,7 @@ const DataGridProRaw = forwardRef(function DataGridPro - - - - - + ); diff --git a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx index 58a36ea9d2b21..ac954d606fa22 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx @@ -117,6 +117,7 @@ export const useDataGridProComponent = ( useGridInitializeState(columnPinningStateInitializer, apiRef, props); useGridInitializeState(columnsStateInitializer, apiRef, props); useGridInitializeState(rowPinningStateInitializer, apiRef, props); + useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsStateInitializer, apiRef, props); useGridInitializeState(editingStateInitializer, apiRef, props); useGridInitializeState(focusStateInitializer, apiRef, props); @@ -127,7 +128,6 @@ export const useDataGridProComponent = ( useGridInitializeState(densityStateInitializer, apiRef, props); useGridInitializeState(columnReorderStateInitializer, apiRef, props); useGridInitializeState(columnResizeStateInitializer, apiRef, props); - useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsMetaStateInitializer, apiRef, props); useGridInitializeState(columnMenuStateInitializer, apiRef, props); useGridInitializeState(columnGroupsStateInitializer, apiRef, props); diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index e87ba097dd178..9e1867fcb84ee 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -22,17 +22,18 @@ import { GridFilterInputSingleSelect, gridFilterModelSelector, gridFilterableColumnLookupSelector, - GridPinnedColumnPosition, } from '@mui/x-data-grid'; import { + PinnedColumnPosition, GridStateColDef, useGridPrivateApiContext, gridHeaderFilteringEditFieldSelector, gridHeaderFilteringMenuSelector, isNavigationKey, - shouldCellShowLeftBorder, - shouldCellShowRightBorder, + rtlFlipSide, + attachPinnedStyle, } from '@mui/x-data-grid/internals'; +import { useRtl } from '@mui/system/RtlProvider'; import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { DataGridProProcessedProps } from '../../models/dataGridProProps'; @@ -55,16 +56,16 @@ export interface GridHeaderFilterCellProps extends Pick { 'withBorderColor', showRightBorder && 'columnHeader--withRightBorder', showLeftBorder && 'columnHeader--withLeftBorder', - pinnedPosition === 'left' && 'columnHeader--pinnedLeft', - pinnedPosition === 'right' && 'columnHeader--pinnedRight', + pinnedPosition === PinnedColumnPosition.LEFT && 'columnHeader--pinnedLeft', + pinnedPosition === PinnedColumnPosition.RIGHT && 'columnHeader--pinnedRight', ], }; @@ -115,14 +116,15 @@ const GridHeaderFilterCell = forwardRef(null); @@ -282,15 +284,6 @@ const GridHeaderFilterCell = forwardRef { ); const { getColumnsToRender, + getPinnedCellOffset, renderContext, leftRenderContext, rightRenderContext, pinnedColumns, visibleColumns, - getCellOffsetStyle, + columnPositions, ...otherProps } = useGridColumnHeadersCommunity({ ...props, @@ -117,11 +120,27 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const item = getFilterItem(colDef); const pinnedPosition = params?.position; - const style = getCellOffsetStyle({ + const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; + const pinnedOffset = getPinnedCellOffset( pinnedPosition, + colDef.computedWidth, columnIndex, - computedWidth: colDef.computedWidth, - }); + columnPositions, + dimensions.columnsTotalWidth, + scrollbarWidth, + ); + + const indexInSection = i; + const sectionLength = renderedColumns.length; + + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + sectionLength, + rootProps.showCellVerticalBorder, + gridHasFiller, + ); filters.push( { data-field={colDef.field} item={item} pinnedPosition={pinnedPosition} - style={style} - indexInSection={i} - sectionLength={renderedColumns.length} - gridHasFiller={gridHasFiller} + pinnedOffset={pinnedOffset} + showLeftBorder={showLeftBorder} + showRightBorder={showRightBorder} {...rootProps.slotProps?.headerFilterCell} />, ); @@ -164,7 +182,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { > {leftRenderContext && getColumnFilters({ - position: GridPinnedColumnPosition.LEFT, + position: PinnedColumnPosition.LEFT, renderContext: leftRenderContext, maxLastColumn: leftRenderContext.lastColumnIndex, })} @@ -174,7 +192,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { })} {rightRenderContext && getColumnFilters({ - position: GridPinnedColumnPosition.RIGHT, + position: PinnedColumnPosition.RIGHT, renderContext: rightRenderContext, maxLastColumn: rightRenderContext.lastColumnIndex, })} diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index 0fc08b33366f3..cba9be214c7c7 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { forwardRef } from '@mui/x-internals/forwardRef'; -import { GridBody, GridFooterPlaceholder, GridHeader, GridRoot } from '../components'; +import { GridRoot } from '../components'; import { useGridAriaAttributes } from '../hooks/utils/useGridAriaAttributes'; import { useGridRowAriaAttributes } from '../hooks/features/rows/useGridRowAriaAttributes'; import { DataGridProcessedProps, DataGridProps } from '../models/props/DataGridProps'; @@ -61,11 +61,7 @@ const DataGridRaw = forwardRef(function DataGrid( sx={props.sx} {...props.slotProps?.root} ref={ref} - > - - - - + /> ); }); diff --git a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx index 827f1e48b6d8a..63b9dda14ea55 100644 --- a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx +++ b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx @@ -83,6 +83,7 @@ export const useDataGridComponent = ( useGridInitializeState(dimensionsStateInitializer, apiRef, props); useGridInitializeState(rowSelectionStateInitializer, apiRef, props); useGridInitializeState(columnsStateInitializer, apiRef, props); + useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsStateInitializer, apiRef, props); useGridInitializeState(editingStateInitializer, apiRef, props); useGridInitializeState(focusStateInitializer, apiRef, props); @@ -92,7 +93,6 @@ export const useDataGridComponent = ( useGridInitializeState(rowSpanningStateInitializer, apiRef, props); useGridInitializeState(densityStateInitializer, apiRef, props); useGridInitializeState(columnResizeStateInitializer, apiRef, props); - useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsMetaStateInitializer, apiRef, props); useGridInitializeState(columnMenuStateInitializer, apiRef, props); useGridInitializeState(columnGroupsStateInitializer, apiRef, props); diff --git a/packages/x-data-grid/src/components/GridHeaders.tsx b/packages/x-data-grid/src/components/GridHeaders.tsx index c2df6d301ce17..44e11860015e5 100644 --- a/packages/x-data-grid/src/components/GridHeaders.tsx +++ b/packages/x-data-grid/src/components/GridHeaders.tsx @@ -30,7 +30,11 @@ function GridHeaders() { const filterColumnLookup = useGridSelector(apiRef, gridFilterActiveItemsLookupSelector); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); const columnHeaderTabIndexState = useGridSelector(apiRef, gridTabIndexColumnHeaderSelector); - const cellTabIndexState = useGridSelector(apiRef, gridTabIndexCellSelector); + const hasNoCellTabIndexState = useGridSelector( + apiRef, + () => gridTabIndexCellSelector(apiRef) === null, + ); + const columnGroupHeaderTabIndexState = useGridSelector( apiRef, gridTabIndexColumnGroupHeaderSelector, @@ -51,7 +55,7 @@ function GridHeaders() { const hasOtherElementInTabSequence = !( columnGroupHeaderTabIndexState === null && columnHeaderTabIndexState === null && - cellTabIndexState === null + hasNoCellTabIndexState ); const columnsContainerRef = apiRef.current.columnHeadersContainerRef; diff --git a/packages/x-data-grid/src/components/GridRow.tsx b/packages/x-data-grid/src/components/GridRow.tsx index 7af29485e080d..894a34fe769aa 100644 --- a/packages/x-data-grid/src/components/GridRow.tsx +++ b/packages/x-data-grid/src/components/GridRow.tsx @@ -12,7 +12,7 @@ import { composeGridClasses } from '../utils/composeGridClasses'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { GridPinnedColumns } from '../hooks/features/columns'; import type { GridStateColDef } from '../models/colDef/gridColDef'; -import type { GridRenderContext } from '../models/params/gridScrollParams'; +import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../utils/cellBorderUtils'; import { gridColumnPositionsSelector } from '../hooks/features/columns/gridColumnsSelector'; import { useGridSelector, objectShallowCompare } from '../hooks/utils/useGridSelector'; import { GridRowClassNameParams } from '../models/params/gridRowParams'; @@ -20,17 +20,14 @@ import { useGridVisibleRows } from '../hooks/utils/useGridVisibleRows'; import { findParentElementFromClassName, isEventTargetInPortal } from '../utils/domUtils'; import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../colDef/gridCheckboxSelectionColDef'; import { GRID_ACTIONS_COLUMN_TYPE } from '../colDef/gridActionsColDef'; -import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from '../internals/constants'; -import type { GridDimensions } from '../hooks/features/dimensions'; +import { GRID_DETAIL_PANEL_TOGGLE_FIELD, PinnedColumnPosition } from '../internals/constants'; import { gridSortModelSelector } from '../hooks/features/sorting/gridSortingSelector'; import { gridRowMaximumTreeDepthSelector } from '../hooks/features/rows/gridRowsSelector'; import { gridEditRowsStateSelector } from '../hooks/features/editing/gridEditingSelectors'; -import { PinnedPosition, gridPinnedColumnPositionLookup } from './cell/GridCell'; import { GridScrollbarFillerCell as ScrollbarFiller } from './GridScrollbarFillerCell'; import { getPinnedCellOffset } from '../internals/utils/getPinnedCellOffset'; import { useGridConfiguration } from '../hooks/utils/useGridConfiguration'; import { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext'; -import { gridVirtualizationColumnEnabledSelector } from '../hooks'; export interface GridRowProps extends React.HTMLAttributes { row: GridRowModel; @@ -44,8 +41,9 @@ export interface GridRowProps extends React.HTMLAttributes { rowHeight: number | 'auto'; offsetTop: number | undefined; offsetLeft: number; - dimensions: GridDimensions; - renderContext: GridRenderContext; + columnsTotalWidth: number; + firstColumnIndex: number; + lastColumnIndex: number; visibleColumns: GridStateColDef[]; pinnedColumns: GridPinnedColumns; /** @@ -53,15 +51,12 @@ export interface GridRowProps extends React.HTMLAttributes { * If `null`, no cell in this row has focus. */ focusedColumnIndex: number | undefined; - /** - * Determines which cell should be tabbable by having tabIndex=0. - * If `null`, no cell in this row is in the tab sequence. - */ - tabbableCell: string | null; isFirstVisible: boolean; isLastVisible: boolean; isNotVisible: boolean; showBottomBorder: boolean; + scrollbarWidth: number; + gridHasFiller: boolean; onClick?: React.MouseEventHandler; onDoubleClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; @@ -82,14 +77,16 @@ const GridRow = forwardRef(function GridRow(props, pinnedColumns, offsetTop, offsetLeft, - dimensions, - renderContext, + columnsTotalWidth, + firstColumnIndex, + lastColumnIndex, focusedColumnIndex, isFirstVisible, isLastVisible, isNotVisible, showBottomBorder, - tabbableCell, + scrollbarWidth, + gridHasFiller, onClick, onDoubleClick, onMouseEnter, @@ -109,20 +106,17 @@ const GridRow = forwardRef(function GridRow(props, const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); const handleRef = useForkRef(ref, refProp); const rowNode = apiRef.current.getRowNode(rowId); - const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; - const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width; const editing = apiRef.current.getRowMode(rowId) === GridRowModes.Edit; const editable = rootProps.editMode === GridEditModes.Row; - const hasColumnVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector); const hasFocusCell = focusedColumnIndex !== undefined; const hasVirtualFocusCellLeft = hasFocusCell && focusedColumnIndex >= pinnedColumns.left.length && - focusedColumnIndex < renderContext.firstColumnIndex; + focusedColumnIndex < firstColumnIndex; const hasVirtualFocusCellRight = hasFocusCell && focusedColumnIndex < visibleColumns.length - pinnedColumns.right.length && - focusedColumnIndex >= renderContext.lastColumnIndex; + focusedColumnIndex >= lastColumnIndex; const classes = composeGridClasses(rootProps.classes, { root: [ @@ -285,7 +279,7 @@ const GridRow = forwardRef(function GridRow(props, indexInSection: number, indexRelativeToAllColumns: number, sectionLength: number, - pinnedPosition = PinnedPosition.NONE, + pinnedPosition = PinnedColumnPosition.NONE, ) => { const cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo( rowId, @@ -300,11 +294,12 @@ const GridRow = forwardRef(function GridRow(props, const colSpan = cellColSpanInfo?.cellProps.colSpan ?? 1; const pinnedOffset = getPinnedCellOffset( - gridPinnedColumnPositionLookup[pinnedPosition], + pinnedPosition, column.computedWidth, indexRelativeToAllColumns, columnPositions, - dimensions, + columnsTotalWidth, + scrollbarWidth, ); if (rowNode?.type === 'skeletonRow') { @@ -332,7 +327,16 @@ const GridRow = forwardRef(function GridRow(props, const disableDragEvents = !(canReorderColumn || (isReorderCell && canReorderRow)); - const cellIsNotVisible = pinnedPosition === PinnedPosition.VIRTUAL; + const cellIsNotVisible = pinnedPosition === PinnedColumnPosition.VIRTUAL; + + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + sectionLength, + rootProps.showCellVerticalBorder, + gridHasFiller, + ); return ( (function GridRow(props, isNotVisible={cellIsNotVisible} pinnedOffset={pinnedOffset} pinnedPosition={pinnedPosition} - sectionIndex={indexInSection} - sectionLength={sectionLength} - gridHasFiller={gridHasFiller} + showLeftBorder={showLeftBorder} + showRightBorder={showRightBorder} {...slotProps?.cell} /> ); @@ -369,7 +372,7 @@ const GridRow = forwardRef(function GridRow(props, i, indexRelativeToAllColumns, pinnedColumns.left.length, - PinnedPosition.LEFT, + PinnedColumnPosition.LEFT, ); }); @@ -380,7 +383,7 @@ const GridRow = forwardRef(function GridRow(props, i, indexRelativeToAllColumns, pinnedColumns.right.length, - PinnedPosition.RIGHT, + PinnedColumnPosition.RIGHT, ); }); @@ -395,19 +398,10 @@ const GridRow = forwardRef(function GridRow(props, focusedColumnIndex - pinnedColumns.left.length, focusedColumnIndex, middleColumnsLength, - PinnedPosition.VIRTUAL, + PinnedColumnPosition.VIRTUAL, ), ); } - let firstColumnIndex; - let lastColumnIndex; - if (!rootProps.disableVirtualization && !hasColumnVirtualization) { - firstColumnIndex = 0; - lastColumnIndex = visibleColumns.length; - } else { - firstColumnIndex = renderContext.firstColumnIndex; - lastColumnIndex = renderContext.lastColumnIndex; - } for (let i = firstColumnIndex; i < lastColumnIndex; i += 1) { const column = visibleColumns[i]; @@ -427,7 +421,7 @@ const GridRow = forwardRef(function GridRow(props, focusedColumnIndex - pinnedColumns.left.length, focusedColumnIndex, middleColumnsLength, - PinnedPosition.VIRTUAL, + PinnedColumnPosition.VIRTUAL, ), ); } @@ -474,48 +468,14 @@ GridRow.propTypes = { // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "pnpm proptypes" | // ---------------------------------------------------------------------- - dimensions: PropTypes.shape({ - bottomContainerHeight: PropTypes.number.isRequired, - columnsTotalWidth: PropTypes.number.isRequired, - contentSize: PropTypes.shape({ - height: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - }).isRequired, - groupHeaderHeight: PropTypes.number.isRequired, - hasScrollX: PropTypes.bool.isRequired, - hasScrollY: PropTypes.bool.isRequired, - headerFilterHeight: PropTypes.number.isRequired, - headerHeight: PropTypes.number.isRequired, - headersTotalHeight: PropTypes.number.isRequired, - isReady: PropTypes.bool.isRequired, - leftPinnedWidth: PropTypes.number.isRequired, - minimumSize: PropTypes.shape({ - height: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - }).isRequired, - rightPinnedWidth: PropTypes.number.isRequired, - root: PropTypes.shape({ - height: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - }).isRequired, - rowHeight: PropTypes.number.isRequired, - rowWidth: PropTypes.number.isRequired, - scrollbarSize: PropTypes.number.isRequired, - topContainerHeight: PropTypes.number.isRequired, - viewportInnerSize: PropTypes.shape({ - height: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - }).isRequired, - viewportOuterSize: PropTypes.shape({ - height: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - }).isRequired, - }).isRequired, + columnsTotalWidth: PropTypes.number.isRequired, + firstColumnIndex: PropTypes.number.isRequired, /** * Determines which cell has focus. * If `null`, no cell in this row has focus. */ focusedColumnIndex: PropTypes.number, + gridHasFiller: PropTypes.bool.isRequired, /** * Index of the row in the whole sorted and filtered dataset. * If some rows above have expanded children, this index also take those children into account. @@ -524,6 +484,7 @@ GridRow.propTypes = { isFirstVisible: PropTypes.bool.isRequired, isLastVisible: PropTypes.bool.isRequired, isNotVisible: PropTypes.bool.isRequired, + lastColumnIndex: PropTypes.number.isRequired, offsetLeft: PropTypes.number.isRequired, offsetTop: PropTypes.number, onClick: PropTypes.func, @@ -531,22 +492,12 @@ GridRow.propTypes = { onMouseEnter: PropTypes.func, onMouseLeave: PropTypes.func, pinnedColumns: PropTypes.object.isRequired, - renderContext: PropTypes.shape({ - firstColumnIndex: PropTypes.number.isRequired, - firstRowIndex: PropTypes.number.isRequired, - lastColumnIndex: PropTypes.number.isRequired, - lastRowIndex: PropTypes.number.isRequired, - }).isRequired, row: PropTypes.object.isRequired, rowHeight: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]).isRequired, rowId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + scrollbarWidth: PropTypes.number.isRequired, selected: PropTypes.bool.isRequired, showBottomBorder: PropTypes.bool.isRequired, - /** - * Determines which cell should be tabbable by having tabIndex=0. - * If `null`, no cell in this row is in the tab sequence. - */ - tabbableCell: PropTypes.string, visibleColumns: PropTypes.arrayOf(PropTypes.object).isRequired, } as any; diff --git a/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx b/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx index afd60790a004f..5b1e2ad89e5f1 100644 --- a/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx +++ b/packages/x-data-grid/src/components/GridSkeletonLoadingOverlay.tsx @@ -3,11 +3,11 @@ import clsx from 'clsx'; import { styled } from '@mui/system'; import useForkRef from '@mui/utils/useForkRef'; import composeClasses from '@mui/utils/composeClasses'; +import { useRtl } from '@mui/system/RtlProvider'; import { forwardRef } from '@mui/x-internals/forwardRef'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { - GridPinnedColumnPosition, gridColumnPositionsSelector, gridColumnsTotalWidthSelector, gridDimensionsSelector, @@ -16,6 +16,7 @@ import { useGridApiEventHandler, useGridSelector, } from '../hooks'; +import { PinnedColumnPosition } from '../internals/constants'; import { GridEventListener } from '../models'; import { DataGridProcessedProps } from '../models/props/DataGridProps'; import { getDataGridUtilityClass, gridClasses } from '../constants/gridClasses'; @@ -23,6 +24,8 @@ import { getPinnedCellOffset } from '../internals/utils/getPinnedCellOffset'; import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../utils/cellBorderUtils'; import { escapeOperandAttributeSelector } from '../utils/domUtils'; import { GridScrollbarFillerCell } from './GridScrollbarFillerCell'; +import { rtlFlipSide } from '../utils/rtlFlipSide'; +import { attachPinnedStyle } from '../internals/utils'; const SkeletonOverlay = styled('div', { name: 'MuiDataGrid', @@ -53,6 +56,7 @@ const GridSkeletonLoadingOverlay = forwardRef(null); const handleRef = useForkRef(ref, forwardedRef); @@ -73,27 +77,13 @@ const GridSkeletonLoadingOverlay = forwardRef { - const pinnedOffset = getPinnedCellOffset( - position, - computedWidth, - index, - positions, - dimensions, - ); - return { [position]: pinnedOffset } as const; - }, - [dimensions, positions], - ); - const getPinnedPosition = React.useCallback( (field: string) => { if (pinnedColumns.left.findIndex((col) => col.field === field) !== -1) { - return GridPinnedColumnPosition.LEFT; + return PinnedColumnPosition.LEFT; } if (pinnedColumns.right.findIndex((col) => col.field === field) !== -1) { - return GridPinnedColumnPosition.RIGHT; + return PinnedColumnPosition.RIGHT; } return undefined; }, @@ -109,16 +99,28 @@ const GridSkeletonLoadingOverlay = forwardRef col.field === column.field) // pinned section + const sectionIndex = pinnedSide + ? pinnedColumns[pinnedSide].findIndex((col) => col.field === column.field) // pinned section : colIndex - pinnedColumns.left.length; // middle section - const pinnedStyle = - pinnedPosition && getPinnedStyle(column.computedWidth, colIndex, pinnedPosition); + const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; + const pinnedStyle = attachPinnedStyle( + {}, + pinnedSide, + getPinnedCellOffset( + pinnedPosition, + column.computedWidth, + colIndex, + positions, + dimensions.columnsTotalWidth, + scrollbarWidth, + ), + ); const gridHasFiller = dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width; const showRightBorder = shouldCellShowRightBorder( pinnedPosition, @@ -137,7 +139,6 @@ const GridSkeletonLoadingOverlay = forwardRef ); - const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; const hasScrollbarFiller = isLastColumn && scrollbarWidth !== 0; if (hasFillerBefore) { @@ -199,13 +200,10 @@ const GridSkeletonLoadingOverlay = forwardRef col.field === colDef.field); const pinnedPosition = getPinnedPosition(colDef.field); - const isPinnedLeft = pinnedPosition === GridPinnedColumnPosition.LEFT; - const isPinnedRight = pinnedPosition === GridPinnedColumnPosition.RIGHT; + const isPinnedLeft = pinnedPosition === PinnedColumnPosition.LEFT; + const isPinnedRight = pinnedPosition === PinnedColumnPosition.RIGHT; const currentWidth = getComputedStyle(cells[0]).getPropertyValue('--width'); const delta = parseInt(currentWidth, 10) - width; diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index 92ee0efc8f47d..824711f95f93d 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -10,6 +10,7 @@ import { import { fastMemo } from '@mui/x-internals/fastMemo'; import { useRtl } from '@mui/system/RtlProvider'; import { forwardRef } from '@mui/x-internals/forwardRef'; +import { rtlFlipSide } from '../../utils/rtlFlipSide'; import { doesSupportPreventScroll } from '../../utils/doesSupportPreventScroll'; import { getDataGridUtilityClass, gridClasses } from '../../constants/gridClasses'; import { @@ -32,25 +33,19 @@ import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { gridFocusCellSelector } from '../../hooks/features/focus/gridFocusStateSelector'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; -import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../../utils/cellBorderUtils'; import { GridPinnedColumnPosition } from '../../hooks/features/columns/gridColumnsInterfaces'; +import { PinnedColumnPosition } from '../../internals/constants'; import { gridRowSpanningHiddenCellsSelector, gridRowSpanningSpannedCellsSelector, } from '../../hooks/features/rows/gridRowSpanningSelectors'; - -export enum PinnedPosition { - NONE, - LEFT, - RIGHT, - VIRTUAL, -} +import { attachPinnedStyle } from '../../internals/utils'; export const gridPinnedColumnPositionLookup = { - [PinnedPosition.LEFT]: GridPinnedColumnPosition.LEFT, - [PinnedPosition.RIGHT]: GridPinnedColumnPosition.RIGHT, - [PinnedPosition.NONE]: undefined, - [PinnedPosition.VIRTUAL]: undefined, + [PinnedColumnPosition.LEFT]: GridPinnedColumnPosition.LEFT, + [PinnedColumnPosition.RIGHT]: GridPinnedColumnPosition.RIGHT, + [PinnedColumnPosition.NONE]: undefined, + [PinnedColumnPosition.VIRTUAL]: undefined, }; export type GridCellProps = React.HTMLAttributes & { @@ -64,11 +59,10 @@ export type GridCellProps = React.HTMLAttributes & { disableDragEvents?: boolean; isNotVisible: boolean; editCellState: GridEditCellProps | null; - pinnedOffset: number; - pinnedPosition: PinnedPosition; - sectionIndex: number; - sectionLength: number; - gridHasFiller: boolean; + pinnedOffset?: number; + pinnedPosition: PinnedColumnPosition; + showRightBorder: boolean; + showLeftBorder: boolean; onClick?: React.MouseEventHandler; onDoubleClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; @@ -140,8 +134,8 @@ const useUtilityClasses = (ownerState: OwnerState) => { isEditable && 'cell--editable', showLeftBorder && 'cell--withLeftBorder', showRightBorder && 'cell--withRightBorder', - pinnedPosition === PinnedPosition.LEFT && 'cell--pinnedLeft', - pinnedPosition === PinnedPosition.RIGHT && 'cell--pinnedRight', + pinnedPosition === PinnedColumnPosition.LEFT && 'cell--pinnedLeft', + pinnedPosition === PinnedColumnPosition.RIGHT && 'cell--pinnedRight', isSelectionMode && !isEditable && 'cell--selectionMode', ], }; @@ -169,9 +163,8 @@ const GridCell = forwardRef(function GridCell(pro isNotVisible, pinnedOffset, pinnedPosition, - sectionIndex, - sectionLength, - gridHasFiller, + showRightBorder, + showLeftBorder, onClick, onDoubleClick, onMouseDown, @@ -269,16 +262,6 @@ const GridCell = forwardRef(function GridCell(pro const focusElementRef = React.useRef(null); const isSelectionMode = rootProps.cellSelection ?? false; - const position = gridPinnedColumnPositionLookup[pinnedPosition]; - const showLeftBorder = shouldCellShowLeftBorder(position, sectionIndex); - const showRightBorder = shouldCellShowRightBorder( - position, - sectionIndex, - sectionLength, - rootProps.showCellVerticalBorder, - gridHasFiller, - ); - const ownerState = { align, showLeftBorder, @@ -352,18 +335,11 @@ const GridCell = forwardRef(function GridCell(pro ...styleProp, } as React.CSSProperties; - const isLeftPinned = pinnedPosition === PinnedPosition.LEFT; - const isRightPinned = pinnedPosition === PinnedPosition.RIGHT; - - if (isLeftPinned || isRightPinned) { - let side: 'left' | 'right' = isLeftPinned ? 'left' : 'right'; + const isLeftPinned = pinnedPosition === PinnedColumnPosition.LEFT; + const isRightPinned = pinnedPosition === PinnedColumnPosition.RIGHT; - if (isRtl) { - side = isLeftPinned ? 'right' : 'left'; - } - - cellStyle[side] = pinnedOffset; - } + const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); + attachPinnedStyle(cellStyle, pinnedSide, pinnedOffset); if (rowSpan > 1) { cellStyle.height = `calc(var(--height) * ${rowSpan})`; @@ -533,13 +509,12 @@ GridCell.propTypes = { isValidating: PropTypes.bool, value: PropTypes.any, }), - gridHasFiller: PropTypes.bool.isRequired, isNotVisible: PropTypes.bool.isRequired, - pinnedOffset: PropTypes.number.isRequired, + pinnedOffset: PropTypes.number, pinnedPosition: PropTypes.oneOf([0, 1, 2, 3]).isRequired, rowId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - sectionIndex: PropTypes.number.isRequired, - sectionLength: PropTypes.number.isRequired, + showLeftBorder: PropTypes.bool.isRequired, + showRightBorder: PropTypes.bool.isRequired, width: PropTypes.number.isRequired, } as any; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx index d603c91772ad0..08e0ad02ff137 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; import { unstable_useId as useId, unstable_composeClasses as composeClasses } from '@mui/utils'; +import { useRtl } from '@mui/system/RtlProvider'; +import { rtlFlipSide } from '../../utils/rtlFlipSide'; import { GridAlignment } from '../../models/colDef/gridColDef'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -12,8 +14,8 @@ import { GridColumnGroup } from '../../models/gridColumnGrouping'; import { GridColumnGroupHeaderEventLookup } from '../../models/events'; import { GridColumnGroupHeaderParams } from '../../models/params'; import { isEventTargetInPortal } from '../../utils/domUtils'; -import { GridPinnedColumnPosition } from '../../hooks/features/columns/gridColumnsInterfaces'; -import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../../utils/cellBorderUtils'; +import { PinnedColumnPosition } from '../../internals/constants'; +import { attachPinnedStyle } from '../../internals/utils'; interface GridColumnGroupHeaderProps { groupId: string | null; @@ -26,11 +28,11 @@ interface GridColumnGroupHeaderProps { height: number; hasFocus?: boolean; tabIndex: 0 | -1; - pinnedPosition?: GridPinnedColumnPosition; style?: React.CSSProperties; - indexInSection: number; - sectionLength: number; - gridHasFiller: boolean; + showLeftBorder: boolean; + showRightBorder: boolean; + pinnedPosition: PinnedColumnPosition | undefined; + pinnedOffset?: number; } type OwnerState = { @@ -41,7 +43,7 @@ type OwnerState = { isLastColumn: boolean; headerAlign?: GridAlignment; classes?: DataGridProcessedProps['classes']; - pinnedPosition?: GridPinnedColumnPosition; + pinnedPosition?: PinnedColumnPosition | undefined; }; const useUtilityClasses = (ownerState: OwnerState) => { @@ -67,8 +69,8 @@ const useUtilityClasses = (ownerState: OwnerState) => { showLeftBorder && 'columnHeader--withLeftBorder', 'withBorderColor', groupId === null ? 'columnHeader--emptyGroup' : 'columnHeader--filledGroup', - pinnedPosition === 'left' && 'columnHeader--pinnedLeft', - pinnedPosition === 'right' && 'columnHeader--pinnedRight', + pinnedPosition === PinnedColumnPosition.LEFT && 'columnHeader--pinnedLeft', + pinnedPosition === PinnedColumnPosition.RIGHT && 'columnHeader--pinnedRight', isLastColumn && 'columnHeader--last', ], draggableContainer: ['columnHeaderDraggableContainer'], @@ -92,13 +94,11 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { tabIndex, isLastColumn, pinnedPosition, - style, - indexInSection, - sectionLength, - gridHasFiller, + pinnedOffset, } = props; const rootProps = useGridRootProps(); + const isRtl = useRtl(); const headerCellRef = React.useRef(null); const apiRef = useGridApiContext(); @@ -128,20 +128,9 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { headerComponent = render(renderParams); } - const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); - const showRightBorder = shouldCellShowRightBorder( - pinnedPosition, - indexInSection, - sectionLength, - rootProps.showColumnVerticalBorder, - gridHasFiller, - ); - const ownerState = { ...props, classes: rootProps.classes, - showLeftBorder, - showRightBorder, headerAlign, depth, isDragging: false, @@ -189,6 +178,13 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { ? group.headerClassName(renderParams) : group.headerClassName; + const style = React.useMemo(() => { + const styleProp = { ...props.style }; + const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); + attachPinnedStyle(styleProp, pinnedSide, pinnedOffset); + return styleProp; + }, [pinnedPosition, pinnedOffset, props.style, isRtl]); + return ( { 'withBorderColor', showRightBorder && 'columnHeader--withRightBorder', showLeftBorder && 'columnHeader--withLeftBorder', - pinnedPosition === 'left' && 'columnHeader--pinnedLeft', - pinnedPosition === 'right' && 'columnHeader--pinnedRight', + pinnedPosition === PinnedColumnPosition.LEFT && 'columnHeader--pinnedLeft', + pinnedPosition === PinnedColumnPosition.RIGHT && 'columnHeader--pinnedRight', // TODO: Remove classes below and restore `:has` selectors when they are supported in jsdom // See https://github.com/mui/mui-x/pull/14559 isLastUnpinned && 'columnHeader--lastUnpinned', @@ -111,14 +113,14 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { tabIndex, disableReorder, separatorSide, - style, + showLeftBorder, + showRightBorder, pinnedPosition, - indexInSection, - sectionLength, - gridHasFiller, + pinnedOffset, } = props; const apiRef = useGridPrivateApiContext(); const rootProps = useGridRootProps(); + const isRtl = useRtl(); const headerCellRef = React.useRef(null); const columnMenuId = useId(); const columnMenuButtonId = useId(); @@ -135,15 +137,6 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { headerComponent = colDef.renderHeader(apiRef.current.getColumnHeaderParams(colDef.field)); } - const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); - const showRightBorder = shouldCellShowRightBorder( - pinnedPosition, - indexInSection, - sectionLength, - rootProps.showColumnVerticalBorder, - gridHasFiller, - ); - const ownerState = { ...props, classes: rootProps.classes, @@ -287,6 +280,13 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { const label = colDef.headerName ?? colDef.field; + const style = React.useMemo(() => { + const styleProp = { ...props.style }; + const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); + attachPinnedStyle(styleProp, pinnedSide, pinnedOffset); + return styleProp; + }, [pinnedPosition, pinnedOffset, props.style, isRtl]); + return ( { /** @@ -48,7 +51,7 @@ const useUtilityClasses = (ownerState: OwnerState, density: GridDensity) => { const GridRoot = forwardRef(function GridRoot(props, ref) { const rootProps = useGridRootProps(); - const { className, ...other } = props; + const { className, children, ...other } = props; const apiRef = useGridPrivateApiContext(); const density = useGridSelector(apiRef, gridDensitySelector); const rootElementRef = apiRef.current.rootElementRef; @@ -75,7 +78,11 @@ const GridRoot = forwardRef(function GridRoot(pro ownerState={ownerState} {...other} ref={handleRef} - /> + > + + {children} + + ); }); @@ -94,4 +101,5 @@ GridRoot.propTypes = { ]), } as any; -export { GridRoot }; +const MemoizedGridRoot = fastMemo(GridRoot); +export { MemoizedGridRoot as GridRoot }; 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 aae228e6aeb43..49e2157bccb32 100644 --- a/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx +++ b/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import clsx from 'clsx'; import { styled } from '@mui/material/styles'; -import { useRtl } from '@mui/system/RtlProvider'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { useGridSelector } from '../../utils'; import { useGridRootProps } from '../../utils/useGridRootProps'; @@ -11,10 +10,7 @@ import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEventListener } from '../../../models/events'; import { GridColumnHeaderItem } from '../../../components/columnHeaders/GridColumnHeaderItem'; import { gridDimensionsSelector } from '../dimensions'; -import { - gridRenderContextColumnsSelector, - gridVirtualizationColumnEnabledSelector, -} from '../virtualization'; +import { gridRenderContextColumnsSelector } from '../virtualization'; import { computeOffsetLeft } from '../virtualization/useGridVirtualScroller'; import { GridColumnGroupHeader } from '../../../components/columnHeaders/GridColumnGroupHeader'; import { GridColumnGroup } from '../../../models/gridColumnGrouping'; @@ -24,7 +20,6 @@ import { GridFilterActiveItemsLookup } from '../filter'; import { GridColumnGroupIdentifier, GridColumnIdentifier } from '../focus'; import { GridColumnMenuState } from '../columnMenu'; import { - GridPinnedColumnPosition, GridColumnVisibilityModel, gridColumnPositionsSelector, gridVisiblePinnedColumnDefinitionsSelector, @@ -36,6 +31,11 @@ import { GridScrollbarFillerCell as ScrollbarFiller } from '../../../components/ import { getPinnedCellOffset } from '../../../internals/utils/getPinnedCellOffset'; import { GridColumnHeaderSeparatorSides } from '../../../components/columnHeaders/GridColumnHeaderSeparator'; import { gridClasses } from '../../../constants/gridClasses'; +import { + shouldCellShowLeftBorder, + shouldCellShowRightBorder, +} from '../../../utils/cellBorderUtils'; +import { PinnedColumnPosition } from '../../../internals/constants'; interface HeaderInfo { groupId: GridColumnGroup['groupId'] | null; @@ -63,7 +63,7 @@ export interface UseGridColumnHeadersProps { } export interface GetHeadersParams { - position?: GridPinnedColumnPosition; + position?: PinnedColumnPosition; renderContext?: GridColumnsRenderContext; maxLastColumn?: number; } @@ -98,10 +98,8 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const [resizeCol, setResizeCol] = React.useState(''); const apiRef = useGridPrivateApiContext(); - const isRtl = useRtl(); const rootProps = useGridRootProps(); const dimensions = useGridSelector(apiRef, gridDimensionsSelector); - const hasColumnVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector); const columnGroupsModel = useGridSelector(apiRef, gridColumnGroupsUnwrappedModelSelector); const columnPositions = useGridSelector(apiRef, gridColumnPositionsSelector); const renderContext = useGridSelector(apiRef, gridRenderContextColumnsSelector); @@ -159,18 +157,10 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { // Helper for computation common between getColumnHeaders and getColumnGroupHeaders const getColumnsToRender = (params?: GetHeadersParams) => { - const { renderContext: currentContext = renderContext, maxLastColumn = visibleColumns.length } = - params || {}; - - let firstColumnToRender; - let lastColumnToRender; - if (!rootProps.disableVirtualization && !hasColumnVirtualization) { - firstColumnToRender = 0; - lastColumnToRender = maxLastColumn; - } else { - firstColumnToRender = currentContext.firstColumnIndex; - lastColumnToRender = currentContext.lastColumnIndex; - } + const { renderContext: currentContext = renderContext } = params || {}; + + const firstColumnToRender = currentContext.firstColumnIndex; + const lastColumnToRender = currentContext.lastColumnIndex; const renderedColumns = visibleColumns.slice(firstColumnToRender, lastColumnToRender); return { @@ -186,7 +176,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { leftOverflow: number, borderBottom: boolean = false, ) => { - const isPinnedRight = params?.position === GridPinnedColumnPosition.RIGHT; + const isPinnedRight = params?.position === PinnedColumnPosition.RIGHT; const isNotPinned = params?.position === undefined; const hasScrollbarFiller = @@ -220,46 +210,6 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { ); }; - const getCellOffsetStyle = ({ - pinnedPosition, - columnIndex, - computedWidth, - }: { - pinnedPosition?: GridPinnedColumnPosition; - columnIndex: number; - computedWidth: number; - }) => { - let style: React.CSSProperties | undefined; - - const isLeftPinned = pinnedPosition === GridPinnedColumnPosition.LEFT; - const isRightPinned = pinnedPosition === GridPinnedColumnPosition.RIGHT; - - if (isLeftPinned || isRightPinned) { - const pinnedOffset = getPinnedCellOffset( - pinnedPosition, - computedWidth, - columnIndex, - columnPositions, - dimensions, - ); - let side = isLeftPinned ? 'left' : 'right'; - - if (isRtl) { - side = isLeftPinned ? 'right' : 'left'; - } - - if (pinnedPosition === 'left') { - style = { [side]: pinnedOffset }; - } - - if (pinnedPosition === 'right') { - style = { [side]: pinnedOffset }; - } - } - - return style; - }; - const getColumnHeaders = (params?: GetHeadersParams, other = {}) => { const { renderedColumns, firstColumnToRender } = getColumnsToRender(params); @@ -277,15 +227,18 @@ 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 style = getCellOffsetStyle({ + const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; + const pinnedOffset = getPinnedCellOffset( pinnedPosition, + colDef.computedWidth, columnIndex, - computedWidth: colDef.computedWidth, - }); + columnPositions, + dimensions.columnsTotalWidth, + scrollbarWidth, + ); const siblingWithBorderingSeparator = - pinnedPosition === GridPinnedColumnPosition.RIGHT + pinnedPosition === PinnedColumnPosition.RIGHT ? renderedColumns[i - 1] : renderedColumns[i + 1]; const isSiblingFocused = siblingWithBorderingSeparator @@ -295,6 +248,18 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const isLastUnpinned = columnIndex + 1 === columnPositions.length - pinnedColumns.right.length; + const indexInSection = i; + const sectionLength = renderedColumns.length; + + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + sectionLength, + rootProps.showCellVerticalBorder, + gridHasFiller, + ); + columns.push( { hasFocus={hasFocus} tabIndex={tabIndex} pinnedPosition={pinnedPosition} - style={style} - indexInSection={i} - sectionLength={renderedColumns.length} + pinnedOffset={pinnedOffset} gridHasFiller={gridHasFiller} isLastUnpinned={isLastUnpinned} isSiblingFocused={isSiblingFocused} + showLeftBorder={showLeftBorder} + showRightBorder={showRightBorder} {...other} />, ); @@ -337,22 +302,19 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { {leftRenderContext && getColumnHeaders( { - position: GridPinnedColumnPosition.LEFT, + position: PinnedColumnPosition.LEFT, renderContext: leftRenderContext, - maxLastColumn: leftRenderContext.lastColumnIndex, }, { disableReorder: true }, )} {getColumnHeaders({ renderContext, - maxLastColumn: visibleColumns.length - pinnedColumns.right.length, })} {rightRenderContext && getColumnHeaders( { - position: GridPinnedColumnPosition.RIGHT, + position: PinnedColumnPosition.RIGHT, renderContext: rightRenderContext, - maxLastColumn: rightRenderContext.lastColumnIndex, }, { disableReorder: true, @@ -441,16 +403,20 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { }; const pinnedPosition = params.position; - const style = getCellOffsetStyle({ + const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; + const pinnedOffset = getPinnedCellOffset( pinnedPosition, + headerInfo.width, columnIndex, - computedWidth: headerInfo.width, - }); + columnPositions, + dimensions.columnsTotalWidth, + scrollbarWidth, + ); columnIndex += columnFields.length; let indexInSection = index; - if (pinnedPosition === 'left') { + if (pinnedPosition === PinnedColumnPosition.LEFT) { // Group headers can expand to multiple columns, we need to adjust the index indexInSection = columnIndex - 1; } @@ -469,10 +435,15 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { hasFocus={hasFocus} tabIndex={tabIndex} pinnedPosition={pinnedPosition} - style={style} - indexInSection={indexInSection} - sectionLength={visibleColumnGroupHeader.length} - gridHasFiller={gridHasFiller} + pinnedOffset={pinnedOffset} + showLeftBorder={shouldCellShowLeftBorder(pinnedPosition, indexInSection)} + showRightBorder={shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + visibleColumnGroupHeader.length, + rootProps.showCellVerticalBorder, + gridHasFiller, + )} /> ); }); @@ -499,7 +470,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { getColumnGroupHeaders({ depth, params: { - position: GridPinnedColumnPosition.LEFT, + position: PinnedColumnPosition.LEFT, renderContext: leftRenderContext, maxLastColumn: leftRenderContext.lastColumnIndex, }, @@ -509,7 +480,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { getColumnGroupHeaders({ depth, params: { - position: GridPinnedColumnPosition.RIGHT, + position: PinnedColumnPosition.RIGHT, renderContext: rightRenderContext, maxLastColumn: rightRenderContext.lastColumnIndex, }, @@ -527,11 +498,12 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { rightRenderContext, pinnedColumns, visibleColumns, - getCellOffsetStyle, + columnPositions, getFillers, getColumnHeadersRow, getColumnsToRender, getColumnGroupHeadersRows, + getPinnedCellOffset, isDragging: !!dragCol, getInnerProps: () => ({ role: 'rowgroup', 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 f9733a04f577c..218bb839348ca 100644 --- a/packages/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts +++ b/packages/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts @@ -70,8 +70,15 @@ export const gridFilteredDescendantCountLookupSelector = createSelector( export const gridExpandedSortedRowEntriesSelector = createSelectorMemoized( gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, - (visibleRowsLookup, sortedRows) => - sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), + gridRowMaximumTreeDepthSelector, + gridFilterModelSelector, + gridQuickFilterValuesSelector, + (visibleRowsLookup, sortedRows, maxDepth, filterModel, quickFilterValues) => { + if (maxDepth < 2 && !filterModel.items.length && !quickFilterValues?.length) { + return sortedRows; + } + return sortedRows.filter((row) => visibleRowsLookup[row.id] !== false); + }, ); /** diff --git a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts index a47c14ea44544..55f12b0965738 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts @@ -1,9 +1,12 @@ +import { GridFeatureMode } from '../../../models/gridFeatureMode'; import { GridPaginationMeta, GridPaginationModel } from '../../../models/gridPaginationProps'; export interface GridPaginationState { paginationModel: GridPaginationModel; rowCount: number; meta: GridPaginationMeta; + enabled: boolean; + paginationMode: GridFeatureMode; } export interface GridPaginationInitialState { diff --git a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts index f9c8698e59299..c1ef44002ec7b 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts @@ -7,6 +7,7 @@ import { } from '../filter/gridFilterSelector'; import { gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from '../rows/gridRowsSelector'; import { getPageCount } from './gridPaginationUtils'; +import { GridValidRowModel } from '../../../models/gridRows'; const ALL_RESULTS_PAGE_VALUE = -1; @@ -16,6 +17,15 @@ const ALL_RESULTS_PAGE_VALUE = -1; */ export const gridPaginationSelector = (state: GridStateCommunity) => state.pagination; +/** + * @category Pagination + * @ignore - do not document. + */ +export const gridPaginationEnabledClientSideSelector = createSelector( + gridPaginationSelector, + (pagination) => pagination.enabled && pagination.paginationMode === 'client', +); + /** * Get the pagination model * @category Pagination @@ -77,18 +87,24 @@ export const gridPageCountSelector = createSelector( * @category Pagination */ export const gridPaginationRowRangeSelector = createSelectorMemoized( + gridPaginationEnabledClientSideSelector, gridPaginationModelSelector, gridRowTreeSelector, gridRowMaximumTreeDepthSelector, gridExpandedSortedRowEntriesSelector, gridFilteredSortedTopLevelRowEntriesSelector, ( + clientSidePaginationEnabled, paginationModel, rowTree, rowTreeDepth, visibleSortedRowEntries, visibleSortedTopLevelRowEntries, ) => { + if (!clientSidePaginationEnabled) { + return null; + } + const visibleTopLevelRowCount = visibleSortedTopLevelRowEntries.length; const topLevelFirstRowIndex = Math.min( paginationModel.pageSize * paginationModel.page, @@ -181,3 +197,42 @@ export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelectorMemoiz ); }, ); + +/** + * Get the rows, range and rowIndex lookup map after filtering and sorting. + * Does not contain the collapsed children. + * @category Pagination + */ +export const gridVisibleRowsSelector = createSelectorMemoized( + gridPaginationEnabledClientSideSelector, + gridPaginationRowRangeSelector, + gridPaginatedVisibleSortedGridRowEntriesSelector, + gridExpandedSortedRowEntriesSelector, + (clientPaginationEnabled, paginationRowRange, paginationRows, expandedSortedRowEntries) => { + if (clientPaginationEnabled) { + return { + rows: paginationRows, + range: paginationRowRange, + rowToIndexMap: paginationRows.reduce((lookup, row, index) => { + lookup.set(row.model, index); + return lookup; + }, new Map()), + }; + } + + return { + rows: expandedSortedRowEntries, + range: + expandedSortedRowEntries.length === 0 + ? null + : { + firstRowIndex: 0, + lastRowIndex: expandedSortedRowEntries.length - 1, + }, + rowToIndexMap: expandedSortedRowEntries.reduce((lookup, row, index) => { + lookup.set(row.model, index); + return lookup; + }, new Map()), + }; + }, +); 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 d8aa0b927dd28..c83be0ad8903c 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts @@ -20,6 +20,8 @@ export const paginationStateInitializer: GridStateInitializer< | 'autoPageSize' | 'signature' | 'paginationMeta' + | 'pagination' + | 'paginationMode' > > = (state, props) => { const paginationModel = { @@ -34,9 +36,12 @@ export const paginationStateInitializer: GridStateInitializer< return { ...state, pagination: { + ...state.pagination, paginationModel, rowCount, meta, + enabled: props.pagination === true, + paginationMode: props.paginationMode, }, }; }; diff --git a/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts b/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts index 11d42b99e79a8..8cbce80289352 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts @@ -63,6 +63,7 @@ export const useGridPaginationModel = ( | 'autoPageSize' | 'initialState' | 'paginationMode' + | 'pagination' | 'signature' | 'rowHeight' >, @@ -256,7 +257,15 @@ export const useGridPaginationModel = ( /** * EFFECTS */ + const isFirstRender = React.useRef(true); React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + if (!props.pagination) { + return; + } apiRef.current.setState((state) => ({ ...state, pagination: { @@ -268,7 +277,28 @@ export const useGridPaginationModel = ( ), }, })); - }, [apiRef, props.paginationModel, props.paginationMode, props.signature]); + }, [apiRef, props.paginationModel, props.signature, props.pagination]); + + React.useEffect(() => { + apiRef.current.setState((state) => { + const isEnabled = props.pagination === true; + if ( + state.pagination.paginationMode === props.paginationMode || + state.pagination.enabled === isEnabled + ) { + return state; + } + + return { + ...state, + pagination: { + ...state.pagination, + paginationMode: props.paginationMode, + enabled: props.pagination === true, + }, + }; + }); + }, [apiRef, props.paginationMode, props.pagination]); React.useEffect(handleUpdateAutoPageSize, [handleUpdateAutoPageSize]); }; diff --git a/packages/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts b/packages/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts index fbf1f413e50b3..2cfb32ed68156 100644 --- a/packages/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts +++ b/packages/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts @@ -51,10 +51,10 @@ export const gridRowMaximumTreeDepthSelector = createSelectorMemoized( } return ( - entries + (entries .filter(([, nodeCount]) => nodeCount > 0) .map(([depth]) => Number(depth)) - .sort((a, b) => b - a)[0] + 1 + .sort((a, b) => b - a)[0] ?? 0) + 1 ); }, ); 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 2c700e864e872..e5577166b27bc 100644 --- a/packages/x-data-grid/src/hooks/features/rows/useGridRows.ts +++ b/packages/x-data-grid/src/hooks/features/rows/useGridRows.ts @@ -20,7 +20,7 @@ import { import { useTimeout } from '../../utils/useTimeout'; import { GridSignature, useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridStateInitializer } from '../../utils/useGridInitializeState'; -import { useGridVisibleRows } from '../../utils/useGridVisibleRows'; +import { getVisibleRows } from '../../utils/useGridVisibleRows'; import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; import { gridFilteredRowsLookupSelector } from '../filter/gridFilterSelector'; import { GridRowsInternalCache } from './gridRowsInterfaces'; @@ -87,7 +87,7 @@ export const useGridRows = ( } const logger = useGridLogger(apiRef, 'useGridRows'); - const currentPage = useGridVisibleRows(apiRef, props); + const currentPage = getVisibleRows(apiRef); const lastUpdateMs = React.useRef(Date.now()); const lastRowCount = React.useRef(props.rowCount); diff --git a/packages/x-data-grid/src/hooks/features/virtualization/gridFocusedVirtualCellSelector.ts b/packages/x-data-grid/src/hooks/features/virtualization/gridFocusedVirtualCellSelector.ts new file mode 100644 index 0000000000000..c1931ff5dc43d --- /dev/null +++ b/packages/x-data-grid/src/hooks/features/virtualization/gridFocusedVirtualCellSelector.ts @@ -0,0 +1,74 @@ +import { createSelector } from 'reselect'; +import { createSelectorMemoized } from '../../../utils/createSelector'; +import { gridVisibleColumnDefinitionsSelector } from '../columns/gridColumnsSelector'; +import { gridRenderContextSelector } from './gridVirtualizationSelectors'; +import { gridFocusCellSelector } from '../focus'; +import { gridVisibleRowsSelector } from '../pagination'; +import { gridRowsLookupSelector } from '../rows'; + +const gridIsFocusedCellOutOfContex = createSelector( + gridFocusCellSelector, + gridRenderContextSelector, + gridVisibleRowsSelector, + gridVisibleColumnDefinitionsSelector, + gridRowsLookupSelector, + (focusedCell, renderContext, currentPage, visibleColumns, rows) => { + if (!focusedCell) { + return false; + } + + const row = rows[focusedCell.id]; + if (!row) { + return false; + } + + const rowIndex = currentPage.rowToIndexMap.get(row); + const columnIndex = visibleColumns + .slice(renderContext.firstColumnIndex, renderContext.lastColumnIndex) + .findIndex((column) => column.field === focusedCell.field); + + const isInRenderContext = + rowIndex !== undefined && + columnIndex !== -1 && + rowIndex >= renderContext.firstRowIndex && + rowIndex <= renderContext.lastRowIndex; + + return !isInRenderContext; + }, +); + +export const gridFocusedVirtualCellSelector = createSelectorMemoized( + gridIsFocusedCellOutOfContex, + gridVisibleColumnDefinitionsSelector, + gridVisibleRowsSelector, + gridRowsLookupSelector, + gridFocusCellSelector, + (isFocusedCellOutOfRenderContext, visibleColumns, currentPage, rows, focusedCell) => { + if (!isFocusedCellOutOfRenderContext) { + return null; + } + + const row = rows[focusedCell!.id]; + if (!row) { + return null; + } + + const rowIndex = currentPage.rowToIndexMap.get(row); + + if (rowIndex === undefined) { + return null; + } + + const columnIndex = visibleColumns.findIndex((column) => column.field === focusedCell!.field); + + if (columnIndex === -1) { + return null; + } + + return { + ...focusedCell, + rowIndex, + columnIndex, + }; + }, +); 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 8e98187609e8d..9c872497705ce 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -22,22 +22,20 @@ import { import { gridDimensionsSelector } from '../dimensions/gridDimensionsSelectors'; import { gridPinnedRowsSelector } from '../rows/gridRowsSelector'; import { GridPinnedRowsPosition } from '../rows/gridRowsInterfaces'; -import { gridFocusCellSelector, gridTabIndexCellSelector } from '../focus/gridFocusStateSelector'; import { useGridVisibleRows, getVisibleRows } from '../../utils/useGridVisibleRows'; import { useGridApiEventHandler } from '../../utils'; import * as platform from '../../../utils/platform'; import { clamp, range } from '../../../utils/utils'; -import type { - GridRenderContext, - GridColumnsRenderContext, - GridRowEntry, - GridRowId, +import { + type GridRenderContext, + type GridColumnsRenderContext, + type GridRowEntry, + type GridRowId, } from '../../../models'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { selectedIdsLookupSelector } from '../rowSelection/gridRowSelectionSelector'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils'; -import { GridRowProps } from '../../../components/GridRow'; import { GridInfiniteLoaderPrivateApi } from '../../../models/api/gridInfiniteLoaderApi'; import { gridRenderContextSelector, @@ -48,6 +46,7 @@ import { EMPTY_RENDER_CONTEXT } from './useGridVirtualization'; import { gridRowSpanningHiddenCellsOriginMapSelector } from '../rows/gridRowSpanningSelectors'; import { gridListColumnSelector } from '../listView/gridListViewSelectors'; import { minimalContentHeight } from '../rows/gridRowsUtils'; +import { gridFocusedVirtualCellSelector } from './gridFocusedVirtualCellSelector'; const MINIMUM_COLUMN_WIDTH = 50; @@ -122,12 +121,9 @@ export const useGridVirtualScroller = () => { const [panels, setPanels] = React.useState(EMPTY_DETAIL_PANELS); const isRtl = useRtl(); - const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); - const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); const rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector); const selectedRowsLookup = useGridSelector(apiRef, selectedIdsLookupSelector); const currentPage = useGridVisibleRows(apiRef, rootProps); - const gridRootRef = apiRef.current.rootElementRef; const mainRef = apiRef.current.mainElementRef; const scrollerRef = apiRef.current.virtualScrollerRef; const scrollbarVerticalRef = apiRef.current.virtualScrollbarVerticalRef; @@ -135,6 +131,7 @@ export const useGridVirtualScroller = () => { const contentHeight = dimensions.contentSize.height; const columnsTotalWidth = dimensions.columnsTotalWidth; const hasColSpan = useGridSelector(apiRef, gridHasColSpanSelector); + const isRenderContextReady = React.useRef(false); const previousSize = React.useRef<{ width: number; height: number }>(null); @@ -210,6 +207,9 @@ export const useGridVirtualScroller = () => { const previousContextScrollPosition = React.useRef(EMPTY_SCROLL_POSITION); const previousRowContext = React.useRef(EMPTY_RENDER_CONTEXT); const renderContext = useGridSelector(apiRef, gridRenderContextSelector); + + const focusedVirtualCell = useGridSelector(apiRef, gridFocusedVirtualCellSelector); + const scrollTimeout = useTimeout(); const frozenContext = React.useRef(undefined); const scrollCache = useLazyRef(() => @@ -222,18 +222,6 @@ export const useGridVirtualScroller = () => { ), ).current; - const focusedCell = { - rowIndex: React.useMemo( - () => (cellFocus ? currentPage.rows.findIndex((row) => row.id === cellFocus.id) : -1), - [cellFocus, currentPage.rows], - ), - columnIndex: React.useMemo( - () => - cellFocus ? visibleColumns.findIndex((column) => column.field === cellFocus.field) : -1, - [cellFocus, visibleColumns], - ), - }; - const updateRenderContext = React.useCallback( (nextRenderContext: GridRenderContext) => { if ( @@ -431,14 +419,14 @@ export const useGridVirtualScroller = () => { : range(firstRowToRender, lastRowToRender); let virtualRowIndex = -1; - if (!isPinnedSection && focusedCell.rowIndex !== -1) { - if (focusedCell.rowIndex < firstRowToRender) { - virtualRowIndex = focusedCell.rowIndex; - rowIndexes.unshift(virtualRowIndex); + if (!isPinnedSection && focusedVirtualCell) { + if (focusedVirtualCell.rowIndex < firstRowToRender) { + rowIndexes.unshift(focusedVirtualCell.rowIndex); + virtualRowIndex = focusedVirtualCell.rowIndex; } - if (focusedCell.rowIndex >= lastRowToRender) { - virtualRowIndex = focusedCell.rowIndex; - rowIndexes.push(virtualRowIndex); + if (focusedVirtualCell.rowIndex > lastRowToRender) { + rowIndexes.push(focusedVirtualCell.rowIndex); + virtualRowIndex = focusedVirtualCell.rowIndex; } } @@ -481,8 +469,6 @@ export const useGridVirtualScroller = () => { } } - const hasFocus = cellFocus?.id === id; - const baseRowHeight = !apiRef.current.rowHasAutoHeight(id) ? apiRef.current.unstable_getRowHeight(id) : 'auto'; @@ -514,15 +500,6 @@ export const useGridVirtualScroller = () => { } } - const isVirtualRow = rowIndexInPage === virtualRowIndex; - const isNotVisible = isVirtualRow; - - let tabbableCell: GridRowProps['tabbableCell'] = null; - if (cellTabIndex !== null && cellTabIndex.id === id) { - const cellParams = apiRef.current.getCellParams(id, cellTabIndex.field); - tabbableCell = cellParams.cellMode === 'view' ? cellTabIndex.field : null; - } - let currentRenderContext = baseRenderContext; if ( !isPinnedSection && @@ -533,6 +510,9 @@ export const useGridVirtualScroller = () => { currentRenderContext = frozenContext.current; } + const isVirtualFocusRow = rowIndexInPage === virtualRowIndex; + const isVirtualFocusColumn = focusedVirtualCell?.rowIndex === rowIndex; + const offsetLeft = computeOffsetLeft( columnPositions, currentRenderContext, @@ -540,6 +520,9 @@ export const useGridVirtualScroller = () => { ); const showBottomBorder = isLastVisibleInSection && params.position === 'top'; + const firstColumnIndex = currentRenderContext.firstColumnIndex; + const lastColumnIndex = currentRenderContext.lastColumnIndex; + rows.push( { selected={isSelected} offsetTop={params.rows ? undefined : rowsMeta.positions[rowIndexInPage]} offsetLeft={offsetLeft} - dimensions={dimensions} + columnsTotalWidth={dimensions.columnsTotalWidth} rowHeight={baseRowHeight} - tabbableCell={tabbableCell} pinnedColumns={pinnedColumns} visibleColumns={visibleColumns} - renderContext={currentRenderContext} - focusedColumnIndex={hasFocus ? focusedCell.columnIndex : undefined} + firstColumnIndex={firstColumnIndex} + lastColumnIndex={lastColumnIndex} + focusedColumnIndex={isVirtualFocusColumn ? focusedVirtualCell!.columnIndex : undefined} isFirstVisible={isFirstVisible} isLastVisible={isLastVisible} - isNotVisible={isNotVisible} + isNotVisible={isVirtualFocusRow} showBottomBorder={showBottomBorder} + scrollbarWidth={dimensions.hasScrollY ? dimensions.scrollbarSize : 0} + gridHasFiller={dimensions.columnsTotalWidth < dimensions.viewportOuterSize.width} {...rowProps} />, ); - if (isNotVisible) { + if (isVirtualFocusRow) { return; } @@ -609,14 +594,11 @@ export const useGridVirtualScroller = () => { }, [apiRef, contentSize]); useEnhancedEffect(() => { - // TODO a scroll reset should not be necessary - if (enabledForColumns) { - scrollerRef.current!.scrollLeft = 0; - } - if (enabledForRows) { - scrollerRef.current!.scrollTop = 0; + if (!isRenderContextReady.current) { + return; } - }, [enabledForColumns, enabledForRows, gridRootRef, scrollerRef]); + apiRef.current.updateRenderContext?.(); + }, [apiRef, enabledForColumns, enabledForRows]); useEnhancedEffect(() => { if (listView) { @@ -635,6 +617,8 @@ export const useGridVirtualScroller = () => { left: scrollPosition.current.left, renderContext: initialRenderContext, }); + + isRenderContextReady.current = true; }); apiRef.current.register('private', { diff --git a/packages/x-data-grid/src/hooks/utils/useGridVisibleRows.ts b/packages/x-data-grid/src/hooks/utils/useGridVisibleRows.ts index b291c89dbdc53..a03ac8a254478 100644 --- a/packages/x-data-grid/src/hooks/utils/useGridVisibleRows.ts +++ b/packages/x-data-grid/src/hooks/utils/useGridVisibleRows.ts @@ -1,32 +1,16 @@ import * as React from 'react'; import { DataGridProcessedProps } from '../../models/props/DataGridProps'; -import { - gridPaginationRowRangeSelector, - gridPaginatedVisibleSortedGridRowEntriesSelector, -} from '../features/pagination/gridPaginationSelector'; -import { gridExpandedSortedRowEntriesSelector } from '../features/filter/gridFilterSelector'; -import type { GridApiCommon, GridRowEntry } from '../../models'; +import { gridVisibleRowsSelector } from '../features/pagination/gridPaginationSelector'; +import type { GridApiCommon } from '../../models'; +import { useGridSelector } from '.'; export const getVisibleRows = ( apiRef: React.RefObject, - props: Pick, + // TODO: remove after getVisibleRows implementations have been updated + // eslint-disable-next-line @typescript-eslint/no-unused-vars + props?: Pick, ) => { - let rows: GridRowEntry[]; - let range: { firstRowIndex: number; lastRowIndex: number } | null; - - if (props.pagination && props.paginationMode === 'client') { - range = gridPaginationRowRangeSelector(apiRef); - rows = gridPaginatedVisibleSortedGridRowEntriesSelector(apiRef); - } else { - rows = gridExpandedSortedRowEntriesSelector(apiRef); - if (rows.length === 0) { - range = null; - } else { - range = { firstRowIndex: 0, lastRowIndex: rows.length - 1 }; - } - } - - return { rows, range }; + return gridVisibleRowsSelector(apiRef); }; /** @@ -36,17 +20,12 @@ export const getVisibleRows = ( * - If the row tree has several layers, it contains up to `state.pageSize` top level rows and all their descendants. * - If the row tree is flat, it only contains up to `state.pageSize` rows. */ + export const useGridVisibleRows = ( apiRef: React.RefObject, - props: Pick, + // TODO: remove after useGridVisibleRows implementations have been updated + // eslint-disable-next-line @typescript-eslint/no-unused-vars + props?: Pick, ) => { - const response = getVisibleRows(apiRef, props); - - return React.useMemo( - () => ({ - rows: response.rows, - range: response.range, - }), - [response.rows, response.range], - ); + return useGridSelector(apiRef, gridVisibleRowsSelector); }; diff --git a/packages/x-data-grid/src/internals/constants.ts b/packages/x-data-grid/src/internals/constants.ts index 2db12bb9c9d28..a7d3d672d9f3a 100644 --- a/packages/x-data-grid/src/internals/constants.ts +++ b/packages/x-data-grid/src/internals/constants.ts @@ -1,3 +1,9 @@ export const GRID_TREE_DATA_GROUPING_FIELD = '__tree_data_group__'; export const GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD = '__row_group_by_columns_group__'; export const GRID_DETAIL_PANEL_TOGGLE_FIELD = '__detail_panel_toggle__'; +export enum PinnedColumnPosition { + NONE, + LEFT, + RIGHT, + VIRTUAL, +} diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index ad68a871e4bca..e98a964848895 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -155,6 +155,7 @@ export type * from '../models/props/DataGridProps'; export type * from '../models/gridDataSource'; export { getColumnsToExport, defaultGetRowsToExport } from '../hooks/features/export/utils'; export * from '../utils/createControllablePromise'; +export * from '../utils/rtlFlipSide'; export { createSelector, createSelectorMemoized } from '../utils/createSelector'; export { gridRowGroupsToFetchSelector } from '../hooks/features/rows/gridRowsSelector'; export { diff --git a/packages/x-data-grid/src/internals/utils/attachPinnedStyle.ts b/packages/x-data-grid/src/internals/utils/attachPinnedStyle.ts new file mode 100644 index 0000000000000..4f95b040f927d --- /dev/null +++ b/packages/x-data-grid/src/internals/utils/attachPinnedStyle.ts @@ -0,0 +1,11 @@ +export const attachPinnedStyle = ( + style: React.CSSProperties, + position: 'left' | 'right' | undefined, + pinnedOffset?: number, +) => { + if (!position || pinnedOffset === undefined) { + return style; + } + style[position] = pinnedOffset; + return style; +}; diff --git a/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts b/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts index f58566ff035eb..36de771547a59 100644 --- a/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts +++ b/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts @@ -1,32 +1,25 @@ -import { - GridPinnedColumnPosition, - gridColumnPositionsSelector, -} from '../../hooks/features/columns'; -import type { GridDimensions } from '../../hooks/features/dimensions'; +import { PinnedColumnPosition } from '../constants'; +import { gridColumnPositionsSelector } from '../../hooks/features/columns'; export const getPinnedCellOffset = ( - pinnedPosition: GridPinnedColumnPosition | undefined, + pinnedPosition: PinnedColumnPosition | undefined, computedWidth: number, columnIndex: number, columnPositions: ReturnType, - dimensions: GridDimensions, + columnsTotalWidth: number, + scrollbarWidth: number, ) => { - const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; - - let pinnedOffset: number; + let pinnedOffset: number | undefined; switch (pinnedPosition) { - case GridPinnedColumnPosition.LEFT: + case PinnedColumnPosition.LEFT: pinnedOffset = columnPositions[columnIndex]; break; - case GridPinnedColumnPosition.RIGHT: + case PinnedColumnPosition.RIGHT: pinnedOffset = - dimensions.columnsTotalWidth - - columnPositions[columnIndex] - - computedWidth + - scrollbarWidth; + columnsTotalWidth - columnPositions[columnIndex] - computedWidth + scrollbarWidth; break; default: - pinnedOffset = 0; + pinnedOffset = undefined; break; } diff --git a/packages/x-data-grid/src/internals/utils/index.ts b/packages/x-data-grid/src/internals/utils/index.ts index e97431ffc3333..8c4ee60c2ff17 100644 --- a/packages/x-data-grid/src/internals/utils/index.ts +++ b/packages/x-data-grid/src/internals/utils/index.ts @@ -1,3 +1,4 @@ export * from './computeSlots'; export * from './propValidation'; export * from './gridRowGroupingUtils'; +export * from './attachPinnedStyle'; diff --git a/packages/x-data-grid/src/utils/cellBorderUtils.ts b/packages/x-data-grid/src/utils/cellBorderUtils.ts index 007e569bf9bd6..8145cfdf52a9f 100644 --- a/packages/x-data-grid/src/utils/cellBorderUtils.ts +++ b/packages/x-data-grid/src/utils/cellBorderUtils.ts @@ -1,7 +1,7 @@ -import { GridPinnedColumnPosition } from '../hooks/features/columns/gridColumnsInterfaces'; +import { PinnedColumnPosition } from '../internals/constants'; export const shouldCellShowRightBorder = ( - pinnedPosition: GridPinnedColumnPosition | undefined, + pinnedPosition: PinnedColumnPosition | undefined, indexInSection: number, sectionLength: number, showCellVerticalBorderRootProp: boolean, @@ -9,14 +9,14 @@ export const shouldCellShowRightBorder = ( ) => { const isSectionLastCell = indexInSection === sectionLength - 1; - if (pinnedPosition === GridPinnedColumnPosition.LEFT && isSectionLastCell) { + if (pinnedPosition === PinnedColumnPosition.LEFT && isSectionLastCell) { return true; } if (showCellVerticalBorderRootProp) { - if (pinnedPosition === GridPinnedColumnPosition.LEFT) { + if (pinnedPosition === PinnedColumnPosition.LEFT) { return true; } - if (pinnedPosition === GridPinnedColumnPosition.RIGHT) { + if (pinnedPosition === PinnedColumnPosition.RIGHT) { return !isSectionLastCell; } // pinnedPosition === undefined, middle section @@ -26,8 +26,8 @@ export const shouldCellShowRightBorder = ( }; export const shouldCellShowLeftBorder = ( - pinnedPosition: GridPinnedColumnPosition | undefined, + pinnedPosition: PinnedColumnPosition | undefined, indexInSection: number, ) => { - return pinnedPosition === GridPinnedColumnPosition.RIGHT && indexInSection === 0; + return pinnedPosition === PinnedColumnPosition.RIGHT && indexInSection === 0; }; diff --git a/packages/x-data-grid/src/utils/rtlFlipSide.ts b/packages/x-data-grid/src/utils/rtlFlipSide.ts new file mode 100644 index 0000000000000..e6e0652b6f865 --- /dev/null +++ b/packages/x-data-grid/src/utils/rtlFlipSide.ts @@ -0,0 +1,27 @@ +import { PinnedColumnPosition } from '../internals/constants'; + +export const rtlFlipSide = ( + position: PinnedColumnPosition | undefined, + isRtl: boolean, +): 'left' | 'right' | undefined => { + if (!position) { + return undefined; + } + if (!isRtl) { + if (position === PinnedColumnPosition.LEFT) { + return 'left'; + } + if (position === PinnedColumnPosition.RIGHT) { + return 'right'; + } + } else { + if (position === PinnedColumnPosition.LEFT) { + return 'right'; + } + if (position === PinnedColumnPosition.RIGHT) { + return 'left'; + } + } + + return undefined; +}; diff --git a/packages/x-license/src/Watermark/Watermark.tsx b/packages/x-license/src/Watermark/Watermark.tsx index ff1eafba92983..ef64124839dc1 100644 --- a/packages/x-license/src/Watermark/Watermark.tsx +++ b/packages/x-license/src/Watermark/Watermark.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { fastMemo } from '@mui/x-internals/fastMemo'; import { useLicenseVerifier } from '../useLicenseVerifier'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; import { MuiCommercialPackageName } from '../utils/commercialPackages'; @@ -28,7 +29,7 @@ interface WatermarkProps { releaseInfo: string; } -export function Watermark(props: WatermarkProps) { +function Watermark(props: WatermarkProps) { const { packageName, releaseInfo } = props; const licenseStatus = useLicenseVerifier(packageName, releaseInfo); @@ -55,3 +56,6 @@ export function Watermark(props: WatermarkProps) { ); } + +const MemoizedWatermark = fastMemo(Watermark); +export { MemoizedWatermark as Watermark }; diff --git a/packages/x-license/tsconfig.build.json b/packages/x-license/tsconfig.build.json index bc9de8db7f41f..a8a66fb27f257 100644 --- a/packages/x-license/tsconfig.build.json +++ b/packages/x-license/tsconfig.build.json @@ -10,6 +10,7 @@ "outDir": "build", "rootDir": "./src" }, + "references": [{ "path": "../x-internals/tsconfig.build.json" }], "include": ["src/**/*.ts*"], "exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"] } diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index 859f3c83d9be1..07dac64948c69 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -434,6 +434,7 @@ { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, { "name": "GridPagination", "kind": "Variable" }, { "name": "GridPaginationApi", "kind": "Interface" }, + { "name": "gridPaginationEnabledClientSideSelector", "kind": "Variable" }, { "name": "GridPaginationInitialState", "kind": "Interface" }, { "name": "GridPaginationMeta", "kind": "Interface" }, { "name": "gridPaginationMetaSelector", "kind": "Variable" }, @@ -641,6 +642,7 @@ { "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" }, { "name": "gridVisiblePinnedColumnDefinitionsSelector", "kind": "Variable" }, { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, + { "name": "gridVisibleRowsSelector", "kind": "Variable" }, { "name": "GridWorkspacesIcon", "kind": "Variable" }, { "name": "isAutogeneratedRow", "kind": "Variable" }, { "name": "isGroupingColumn", "kind": "ImportSpecifier" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index bc77ebd119c43..ec090e77d7cda 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -396,6 +396,7 @@ { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, { "name": "GridPagination", "kind": "Variable" }, { "name": "GridPaginationApi", "kind": "Interface" }, + { "name": "gridPaginationEnabledClientSideSelector", "kind": "Variable" }, { "name": "GridPaginationInitialState", "kind": "Interface" }, { "name": "GridPaginationMeta", "kind": "Interface" }, { "name": "gridPaginationMetaSelector", "kind": "Variable" }, @@ -591,6 +592,7 @@ { "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" }, { "name": "gridVisiblePinnedColumnDefinitionsSelector", "kind": "Variable" }, { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, + { "name": "gridVisibleRowsSelector", "kind": "Variable" }, { "name": "isAutogeneratedRow", "kind": "Variable" }, { "name": "isLeaf", "kind": "Function" }, { "name": "LicenseInfo", "kind": "Class" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 5949b351aa98a..d1a325690f77c 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -359,6 +359,7 @@ { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, { "name": "GridPagination", "kind": "Variable" }, { "name": "GridPaginationApi", "kind": "Interface" }, + { "name": "gridPaginationEnabledClientSideSelector", "kind": "Variable" }, { "name": "GridPaginationInitialState", "kind": "Interface" }, { "name": "GridPaginationMeta", "kind": "Interface" }, { "name": "gridPaginationMetaSelector", "kind": "Variable" }, @@ -541,6 +542,7 @@ { "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" }, { "name": "gridVisiblePinnedColumnDefinitionsSelector", "kind": "Variable" }, { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, + { "name": "gridVisibleRowsSelector", "kind": "Variable" }, { "name": "isAutogeneratedRow", "kind": "Variable" }, { "name": "isLeaf", "kind": "Function" }, { "name": "LoadingOverlayPropsOverrides", "kind": "Interface" }, diff --git a/scripts/x-license.exports.json b/scripts/x-license.exports.json index 142052e831d4d..d8d94c55e3ed1 100644 --- a/scripts/x-license.exports.json +++ b/scripts/x-license.exports.json @@ -20,5 +20,5 @@ { "name": "Unstable_LicenseInfoProviderProps", "kind": "Interface" }, { "name": "useLicenseVerifier", "kind": "Function" }, { "name": "verifyLicense", "kind": "Function" }, - { "name": "Watermark", "kind": "Function" } + { "name": "Watermark", "kind": "Variable" } ] From 82807f9feb6b5c49c5f5004d1cb59165a26068e9 Mon Sep 17 00:00:00 2001 From: lauri865 Date: Tue, 14 Jan 2025 22:19:39 +0100 Subject: [PATCH 2/6] fix --- .../src/hooks/features/columnHeaders/useGridColumnHeaders.tsx | 1 - 1 file changed, 1 deletion(-) 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 49e2157bccb32..30a0ab2e02c1f 100644 --- a/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx +++ b/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx @@ -278,7 +278,6 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { tabIndex={tabIndex} pinnedPosition={pinnedPosition} pinnedOffset={pinnedOffset} - gridHasFiller={gridHasFiller} isLastUnpinned={isLastUnpinned} isSiblingFocused={isSiblingFocused} showLeftBorder={showLeftBorder} From a00b9381cf833b9a5c99e8e0b25d7529e5c63c58 Mon Sep 17 00:00:00 2001 From: lauri865 Date: Tue, 14 Jan 2025 22:55:18 +0100 Subject: [PATCH 3/6] fix broken test --- .../x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx index dba5820e8cc1e..d3e86353b0ff9 100644 --- a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx @@ -71,6 +71,8 @@ describeSkipIf(isJSDOM)(' - Data source', () => { columns: mockServer.columns, initialState: { pagination: { paginationModel: { page: 0, pageSize: 10 } } }, disableVirtualization: true, + pagination: true, + pageSizeOptions: [10], }; return ( From 14f2d6a6a095b4716e6da8fc3a1e82f13e54625d Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Tue, 14 Jan 2025 17:53:05 -0500 Subject: [PATCH 4/6] refactor: move more logic in attachPinnedStyle() --- .../headerFiltering/GridHeaderFilterCell.tsx | 21 +++++++++---------- .../components/GridSkeletonLoadingOverlay.tsx | 3 ++- .../src/components/cell/GridCell.tsx | 16 +++++++------- .../columnHeaders/GridColumnGroupHeader.tsx | 10 ++++----- .../columnHeaders/GridColumnHeaderItem.tsx | 11 ++++------ .../src/internals/utils/attachPinnedStyle.ts | 17 +++++++++------ 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index 9e1867fcb84ee..476ba93d1d2ef 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -30,7 +30,6 @@ import { gridHeaderFilteringEditFieldSelector, gridHeaderFilteringMenuSelector, isNavigationKey, - rtlFlipSide, attachPinnedStyle, } from '@mui/x-data-grid/internals'; import { useRtl } from '@mui/system/RtlProvider'; @@ -306,19 +305,19 @@ const GridHeaderFilterCell = forwardRef(function GridCell(pro }; } - const cellStyle = { - '--width': `${width}px`, - ...styleProp, - } as React.CSSProperties; + const cellStyle = attachPinnedStyle( + { + '--width': `${width}px`, + ...styleProp, + } as React.CSSProperties, + isRtl, + pinnedPosition, + pinnedOffset, + ); const isLeftPinned = pinnedPosition === PinnedColumnPosition.LEFT; const isRightPinned = pinnedPosition === PinnedColumnPosition.RIGHT; - const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); - attachPinnedStyle(cellStyle, pinnedSide, pinnedOffset); - if (rowSpan > 1) { cellStyle.height = `calc(var(--height) * ${rowSpan})`; cellStyle.zIndex = 5; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx index 08e0ad02ff137..fb6b0beaa1fdc 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx @@ -178,12 +178,10 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { ? group.headerClassName(renderParams) : group.headerClassName; - const style = React.useMemo(() => { - const styleProp = { ...props.style }; - const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); - attachPinnedStyle(styleProp, pinnedSide, pinnedOffset); - return styleProp; - }, [pinnedPosition, pinnedOffset, props.style, isRtl]); + const style = React.useMemo( + () => attachPinnedStyle({ ...props.style }, isRtl, pinnedPosition, pinnedOffset), + [pinnedPosition, pinnedOffset, props.style, isRtl], + ); return ( { - const styleProp = { ...props.style }; - const pinnedSide = rtlFlipSide(pinnedPosition, isRtl); - attachPinnedStyle(styleProp, pinnedSide, pinnedOffset); - return styleProp; - }, [pinnedPosition, pinnedOffset, props.style, isRtl]); + const style = React.useMemo( + () => attachPinnedStyle({ ...props.style }, isRtl, pinnedPosition, pinnedOffset), + [pinnedPosition, pinnedOffset, props.style, isRtl], + ); return ( { - if (!position || pinnedOffset === undefined) { +) { + const side = rtlFlipSide(pinnedPosition, isRtl); + if (!side || pinnedOffset === undefined) { return style; } - style[position] = pinnedOffset; + style[side] = pinnedOffset; return style; -}; +} From d2208e0c94b9b068342dda5634b94735b04bced5 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Tue, 14 Jan 2025 18:01:10 -0500 Subject: [PATCH 5/6] lint --- packages/x-data-grid/src/components/cell/GridCell.tsx | 1 - .../src/components/columnHeaders/GridColumnGroupHeader.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index c553fc7de5266..ec9e97550c75b 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -10,7 +10,6 @@ import { import { fastMemo } from '@mui/x-internals/fastMemo'; import { useRtl } from '@mui/system/RtlProvider'; import { forwardRef } from '@mui/x-internals/forwardRef'; -import { rtlFlipSide } from '../../utils/rtlFlipSide'; import { doesSupportPreventScroll } from '../../utils/doesSupportPreventScroll'; import { getDataGridUtilityClass, gridClasses } from '../../constants/gridClasses'; import { diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx index fb6b0beaa1fdc..c0434e3b377e9 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { unstable_useId as useId, unstable_composeClasses as composeClasses } from '@mui/utils'; import { useRtl } from '@mui/system/RtlProvider'; -import { rtlFlipSide } from '../../utils/rtlFlipSide'; import { GridAlignment } from '../../models/colDef/gridColDef'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; From d257ff869b5297aea2ba5ff645a9478f6f12ce4b Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Tue, 14 Jan 2025 18:21:35 -0500 Subject: [PATCH 6/6] ci: run (empty commit)