diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 68ba1ef303b5f..45fd4c8017089 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -617,10 +617,10 @@ "description": "Set the area in px at the bottom of the grid viewport where onRowsScrollEnd is called." }, "showCellVerticalBorder": { - "description": "If true, the vertical borders of the cells are displayed." + "description": "If true, vertical borders will be displayed between cells." }, "showColumnVerticalBorder": { - "description": "If true, the right border of the column headers are displayed." + "description": "If true, vertical borders will be displayed between column header items." }, "slotProps": { "description": "Overridable components props dynamically passed to the component at rendering." diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index b52063f75d28a..a6fb23cc8c755 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -559,10 +559,10 @@ "description": "Set the area in px at the bottom of the grid viewport where onRowsScrollEnd is called." }, "showCellVerticalBorder": { - "description": "If true, the vertical borders of the cells are displayed." + "description": "If true, vertical borders will be displayed between cells." }, "showColumnVerticalBorder": { - "description": "If true, the right border of the column headers are displayed." + "description": "If true, vertical borders will be displayed between column header items." }, "slotProps": { "description": "Overridable components props dynamically passed to the component at rendering." diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index 545e3c09ac3a1..ce9735263147b 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -454,10 +454,10 @@ "description": "Override the height/width of the Data Grid inner scrollbar." }, "showCellVerticalBorder": { - "description": "If true, the vertical borders of the cells are displayed." + "description": "If true, vertical borders will be displayed between cells." }, "showColumnVerticalBorder": { - "description": "If true, the right border of the column headers are displayed." + "description": "If true, vertical borders will be displayed between column header items." }, "slotProps": { "description": "Overridable components props dynamically passed to the component at rendering." diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index fb0abd4f63b0c..0de310d9ea53a 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -989,12 +989,12 @@ DataGridPremiumRaw.propTypes = { */ scrollEndThreshold: PropTypes.number, /** - * If `true`, the vertical borders of the cells are displayed. + * If `true`, vertical borders will be displayed between cells. * @default false */ showCellVerticalBorder: PropTypes.bool, /** - * If `true`, the right border of the column headers are displayed. + * If `true`, vertical borders will be displayed between column header items. * @default false */ showColumnVerticalBorder: PropTypes.bool, diff --git a/packages/x-data-grid-premium/src/components/GridAggregationHeader.tsx b/packages/x-data-grid-premium/src/components/GridAggregationHeader.tsx index 45048fa891478..0dd68ca313e74 100644 --- a/packages/x-data-grid-premium/src/components/GridAggregationHeader.tsx +++ b/packages/x-data-grid-premium/src/components/GridAggregationHeader.tsx @@ -41,12 +41,9 @@ const GridAggregationFunctionLabel = styled('div', { })<{ ownerState: OwnerState }>(({ theme }) => { return { fontSize: theme.typography.caption.fontSize, - lineHeight: theme.typography.caption.fontSize, - position: 'absolute', - bottom: 4, - fontWeight: theme.typography.fontWeightMedium, - color: (theme.vars || theme).palette.primary.dark, - textTransform: 'uppercase', + lineHeight: 'normal', + color: theme.palette.text.secondary, + marginTop: -1, }; }); diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index b168aad4b0b4a..4b0e431664212 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -895,12 +895,12 @@ DataGridProRaw.propTypes = { */ scrollEndThreshold: PropTypes.number, /** - * If `true`, the vertical borders of the cells are displayed. + * If `true`, vertical borders will be displayed between cells. * @default false */ showCellVerticalBorder: PropTypes.bool, /** - * If `true`, the right border of the column headers are displayed. + * If `true`, vertical borders will be displayed between column header items. * @default false */ showColumnVerticalBorder: PropTypes.bool, diff --git a/packages/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx index ec2f96b1ac44b..3d6e0567771d6 100644 --- a/packages/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx @@ -166,11 +166,11 @@ describe(' - Columns', () => { await microtasks(); expect(onColumnWidthChange.callCount).to.be.at.least(2); const widthArgs = onColumnWidthChange.args.map((arg) => arg[0].width); - const isWidth116Present = widthArgs.some((width) => width === 116); - expect(isWidth116Present).to.equal(true); + const isWidth114Present = widthArgs.some((width) => width === 114); + expect(isWidth114Present).to.equal(true); const colDefWidthArgs = onColumnWidthChange.args.map((arg) => arg[0].colDef.width); - const isColDefWidth116Present = colDefWidthArgs.some((width) => width === 116); - expect(isColDefWidth116Present).to.equal(true); + const isColDefWidth114Present = colDefWidthArgs.some((width) => width === 114); + expect(isColDefWidth114Present).to.equal(true); }); it('should not affect other cell elements that are not part of the main DataGrid instance', () => { @@ -473,7 +473,7 @@ describe(' - Columns', () => { render(); await apiRef.current.autosizeColumns(); await microtasks(); - expect(getWidths()).to.deep.equal([213, 235]); + expect(getWidths()).to.deep.equal([211, 233]); }); it('should work through double-clicking the separator', async () => { @@ -483,14 +483,14 @@ describe(' - Columns', () => { )[1]; fireEvent.doubleClick(separator); await microtasks(); - expect(getWidths()).to.deep.equal([100, 235]); + expect(getWidths()).to.deep.equal([100, 233]); }); it('should work on mount', async () => { render(); await microtasks(); /* first effect after render */ await microtasks(); /* async autosize operation */ - expect(getWidths()).to.deep.equal([213, 235]); + expect(getWidths()).to.deep.equal([211, 233]); }); describe('options', () => { @@ -506,7 +506,7 @@ describe(' - Columns', () => { }); it('.includeHeaders works', async () => { - await autosize({ includeHeaders: true }, [213, 235]); + await autosize({ includeHeaders: true }, [211, 233]); }); it('.includeOutliers works', async () => { diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index 72fcb94533209..b2a3dd45dcdcb 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -744,12 +744,12 @@ DataGridRaw.propTypes = { */ scrollbarSize: PropTypes.number, /** - * If `true`, the vertical borders of the cells are displayed. + * If `true`, vertical borders will be displayed between cells. * @default false */ showCellVerticalBorder: PropTypes.bool, /** - * If `true`, the right border of the column headers are displayed. + * If `true`, vertical borders will be displayed between column header items. * @default false */ showColumnVerticalBorder: PropTypes.bool, diff --git a/packages/x-data-grid/src/components/columnHeaders/ColumnHeaderMenuIcon.tsx b/packages/x-data-grid/src/components/columnHeaders/ColumnHeaderMenuIcon.tsx index 20d7527388aba..82f61061a22e8 100644 --- a/packages/x-data-grid/src/components/columnHeaders/ColumnHeaderMenuIcon.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/ColumnHeaderMenuIcon.tsx @@ -65,7 +65,7 @@ export const ColumnHeaderMenuIcon = React.memo((props: ColumnHeaderMenuIconProps id={columnMenuButtonId} {...rootProps.slotProps?.baseIconButton} > - + diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx index b734b750b04e3..d603c91772ad0 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx @@ -133,7 +133,7 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { pinnedPosition, indexInSection, sectionLength, - rootProps.showCellVerticalBorder, + rootProps.showColumnVerticalBorder, gridHasFiller, ); diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx index a0746768bdd65..ca3ed2a60b8ac 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx @@ -132,7 +132,7 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { pinnedPosition, indexInSection, sectionLength, - rootProps.showCellVerticalBorder, + rootProps.showColumnVerticalBorder, gridHasFiller, ); diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderSeparator.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderSeparator.tsx index 43957b0eaba75..6ae336ca166d9 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderSeparator.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderSeparator.tsx @@ -59,12 +59,7 @@ function GridColumnHeaderSeparatorRaw(props: GridColumnHeaderSeparatorProps) { return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions -
+
); diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx index cf70450d7643b..11f99b8ac0c34 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderTitle.tsx @@ -29,6 +29,7 @@ const GridColumnHeaderTitleRoot = styled('div', { overflow: 'hidden', whiteSpace: 'nowrap', fontWeight: 'var(--unstable_DataGrid-headWeight)', + lineHeight: 'normal', }); const ColumnHeaderInnerTitle = React.forwardRef< diff --git a/packages/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/x-data-grid/src/components/containers/GridRootStyles.ts index e35707002cc2b..2d5040f20f02e 100644 --- a/packages/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/x-data-grid/src/components/containers/GridRootStyles.ts @@ -27,13 +27,6 @@ function getBorderColor(theme: Theme) { return darken(alpha(theme.palette.divider, 1), 0.68); } -const columnHeadersStyles = { - [`.${c.columnSeparator}, .${c['columnSeparator--resizing']}`]: { - visibility: 'visible', - width: 'auto', - }, -}; - const columnHeaderStyles = { [`& .${c.iconButtonContainer}`]: { visibility: 'visible', @@ -45,6 +38,17 @@ const columnHeaderStyles = { }, }; +const columnSeparatorTargetSize = 10; +const columnSeparatorOffset = -5; + +const focusOutlineWidth = 1; + +const separatorIconDragStyles = { + width: 3, + rx: 1.5, + x: 10.5, +}; + // Emotion thinks it knows better than us which selector we should use. // https://github.com/emotion-js/emotion/issues/1105#issuecomment-1722524968 const ignoreSsrWarning = @@ -264,7 +268,6 @@ export const GridRootStyles = styled('div', { }, [`& .${c.columnHeader}, & .${c.cell}`]: { WebkitTapHighlightColor: 'transparent', - lineHeight: null, padding: '0 10px', boxSizing: 'border-box', }, @@ -273,19 +276,45 @@ export const GridRootStyles = styled('div', { t.vars ? `rgba(${t.vars.palette.primary.mainChannel} / 0.5)` : alpha(t.palette.primary.main, 0.5) - } 1px`, - outlineWidth: 1, - outlineOffset: -1, + } ${focusOutlineWidth}px`, + outlineOffset: focusOutlineWidth * -1, }, [`& .${c.columnHeader}:focus, & .${c.cell}:focus`]: { - outline: `solid ${t.palette.primary.main} 1px`, + outline: `solid ${t.palette.primary.main} ${focusOutlineWidth}px`, + outlineOffset: focusOutlineWidth * -1, + }, + // Hide the column separator when: + // - the column is focused and has an outline + // - the next column is focused and has an outline + // - the column has a left or right border + // - the next column is pinned right and has a left border + [`& .${c.columnHeader}:focus, + & .${c.columnHeader}:focus-within, + & .${c.columnHeader}:has(+ .${c.columnHeader}:focus), + & .${c.columnHeader}:has(+ .${c.columnHeader}:focus-within), + & .${c['columnHeader--withLeftBorder']}, + & .${c['columnHeader--withRightBorder']}, + & .${c.columnHeader}:has(+ .${c.filler} + .${c['columnHeader--withLeftBorder']}), + & .${c['virtualScroller--hasScrollX']} .${c['columnHeader--last']} + `]: { + [`& .${c.columnSeparator}`]: { + opacity: 0, + '@media (hover: none)': { + opacity: 1, + color: (t.vars || t).palette.primary.main, + }, + }, + // Show resizable separators again when the column is hovered + [`& .${c['columnSeparator--resizable']}:hover`]: { + opacity: 1, + }, }, [`&.${c['root--noToolbar']} [aria-rowindex="1"] [aria-colindex="1"]`]: { borderTopLeftRadius: 'calc(var(--unstable_DataGrid-radius) - 1px)', }, [`&.${c['root--noToolbar']} [aria-rowindex="1"] .${c['columnHeader--last']}`]: { borderTopRightRadius: - !dimensions.hasScrollY || dimensions.scrollbarSize === 0 + dimensions.hasScrollX && (!dimensions.hasScrollY || dimensions.scrollbarSize === 0) ? 'calc(var(--unstable_DataGrid-radius) - 1px)' : undefined, }, @@ -299,7 +328,7 @@ export const GridRootStyles = styled('div', { display: 'flex', alignItems: 'center', }, - [`& .${c['columnHeader--last']}`]: { + [`& .${c['virtualScroller--hasScrollX']} .${c['columnHeader--last']}`]: { overflow: 'hidden', }, [`& .${c['columnHeader--sorted']} .${c.iconButtonContainer}, & .${c['columnHeader--filtered']} .${c.iconButtonContainer}`]: @@ -316,12 +345,11 @@ export const GridRootStyles = styled('div', { [`& .${c.columnHeaderTitleContainer}`]: { display: 'flex', alignItems: 'center', + gap: t.spacing(0.25), minWidth: 0, flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', - // to anchor the aggregation label - position: 'relative', }, [`& .${c.columnHeaderTitleContainerContent}`]: { overflow: 'hidden', @@ -346,16 +374,13 @@ export const GridRootStyles = styled('div', { { flexDirection: 'row-reverse', }, - [`& .${c['columnHeader--alignCenter']} .${c.menuIcon}, & .${c['columnHeader--alignRight']} .${c.menuIcon}`]: - { - marginRight: 'auto', - marginLeft: -6, - }, - [`& .${c['columnHeader--alignRight']} .${c.menuIcon}, & .${c['columnHeader--alignRight']} .${c.menuIcon}`]: - { - marginRight: 'auto', - marginLeft: -10, - }, + [`& .${c['columnHeader--alignCenter']} .${c.menuIcon}`]: { + marginLeft: 'auto', + }, + [`& .${c['columnHeader--alignRight']} .${c.menuIcon}`]: { + marginRight: 'auto', + marginLeft: -5, + }, [`& .${c['columnHeader--moving']}`]: { backgroundColor: (t.vars || t).palette.action.hover, }, @@ -365,59 +390,70 @@ export const GridRootStyles = styled('div', { background: 'var(--DataGrid-pinnedBackground)', }, [`& .${c.columnSeparator}`]: { - visibility: 'hidden', position: 'absolute', + overflow: 'hidden', zIndex: 3, display: 'flex', flexDirection: 'column', justifyContent: 'center', + alignItems: 'center', + maxWidth: columnSeparatorTargetSize, color: borderColor, }, [`& .${c.columnHeaders}`]: { width: 'var(--DataGrid-rowWidth)', }, '@media (hover: hover)': { - [`& .${c.columnHeaders}:hover`]: columnHeadersStyles, [`& .${c.columnHeader}:hover`]: columnHeaderStyles, [`& .${c.columnHeader}:not(.${c['columnHeader--sorted']}):hover .${c.sortIcon}`]: { opacity: 0.5, }, }, '@media (hover: none)': { - [`& .${c.columnHeaders}`]: columnHeadersStyles, [`& .${c.columnHeader}`]: columnHeaderStyles, }, [`& .${c['columnSeparator--sideLeft']}`]: { - left: -12, + left: columnSeparatorOffset, }, [`& .${c['columnSeparator--sideRight']}`]: { - right: -12, + right: columnSeparatorOffset, + }, + [`& .${c['columnHeader--withRightBorder']} .${c['columnSeparator--sideLeft']}`]: { + left: columnSeparatorOffset - 0.5, + }, + [`& .${c['columnHeader--withRightBorder']} .${c['columnSeparator--sideRight']}`]: { + right: columnSeparatorOffset - 0.5, }, [`& .${c['columnSeparator--resizable']}`]: { cursor: 'col-resize', touchAction: 'none', - '&:hover': { - color: (t.vars || t).palette.text.primary, - // Reset on touch devices, it doesn't add specificity + // Always appear as draggable on touch devices + '@media (hover: none)': { + [`& .${c.iconSeparator} rect`]: separatorIconDragStyles, + color: borderColor, + }, + [`&:hover, &.${c['columnSeparator--resizing']}`]: { + color: (t.vars || t).palette.primary.main, + [`& .${c.iconSeparator} rect`]: separatorIconDragStyles, '@media (hover: none)': { color: borderColor, }, }, - [`&.${c['columnSeparator--resizing']}`]: { - color: (t.vars || t).palette.text.primary, - }, '& svg': { pointerEvents: 'none', }, }, [`& .${c.iconSeparator}`]: { color: 'inherit', + transition: t.transitions.create(['color', 'width'], { + duration: t.transitions.duration.shortest, + }), }, [`& .${c.menuIcon}`]: { width: 0, visibility: 'hidden', fontSize: 20, - marginRight: -10, + marginRight: -5, display: 'flex', alignItems: 'center', }, @@ -511,8 +547,8 @@ export const GridRootStyles = styled('div', { boxShadow: t.shadows[2], backgroundColor: (t.vars || t).palette.background.paper, '&:focus-within': { - outline: `solid ${(t.vars || t).palette.primary.main} 1px`, - outlineOffset: '-1px', + outline: `${focusOutlineWidth}px solid ${(t.vars || t).palette.primary.main}`, + outlineOffset: focusOutlineWidth * -1, }, }, [`& .${c['row--editing']}`]: { diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx index 875d1e2b80e52..923fe0502e201 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx @@ -29,14 +29,13 @@ const useUtilityClasses = ( loadingOverlayVariant: GridLoadingOverlayVariant | null, ) => { const { classes } = ownerState; - const slots = { root: [ 'main', dimensions.rightPinnedWidth > 0 && 'main--hasPinnedRight', loadingOverlayVariant === 'skeleton' && 'main--hasSkeletonLoadingOverlay', ], - scroller: ['virtualScroller'], + scroller: ['virtualScroller', dimensions.hasScrollX && 'virtualScroller--hasScrollX'], }; return composeClasses(slots, getDataGridUtilityClass, classes); diff --git a/packages/x-data-grid/src/constants/gridClasses.ts b/packages/x-data-grid/src/constants/gridClasses.ts index 3251ec30d194d..83dc3dfe4c1e1 100644 --- a/packages/x-data-grid/src/constants/gridClasses.ts +++ b/packages/x-data-grid/src/constants/gridClasses.ts @@ -408,6 +408,11 @@ export interface GridClasses { * Styles applied to the virtualization container. */ virtualScroller: string; + /** + * Styles applied to the virtualization container when it is scrollable in the horizontal direction. + * @ignore - do not document. + */ + 'virtualScroller--hasScrollX': string; /** * Styles applied to the virtualization content. */ @@ -770,6 +775,7 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'toolbarContainer', 'toolbarFilterList', 'virtualScroller', + 'virtualScroller--hasScrollX', 'virtualScrollerContent', 'virtualScrollerContent--overflowed', 'virtualScrollerRenderZone', diff --git a/packages/x-data-grid/src/material/icons/index.tsx b/packages/x-data-grid/src/material/icons/index.tsx index bd0e790fc7284..0f3c0946bf97d 100644 --- a/packages/x-data-grid/src/material/icons/index.tsx +++ b/packages/x-data-grid/src/material/icons/index.tsx @@ -50,7 +50,10 @@ export const GridColumnIcon = createSvgIcon( 'ColumnIcon', ); -export const GridSeparatorIcon = createSvgIcon(, 'Separator'); +export const GridSeparatorIcon = createSvgIcon( + , + 'Separator', +); export const GridViewHeadlineIcon = createSvgIcon( , diff --git a/packages/x-data-grid/src/models/props/DataGridProps.ts b/packages/x-data-grid/src/models/props/DataGridProps.ts index 97b44620eca30..e0381e1bf83e7 100644 --- a/packages/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/x-data-grid/src/models/props/DataGridProps.ts @@ -299,12 +299,12 @@ export interface DataGridPropsWithDefaultValues