Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGrid] Column management design updates #16630

Merged
merged 5 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/pages/x/api/data-grid/data-grid-premium.json
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,12 @@
"description": "Styles applied to the column header separator if the side is \"right\".",
"isGlobal": false
},
{
"key": "columnsManagementEmptyText",
"className": "MuiDataGridPremium-columnsManagementEmptyText",
"description": "Styles applied to the columns management empty text element.",
"isGlobal": false
},
{
"key": "columnsManagementFooter",
"className": "MuiDataGridPremium-columnsManagementFooter",
Expand All @@ -1560,6 +1566,12 @@
"description": "Styles applied to the columns management row element.",
"isGlobal": false
},
{
"key": "columnsManagementScrollArea",
"className": "MuiDataGridPremium-columnsManagementScrollArea",
"description": "Styles applied to the columns management scroll area element.",
"isGlobal": false
},
{
"key": "columnsManagementSearchInput",
"className": "MuiDataGridPremium-columnsManagementSearchInput",
Expand Down
12 changes: 12 additions & 0 deletions docs/pages/x/api/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,12 @@
"description": "Styles applied to the column header separator if the side is \"right\".",
"isGlobal": false
},
{
"key": "columnsManagementEmptyText",
"className": "MuiDataGridPro-columnsManagementEmptyText",
"description": "Styles applied to the columns management empty text element.",
"isGlobal": false
},
{
"key": "columnsManagementFooter",
"className": "MuiDataGridPro-columnsManagementFooter",
Expand All @@ -1465,6 +1471,12 @@
"description": "Styles applied to the columns management row element.",
"isGlobal": false
},
{
"key": "columnsManagementScrollArea",
"className": "MuiDataGridPro-columnsManagementScrollArea",
"description": "Styles applied to the columns management scroll area element.",
"isGlobal": false
},
{
"key": "columnsManagementSearchInput",
"className": "MuiDataGridPro-columnsManagementSearchInput",
Expand Down
12 changes: 12 additions & 0 deletions docs/pages/x/api/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,12 @@
"description": "Styles applied to the column header separator if the side is \"right\".",
"isGlobal": false
},
{
"key": "columnsManagementEmptyText",
"className": "MuiDataGrid-columnsManagementEmptyText",
"description": "Styles applied to the columns management empty text element.",
"isGlobal": false
},
{
"key": "columnsManagementFooter",
"className": "MuiDataGrid-columnsManagementFooter",
Expand All @@ -1336,6 +1342,12 @@
"description": "Styles applied to the columns management row element.",
"isGlobal": false
},
{
"key": "columnsManagementScrollArea",
"className": "MuiDataGrid-columnsManagementScrollArea",
"description": "Styles applied to the columns management scroll area element.",
"isGlobal": false
},
{
"key": "columnsManagementSearchInput",
"className": "MuiDataGrid-columnsManagementSearchInput",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,10 @@
"nodeName": "the column header separator",
"conditions": "the side is "right""
},
"columnsManagementEmptyText": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management empty text element"
},
"columnsManagementFooter": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management footer element"
Expand All @@ -953,6 +957,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management row element"
},
"columnsManagementScrollArea": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management scroll area element"
},
"columnsManagementSearchInput": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management search input element"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@
"nodeName": "the column header separator",
"conditions": "the side is "right""
},
"columnsManagementEmptyText": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management empty text element"
},
"columnsManagementFooter": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management footer element"
Expand All @@ -891,6 +895,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management row element"
},
"columnsManagementScrollArea": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management scroll area element"
},
"columnsManagementSearchInput": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management search input element"
Expand Down
8 changes: 8 additions & 0 deletions docs/translations/api-docs/data-grid/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,10 @@
"nodeName": "the column header separator",
"conditions": "the side is "right""
},
"columnsManagementEmptyText": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management empty text element"
},
"columnsManagementFooter": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management footer element"
Expand All @@ -765,6 +769,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management row element"
},
"columnsManagementScrollArea": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management scroll area element"
},
"columnsManagementSearchInput": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the columns management search input element"
Expand Down
61 changes: 61 additions & 0 deletions packages/x-data-grid/src/components/GridShadowScrollArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react';
import { styled, keyframes } from '@mui/system';

export interface GridShadowScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}

const reveal = keyframes({ from: { opacity: 0 }, to: { opacity: 1 } });
const detectScroll = keyframes({ 'from, to': { '--scrollable': '" "' } });

