Skip to content

Commit

Permalink
[DataGrid] Fix scroll to cell logic for keyboard navigating cells and…
Browse files Browse the repository at this point in the history
… drag selection with pinned columns (mui#14550)

Signed-off-by: Kenan Yusuf <kenan.m.yusuf@gmail.com>
  • Loading branch information
KenanYusuf authored and Arthur Balduini committed Sep 30, 2024
1 parent eba7e2e commit c0cb5f8
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ export const useGridCellSelection = (
}

const { x: mouseX, y: mouseY } = mousePosition.current;
const { height, width } = dimensions.viewportInnerSize;
const { width, height: viewportOuterHeight } = dimensions.viewportOuterSize;
const height = viewportOuterHeight - totalHeaderHeight;

let deltaX = 0;
let deltaY = 0;
Expand Down Expand Up @@ -330,7 +331,7 @@ export const useGridCellSelection = (
}

autoScroll();
}, [apiRef, dimensions]);
}, [apiRef, dimensions, totalHeaderHeight]);

const handleCellMouseOver = React.useCallback<GridEventListener<'cellMouseOver'>>(
(params, event) => {
Expand All @@ -354,7 +355,8 @@ export const useGridCellSelection = (
}

const { x, y } = virtualScrollerRect;
const { height, width } = dimensions.viewportInnerSize;
const { width, height: viewportOuterHeight } = dimensions.viewportOuterSize;
const height = viewportOuterHeight - totalHeaderHeight;
const mouseX = event.clientX - x;
const mouseY = event.clientY - y - totalHeaderHeight;
mousePosition.current = { x: mouseX, y: mouseY };
Expand Down
67 changes: 40 additions & 27 deletions packages/x-data-grid/src/components/containers/GridRootStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,25 +157,47 @@ export const GridRootStyles = styled('div', {
const hoverColor = (t.vars || t).palette.action.hover;

const selectedOpacity = (t.vars || t).palette.action.selectedOpacity;
const selectedHoverOpacity = t.vars
? (`calc(${hoverOpacity} + ${selectedOpacity})` as unknown as number) // TODO: Improve type
: hoverOpacity + selectedOpacity;
const selectedBackground = t.vars
? `rgba(${t.vars.palette.primary.mainChannel} / ${selectedOpacity})`
: alpha(t.palette.primary.main, selectedOpacity);

const selectedHoverBackground = t.vars
? `rgba(${t.vars.palette.primary.mainChannel} / calc(
${t.vars.palette.action.selectedOpacity} +
${t.vars.palette.action.hoverOpacity}
))`
: alpha(
t.palette.primary.main,
t.palette.action.selectedOpacity + t.palette.action.hoverOpacity,
);
? `rgba(${t.vars.palette.primary.mainChannel} / ${selectedHoverOpacity})`
: alpha(t.palette.primary.main, selectedHoverOpacity);

const blendFn = t.vars ? blendCssVars : blend;

const pinnedHoverBackground = blendFn(pinnedBackground, hoverColor, hoverOpacity);
const pinnedSelectedBackground = blendFn(pinnedBackground, selectedBackground, selectedOpacity);
const pinnedSelectedHoverBackground = blendFn(pinnedSelectedBackground, hoverColor, hoverOpacity);
const getPinnedBackgroundStyles = (backgroundColor: string) => ({
[`& .${c['cell--pinnedLeft']}, & .${c['cell--pinnedRight']}`]: {
backgroundColor,
'&.Mui-selected': {
backgroundColor: blendFn(backgroundColor, selectedBackground, selectedOpacity),
'&:hover': {
backgroundColor: blendFn(backgroundColor, selectedBackground, selectedHoverOpacity),
},
},
},
});

const pinnedBackgroundColor = blendFn(pinnedBackground, hoverColor, hoverOpacity);
const pinnedHoverStyles = getPinnedBackgroundStyles(pinnedBackgroundColor);

const pinnedSelectedBackgroundColor = blendFn(
pinnedBackground,
selectedBackground,
selectedOpacity,
);
const pinnedSelectedStyles = getPinnedBackgroundStyles(pinnedSelectedBackgroundColor);

const pinnedSelectedHoverBackgroundColor = blendFn(
pinnedBackground,
selectedHoverBackground,
selectedHoverOpacity,
);
const pinnedSelectedHoverStyles = getPinnedBackgroundStyles(pinnedSelectedHoverBackgroundColor);

const selectedStyles = {
backgroundColor: selectedBackground,
Expand Down Expand Up @@ -632,23 +654,14 @@ export const GridRootStyles = styled('div', {
position: 'sticky',
zIndex: 3,
background: 'var(--DataGrid-pinnedBackground)',
'&.Mui-selected': {
backgroundColor: pinnedSelectedBackgroundColor,
},
},
[`& .${c.virtualScrollerContent} .${c.row}`]: {
'&:hover': {
[`& .${c['cell--pinnedLeft']}, & .${c['cell--pinnedRight']}`]: {
backgroundColor: pinnedHoverBackground,
},
},
[`&.Mui-selected`]: {
[`& .${c['cell--pinnedLeft']}, & .${c['cell--pinnedRight']}`]: {
backgroundColor: pinnedSelectedBackground,
},
'&:hover': {
[`& .${c['cell--pinnedLeft']}, & .${c['cell--pinnedRight']}`]: {
backgroundColor: pinnedSelectedHoverBackground,
},
},
},
'&:hover': pinnedHoverStyles,
'&.Mui-selected': pinnedSelectedStyles,
'&.Mui-selected:hover': pinnedSelectedHoverStyles,
},
[`& .${c.cellOffsetLeft}`]: {
flex: '0 0 auto',
Expand Down Expand Up @@ -778,6 +791,6 @@ function blend(background: string, overlay: string, opacity: number, gamma: numb
}

const removeOpacity = (color: string) => `rgb(from ${color} r g b / 1)`;
function blendCssVars(background: string, overlay: string, opacity: number) {
function blendCssVars(background: string, overlay: string, opacity: string | number) {
return `color-mix(in srgb,${background}, ${removeOpacity(overlay)} calc(${opacity} * 100%))`;
}
40 changes: 20 additions & 20 deletions packages/x-data-grid/src/hooks/features/scroll/useGridScroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,24 @@ import { gridDimensionsSelector } from '../dimensions';
// Logic copied from https://www.w3.org/TR/wai-aria-practices/examples/listbox/js/listbox.js
// Similar to https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
function scrollIntoView(dimensions: {
clientHeight: number;
scrollTop: number;
offsetHeight: number;
offsetTop: number;
containerSize: number;
scrollPosition: number;
elementSize: number;
elementOffset: number;
}) {
const { clientHeight, scrollTop, offsetHeight, offsetTop } = dimensions;
const { containerSize, scrollPosition, elementSize, elementOffset } = dimensions;

const elementBottom = offsetTop + offsetHeight;
const elementEnd = elementOffset + elementSize;
// Always scroll to top when cell is higher than viewport to avoid scroll jump
// See https://github.com/mui/mui-x/issues/4513 and https://github.com/mui/mui-x/issues/4514
if (offsetHeight > clientHeight) {
return offsetTop;
if (elementSize > containerSize) {
return elementOffset;
}
if (elementBottom - clientHeight > scrollTop) {
return elementBottom - clientHeight;
if (elementEnd - containerSize > scrollPosition) {
return elementEnd - containerSize;
}
if (offsetTop < scrollTop) {
return offsetTop;
if (elementOffset < scrollPosition) {
return elementOffset;
}
return undefined;
}
Expand Down Expand Up @@ -96,10 +96,10 @@ export const useGridScroll = (
}
// When using RTL, `scrollLeft` becomes negative, so we must ensure that we only compare values.
scrollCoordinates.left = scrollIntoView({
clientHeight: dimensions.viewportInnerSize.width,
scrollTop: Math.abs(virtualScrollerRef.current!.scrollLeft),
offsetHeight: cellWidth,
offsetTop: columnPositions[params.colIndex],
containerSize: dimensions.viewportOuterSize.width,
scrollPosition: Math.abs(virtualScrollerRef.current!.scrollLeft),
elementSize: cellWidth,
elementOffset: columnPositions[params.colIndex],
});
}
if (params.rowIndex !== undefined) {
Expand All @@ -116,10 +116,10 @@ export const useGridScroll = (
: rowsMeta.currentPageTotalHeight - rowsMeta.positions[elementIndex];

scrollCoordinates.top = scrollIntoView({
clientHeight: dimensions.viewportInnerSize.height,
scrollTop: virtualScrollerRef.current!.scrollTop,
offsetHeight: targetOffsetHeight,
offsetTop: rowsMeta.positions[elementIndex],
containerSize: dimensions.viewportInnerSize.height,
scrollPosition: virtualScrollerRef.current!.scrollTop,
elementSize: targetOffsetHeight,
elementOffset: rowsMeta.positions[elementIndex],
});
}

Expand Down

0 comments on commit c0cb5f8

Please sign in to comment.