Skip to content

Commit 4f6dc88

Browse files
lauri865KenanYusuf
andauthored
[data grid] Fix renderContext calculation with scroll bounce / over-scroll (#16297)
Signed-off-by: Lauri <[email protected]> Signed-off-by: Kenan Yusuf <[email protected]> Co-authored-by: Kenan Yusuf <[email protected]>
1 parent a40079a commit 4f6dc88

File tree

3 files changed

+40
-35
lines changed

3 files changed

+40
-35
lines changed

packages/x-data-grid/src/components/GridRow.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export interface GridRowProps extends React.HTMLAttributes<HTMLDivElement> {
5656
*/
5757
index: number;
5858
rowHeight: number | 'auto';
59-
offsetTop: number | undefined;
6059
offsetLeft: number;
6160
columnsTotalWidth: number;
6261
firstColumnIndex: number;
@@ -92,7 +91,6 @@ const GridRow = forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props,
9291
className,
9392
visibleColumns,
9493
pinnedColumns,
95-
offsetTop,
9694
offsetLeft,
9795
columnsTotalWidth,
9896
firstColumnIndex,
@@ -503,7 +501,6 @@ GridRow.propTypes = {
503501
isNotVisible: PropTypes.bool.isRequired,
504502
lastColumnIndex: PropTypes.number.isRequired,
505503
offsetLeft: PropTypes.number.isRequired,
506-
offsetTop: PropTypes.number,
507504
onClick: PropTypes.func,
508505
onDoubleClick: PropTypes.func,
509506
onMouseEnter: PropTypes.func,

packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ import { DataGridProcessedProps } from '../../models/props/DataGridProps';
1515

1616
type Position = 'vertical' | 'horizontal';
1717
type OwnerState = DataGridProcessedProps;
18-
type GridVirtualScrollbarProps = { position: Position };
18+
type GridVirtualScrollbarProps = {
19+
position: Position;
20+
scrollPosition: React.RefObject<{
21+
left: number;
22+
top: number;
23+
}>;
24+
};
1925

2026
const useUtilityClasses = (ownerState: OwnerState, position: Position) => {
2127
const { classes } = ownerState;
@@ -83,6 +89,7 @@ const GridVirtualScrollbar = forwardRef<HTMLDivElement, GridVirtualScrollbarProp
8389

8490
const propertyDimension = props.position === 'vertical' ? 'height' : 'width';
8591
const propertyScroll = props.position === 'vertical' ? 'scrollTop' : 'scrollLeft';
92+
const propertyScrollPosition = props.position === 'vertical' ? 'top' : 'left';
8693
const hasScroll = props.position === 'vertical' ? dimensions.hasScrollX : dimensions.hasScrollY;
8794

8895
const contentSize =
@@ -97,31 +104,33 @@ const GridVirtualScrollbar = forwardRef<HTMLDivElement, GridVirtualScrollbarProp
97104
scrollbarSize * (contentSize / dimensions.viewportOuterSize[propertyDimension]);
98105

99106
const onScrollerScroll = useEventCallback(() => {
100-
const scroller = apiRef.current.virtualScrollerRef.current!;
101107
const scrollbar = scrollbarRef.current;
108+
const scrollPosition = props.scrollPosition.current;
109+
102110
if (!scrollbar) {
103111
return;
104112
}
105113

106-
if (scroller[propertyScroll] === lastPosition.current) {
114+
if (scrollPosition[propertyScrollPosition] === lastPosition.current) {
107115
return;
108116
}
109117

110-
lastPosition.current = scroller[propertyScroll];
118+
lastPosition.current = scrollPosition[propertyScrollPosition];
111119

112120
if (isLocked.current) {
113121
isLocked.current = false;
114122
return;
115123
}
116124
isLocked.current = true;
117125

118-
const value = scroller[propertyScroll] / contentSize;
126+
const value = scrollPosition[propertyScrollPosition] / contentSize;
119127
scrollbar[propertyScroll] = value * scrollbarInnerSize;
120128
});
121129

122130
const onScrollbarScroll = useEventCallback(() => {
123131
const scroller = apiRef.current.virtualScrollerRef.current!;
124132
const scrollbar = scrollbarRef.current;
133+
125134
if (!scrollbar) {
126135
return;
127136
}
@@ -139,7 +148,7 @@ const GridVirtualScrollbar = forwardRef<HTMLDivElement, GridVirtualScrollbarProp
139148
useOnMount(() => {
140149
const scroller = apiRef.current.virtualScrollerRef.current!;
141150
const scrollbar = scrollbarRef.current!;
142-
const options = { capture: true, passive: true };
151+
const options: AddEventListenerOptions = { passive: true };
143152
scroller.addEventListener('scroll', onScrollerScroll, options);
144153
scrollbar.addEventListener('scroll', onScrollbarScroll, options);
145154
return () => {

packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ export const useGridVirtualScroller = () => {
115115
const [panels, setPanels] = React.useState(EMPTY_DETAIL_PANELS);
116116

117117
const isRtl = useRtl();
118-
const rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector);
119118
const selectedRowsLookup = useGridSelector(apiRef, selectedIdsLookupSelector);
120119
const currentPage = useGridVisibleRows(apiRef);
121120
const mainRef = apiRef.current.mainElementRef;
@@ -264,9 +263,19 @@ export const useGridVirtualScroller = () => {
264263
return undefined;
265264
}
266265

266+
const maxScrollTop = Math.ceil(
267+
dimensions.minimumSize.height - dimensions.viewportOuterSize.height,
268+
);
269+
const maxScrollLeft = Math.ceil(
270+
dimensions.minimumSize.width - dimensions.viewportInnerSize.width,
271+
);
272+
273+
// Clamp the scroll position to the viewport to avoid re-calculating the render context for scroll bounce
267274
const newScroll = {
268-
top: scroller.scrollTop,
269-
left: scroller.scrollLeft,
275+
top: clamp(scroller.scrollTop, 0, maxScrollTop),
276+
left: isRtl
277+
? clamp(scroller.scrollLeft, -maxScrollLeft, 0)
278+
: clamp(scroller.scrollLeft, 0, maxScrollLeft),
270279
};
271280

272281
const dx = newScroll.left - scrollPosition.current.left;
@@ -342,34 +351,17 @@ export const useGridVirtualScroller = () => {
342351
updateRenderContext(nextRenderContext);
343352
};
344353

345-
const handleScroll = useEventCallback((event: React.UIEvent) => {
354+
const handleScroll = useEventCallback(() => {
346355
if (ignoreNextScrollEvent.current) {
347356
ignoreNextScrollEvent.current = false;
348357
return;
349358
}
350359

351-
const { scrollTop, scrollLeft } = event.currentTarget;
352-
353-
// On iOS and macOS, negative offsets are possible when swiping past the start
354-
if (scrollTop < 0) {
355-
return;
356-
}
357-
if (!isRtl) {
358-
if (scrollLeft < 0) {
359-
return;
360-
}
361-
}
362-
if (isRtl) {
363-
if (scrollLeft > 0) {
364-
return;
365-
}
366-
}
367-
368360
const nextRenderContext = triggerUpdateRenderContext();
369361

370362
apiRef.current.publishEvent('scrollPositionChange', {
371-
top: scrollTop,
372-
left: scrollLeft,
363+
top: scrollPosition.current.top,
364+
left: scrollPosition.current.left,
373365
renderContext: nextRenderContext,
374366
});
375367
});
@@ -536,7 +528,6 @@ export const useGridVirtualScroller = () => {
536528
rowId={id}
537529
index={rowIndex}
538530
selected={isSelected}
539-
offsetTop={params.rows ? undefined : rowsMeta.positions[rowIndexInPage]}
540531
offsetLeft={offsetLeft}
541532
columnsTotalWidth={dimensions.columnsTotalWidth}
542533
rowHeight={baseRowHeight}
@@ -717,8 +708,16 @@ export const useGridVirtualScroller = () => {
717708
ref: onContentSizeApplied,
718709
}),
719710
getRenderZoneProps: () => ({ role: 'rowgroup' }),
720-
getScrollbarVerticalProps: () => ({ ref: scrollbarVerticalRef, role: 'presentation' }),
721-
getScrollbarHorizontalProps: () => ({ ref: scrollbarHorizontalRef, role: 'presentation' }),
711+
getScrollbarVerticalProps: () => ({
712+
ref: scrollbarVerticalRef,
713+
role: 'presentation',
714+
scrollPosition,
715+
}),
716+
getScrollbarHorizontalProps: () => ({
717+
ref: scrollbarHorizontalRef,
718+
role: 'presentation',
719+
scrollPosition,
720+
}),
722721
};
723722
};
724723

0 commit comments

Comments
 (0)