const ShadowScrollArea = styled('div', {
name: 'MuiDataGrid',
slot: 'ShadowScrollArea',
})({
flex: 1,
display: 'flex',
flexDirection: 'column',
animation: detectScroll,
animationTimeline: '--scroll-timeline',
animationFillMode: 'none',
boxSizing: 'border-box',
overflow: 'auto',
scrollTimeline: '--scroll-timeline block',
'&::before, &::after': {
content: '""',
flexShrink: 0,
display: 'block',
position: 'sticky',
left: 0,
width: '100%',
height: '4px',
animation: `${reveal} linear both`,
animationTimeline: '--scroll-timeline',
Copy link
Member Author

@KenanYusuf KenanYusuf Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uses animation-timeline to show scroll shadows.

The support for animation-timeline isn't the best, but these scroll shadows can be a progressive enhancement as they are only a minor UX improvement. Not worth implementing with JS.


// Custom property toggle trick:
// - Detects if the element is scrollable
// - https://css-tricks.com/the-css-custom-property-toggle-trick/
'--visibility-scrollable': 'var(--scrollable) visible',
'--visibility-not-scrollable': 'hidden',
visibility: 'var(--visibility-scrollable, var(--visibility-not-scrollable))',
},
'&::before': {
top: 0,
background: 'linear-gradient(to bottom, rgba(0,0,0,0.05) 0, transparent 100%)',
animationRange: '0 4px',
},
'&::after': {
bottom: 0,
background: 'linear-gradient(to top, rgba(0,0,0,0.05) 0, transparent 100%)',
animationDirection: 'reverse',
animationRange: 'calc(100% - 4px) 100%',
},
});

