Skip to content

Commit c4dc586

Browse files
committed
fix: virtualElement shouldn't adjust while scrolling. Earlier there was just a 5sec delay to adjust the virtualElement. Instead there's a state in viewPort telling if it's scrolling
1 parent 920a30e commit c4dc586

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

packages/webui/src/client/lib/VirtualElement.tsx

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'
22
import { InView } from 'react-intersection-observer'
3+
import { viewPortScrollingState } from './viewPort'
34

45
interface IElementMeasurements {
56
width: string | number
@@ -11,12 +12,12 @@ interface IElementMeasurements {
1112
id: string | undefined
1213
}
1314

14-
const OPTIMIZE_PERIOD = 5000
1515
const IDLE_CALLBACK_TIMEOUT = 100
1616

1717
/**
1818
* This is a component that allows optimizing the amount of elements present in the DOM through replacing them
1919
* with placeholders when they aren't visible in the viewport.
20+
* Scroll timing issues, should be handled in viewPort.tsx where the scrolling state is tracked.
2021
*
2122
* @export
2223
* @param {(React.PropsWithChildren<{
@@ -83,14 +84,40 @@ export function VirtualElement({
8384
setInView(visible)
8485
}, [])
8586

87+
const isScrolling = (): boolean => {
88+
// Don't do updates while scrolling:
89+
if (viewPortScrollingState.isProgrammaticScrollInProgress) {
90+
return true
91+
}
92+
// And wait if a programmatic scroll was done recently:
93+
const timeSinceLastProgrammaticScroll = Date.now() - viewPortScrollingState.lastProgrammaticScrollTime
94+
if (timeSinceLastProgrammaticScroll < 100) {
95+
return true
96+
}
97+
return false
98+
}
99+
86100
useEffect(() => {
87101
if (inView === true) {
88102
setIsShowingChildren(true)
89103
return
90104
}
91105

92106
let idleCallback: number | undefined
93-
const optimizeTimeout = window.setTimeout(() => {
107+
let optimizeTimeout: number | undefined
108+
109+
const scheduleOptimization = () => {
110+
if (optimizeTimeout) {
111+
window.clearTimeout(optimizeTimeout)
112+
}
113+
// Don't proceed if we're scrolling
114+
if (isScrolling()) {
115+
// Reschedule for after the scroll should be complete
116+
const scrollDelay = 200
117+
window.clearTimeout(optimizeTimeout)
118+
optimizeTimeout = window.setTimeout(scheduleOptimization, scrollDelay)
119+
return
120+
}
94121
idleCallback = window.requestIdleCallback(
95122
() => {
96123
if (childRef) {
@@ -102,14 +129,18 @@ export function VirtualElement({
102129
timeout: IDLE_CALLBACK_TIMEOUT,
103130
}
104131
)
105-
}, OPTIMIZE_PERIOD)
132+
}
133+
134+
// Schedule the optimization:
135+
scheduleOptimization()
106136

107137
return () => {
108138
if (idleCallback) {
109139
window.cancelIdleCallback(idleCallback)
110140
}
111-
112-
window.clearTimeout(optimizeTimeout)
141+
if (optimizeTimeout) {
142+
window.clearTimeout(optimizeTimeout)
143+
}
113144
}
114145
}, [childRef, inView])
115146

packages/webui/src/client/lib/viewPort.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,11 @@ function getRegionPosition(topElement: HTMLElement, bottomElement: HTMLElement):
267267
return { top, bottom }
268268
}
269269

270+
export const viewPortScrollingState = {
271+
isProgrammaticScrollInProgress: false,
272+
lastProgrammaticScrollTime: 0,
273+
}
274+
270275
export async function scrollToPosition(scrollPosition: number, noAnimation?: boolean): Promise<void> {
271276
// Calculate the exact position
272277
const headerOffset = getHeaderHeight() + HEADER_MARGIN
@@ -280,11 +285,16 @@ export async function scrollToPosition(scrollPosition: number, noAnimation?: boo
280285
})
281286
return Promise.resolve()
282287
} else {
288+
viewPortScrollingState.isProgrammaticScrollInProgress = true
289+
viewPortScrollingState.lastProgrammaticScrollTime = Date.now()
290+
283291
window.scroll({
284292
top: targetTop,
285293
left: 0,
286294
behavior: 'smooth',
287295
})
296+
await new Promise((resolve) => setTimeout(resolve, 500))
297+
viewPortScrollingState.isProgrammaticScrollInProgress = false
288298
}
289299
}
290300

0 commit comments

Comments
 (0)