Skip to content

Commit 240c808

Browse files
authored
feat(trace): fix scrollbar offset bug rendering indicator in the wrong position (#67803)
Workaround the scrollbar width causing the indicator to be rendered in the wrong position. We now force the scrollbar whenever we know the content will scroll and disable it when it doesnt. This adds a bit of layout thrashing, though we optimize it to only be done when the bit is actually flipped as opposed to on each resize event
1 parent 513f83a commit 240c808

File tree

1 file changed

+45
-2
lines changed

1 file changed

+45
-2
lines changed

static/app/views/performance/newTraceDetails/virtualizedViewManager.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,15 @@ export class VirtualizedViewManager {
378378
this.previousDividerClientVec = [event.clientX, event.clientY];
379379
}
380380

381+
private scrollbar_width: number = 0;
382+
onScrollbarWidthChange(width: number) {
383+
if (width === this.scrollbar_width) {
384+
return;
385+
}
386+
this.scrollbar_width = width;
387+
this.draw();
388+
}
389+
381390
registerList(list: VirtualizedList | null) {
382391
this.list = list;
383392
}
@@ -1226,7 +1235,9 @@ export class VirtualizedViewManager {
12261235

12271236
if (this.divider) {
12281237
this.divider.style.transform = `translateX(${
1229-
list_width * this.container_physical_space.width - DIVIDER_WIDTH / 2 - 1
1238+
list_width * (this.container_physical_space.width - this.scrollbar_width) -
1239+
DIVIDER_WIDTH / 2 -
1240+
1
12301241
}px)`;
12311242
}
12321243
if (this.indicator_container) {
@@ -1614,12 +1625,30 @@ export class VirtualizedList {
16141625
}
16151626
}
16161627

1628+
function maybeToggleScrollbar(
1629+
container: HTMLElement,
1630+
containerHeight: number,
1631+
scrollHeight: number,
1632+
manager: VirtualizedViewManager
1633+
) {
1634+
if (scrollHeight > containerHeight) {
1635+
container.style.overflowY = 'scroll';
1636+
container.style.scrollbarGutter = 'stable';
1637+
manager.onScrollbarWidthChange(container.offsetWidth - container.clientWidth);
1638+
} else {
1639+
container.style.overflowY = 'auto';
1640+
container.style.scrollbarGutter = 'auto';
1641+
manager.onScrollbarWidthChange(0);
1642+
}
1643+
}
1644+
16171645
interface UseVirtualizedListProps {
16181646
container: HTMLElement | null;
16191647
items: ReadonlyArray<TraceTreeNode<TraceTree.NodeValue>>;
16201648
manager: VirtualizedViewManager;
16211649
render: (item: VirtualizedRow) => React.ReactNode;
16221650
}
1651+
16231652
interface UseVirtualizedListResult {
16241653
list: VirtualizedList;
16251654
rendered: React.ReactNode[];
@@ -1697,6 +1726,13 @@ export const useVirtualizedList = (
16971726
list.current.scrollHeight = scrollHeightRef.current;
16981727
}
16991728

1729+
maybeToggleScrollbar(
1730+
elements[0].target as HTMLElement,
1731+
scrollHeightRef.current,
1732+
itemsRef.current.length * 24,
1733+
managerRef.current
1734+
);
1735+
17001736
const recomputedItems = findRenderedItems({
17011737
scrollTop: scrollTopRef.current,
17021738
items: itemsRef.current,
@@ -1738,6 +1774,13 @@ export const useVirtualizedList = (
17381774
scrollContainerRef.current!.style.willChange = 'transform';
17391775
scrollContainerRef.current!.style.height = `${props.items.length * 24}px`;
17401776

1777+
maybeToggleScrollbar(
1778+
props.container,
1779+
scrollHeightRef.current,
1780+
props.items.length * 24,
1781+
props.manager
1782+
);
1783+
17411784
const onScroll = event => {
17421785
if (!list.current) {
17431786
return;
@@ -1805,7 +1848,7 @@ export const useVirtualizedList = (
18051848
return () => {
18061849
props.container?.removeEventListener('scroll', onScroll);
18071850
};
1808-
}, [props.container, props.items, props.items.length]);
1851+
}, [props.container, props.items, props.items.length, props.manager]);
18091852

18101853
useLayoutEffect(() => {
18111854
if (!list.current || !styleCache.current || !renderCache.current) {

0 commit comments

Comments
 (0)