/**
* Adds scroll shadows above and below content in a scrollable container.
*/
export function GridShadowScrollArea(props: GridShadowScrollAreaProps) {
const { children, ...rest } = props;
return <ShadowScrollArea {...rest}>{children}</ShadowScrollArea>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type { GridSlotProps } from '../../models/gridSlotsComponentsProps';
import { getDataGridUtilityClass } from '../../constants/gridClasses';
import { checkColumnVisibilityModelsSame, defaultSearchPredicate } from './utils';
import { NotRendered } from '../../utils/assert';
import { GridShadowScrollArea } from '../GridShadowScrollArea';

export interface GridColumnsManagementProps {
/*
Expand Down Expand Up @@ -237,28 +238,31 @@ function GridColumnsManagement(props: GridColumnsManagementProps) {
input: {
startAdornment: (
<rootProps.slots.baseInputAdornment position="start">
<rootProps.slots.quickFilterIcon />
<rootProps.slots.quickFilterIcon fontSize="small" />
</rootProps.slots.baseInputAdornment>
),
endAdornment: (
<rootProps.slots.baseIconButton
aria-label={apiRef.current.getLocaleText('columnsManagementDeleteIconLabel')}
size="small"
style={
searchValue
? {
visibility: 'visible',
}
: {
visibility: 'hidden',
}
}
tabIndex={-1}
onClick={handleSearchReset}
{...rootProps.slotProps?.baseIconButton}
>
<rootProps.slots.quickFilterClearIcon fontSize="small" />
</rootProps.slots.baseIconButton>
<rootProps.slots.baseInputAdornment position="end">
<rootProps.slots.baseIconButton
size="small"
aria-label={apiRef.current.getLocaleText('columnsManagementDeleteIconLabel')}
style={
searchValue
? {
visibility: 'visible',
}
: {
visibility: 'hidden',
}
}
tabIndex={-1}
onClick={handleSearchReset}
edge="end"
{...rootProps.slotProps?.baseIconButton}
>
<rootProps.slots.quickFilterClearIcon fontSize="small" />
</rootProps.slots.baseIconButton>
</rootProps.slots.baseInputAdornment>
),
},
htmlInput: {
Expand All @@ -271,30 +275,31 @@ function GridColumnsManagement(props: GridColumnsManagementProps) {
{...searchInputProps}
/>
</GridColumnsManagementHeader>
<GridColumnsManagementBody className={classes.root} ownerState={rootProps}>
{currentColumns.map((column) => (
<rootProps.slots.baseCheckbox
key={column.field}
className={classes.row}
disabled={column.hideable === false}
checked={columnVisibilityModel[column.field] !== false}
onClick={toggleColumn}
name={column.field}
inputRef={isFirstHideableColumn(column) ? firstSwitchRef : undefined}
label={column.headerName || column.field}
size="medium"
density="compact"
fullWidth
{...rootProps.slotProps?.baseCheckbox}
/>
))}
{currentColumns.length === 0 && (
<GridColumnsManagementEmptyText ownerState={rootProps}>
{apiRef.current.getLocaleText('columnsManagementNoColumns')}
</GridColumnsManagementEmptyText>
)}
</GridColumnsManagementBody>
{(!disableShowHideToggle || !disableResetButton) && currentColumns.length > 0 ? (
<GridColumnsManagementScrollArea ownerState={rootProps}>
<GridColumnsManagementBody className={classes.root} ownerState={rootProps}>
{currentColumns.map((column) => (
<rootProps.slots.baseCheckbox
key={column.field}
className={classes.row}
disabled={column.hideable === false}
checked={columnVisibilityModel[column.field] !== false}
onClick={toggleColumn}
name={column.field}
inputRef={isFirstHideableColumn(column) ? firstSwitchRef : undefined}
label={column.headerName || column.field}
density="compact"
fullWidth
{...rootProps.slotProps?.baseCheckbox}
/>
))}
{currentColumns.length === 0 && (
<GridColumnsManagementEmptyText ownerState={rootProps}>
{apiRef.current.getLocaleText('columnsManagementNoColumns')}
</GridColumnsManagementEmptyText>
)}
</GridColumnsManagementBody>
</GridColumnsManagementScrollArea>
{!disableShowHideToggle || !disableResetButton ? (
<GridColumnsManagementFooter ownerState={rootProps} className={classes.footer}>
{!disableShowHideToggle ? (
<rootProps.slots.baseCheckbox
Expand All @@ -304,6 +309,7 @@ function GridColumnsManagement(props: GridColumnsManagementProps) {
onClick={() => toggleAllColumns(!allHideableColumnsVisible)}
name={apiRef.current.getLocaleText('columnsManagementShowHideAllText')}
label={apiRef.current.getLocaleText('columnsManagementShowHideAllText')}
density="compact"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to set it (and other styling like spacing etc) according to rootProps.density?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially, yes. I am thinking about how the other UI elements should respond to the density setting but it's going to require a bit of experimentation. Will create a follow up issue to review this across the board.

{...rootProps.slotProps?.baseCheckbox}
/>
) : (
Expand Down Expand Up @@ -422,29 +428,30 @@ const GridColumnsManagementBody = styled('div', {
name: 'MuiDataGrid',
slot: 'ColumnsManagement',
})<{ ownerState: OwnerState }>({
padding: vars.spacing(0, 2, 1.5),
display: 'flex',
flexDirection: 'column',
overflow: 'auto',
flex: '1 1',
padding: vars.spacing(0.5, 1.5),
});

const GridColumnsManagementScrollArea = styled(GridShadowScrollArea, {
name: 'MuiDataGrid',
slot: 'ColumnsManagementScrollArea',
})<{ ownerState: OwnerState }>({
maxHeight: 400,
alignItems: 'flex-start',
});

const GridColumnsManagementHeader = styled('div', {
name: 'MuiDataGrid',
slot: 'ColumnsManagementHeader',
})<{ ownerState: OwnerState }>({
padding: vars.spacing(1.5, 3),
padding: vars.spacing(1.5, 2),
borderBottom: `1px solid ${vars.colors.border.base}`,
});

const SearchInput = styled(NotRendered<GridSlotProps['baseTextField']>, {
name: 'MuiDataGrid',
slot: 'ColumnsManagementSearchInput',
})<{ ownerState: OwnerState }>({
[`& .${inputBaseClasses.root}`]: {
padding: vars.spacing(0, 1.5, 0, 1.5),
},
[`& .${inputBaseClasses.input}::-webkit-search-decoration,
& .${inputBaseClasses.input}::-webkit-search-cancel-button,
& .${inputBaseClasses.input}::-webkit-search-results-button,
Expand All @@ -458,15 +465,19 @@ const GridColumnsManagementFooter = styled('div', {
name: 'MuiDataGrid',
slot: 'ColumnsManagementFooter',
})<{ ownerState: OwnerState }>({
padding: vars.spacing(0.5, 1, 0.5, 3),
padding: vars.spacing(1, 1, 1, 1.5),
display: 'flex',
justifyContent: 'space-between',
borderTop: `1px solid ${vars.colors.border.base}`,
});

const GridColumnsManagementEmptyText = styled('div')<{ ownerState: OwnerState }>({
padding: vars.spacing(0.5, 0),
color: vars.colors.foreground.muted,
const GridColumnsManagementEmptyText = styled('div', {
name: 'MuiDataGrid',
slot: 'ColumnsManagementEmptyText',
})<{ ownerState: OwnerState }>({
padding: vars.spacing(1, 0),
alignSelf: 'center',
fontSize: vars.typography.body.fontSize,
});

export { GridColumnsManagement };
Loading