fix(virtual-core): clamp tracked scrollOffset at 0 in end-anchor comp… · TanStack/virtual@1e3b908 · GitHub
Skip to content

Commit 1e3b908

Browse files
authored
fix(virtual-core): clamp tracked scrollOffset at 0 in end-anchor compensation (#1230)
1 parent e2cb096 commit 1e3b908

3 files changed

Lines changed: 125 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions

‎packages/virtual-core/src/index.ts‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,11 @@ export class Virtualizer<
651651
if (idx < count) {
652652
const anchorItem = newMeasurements[idx]
653653
if (anchorItem) {
654-
const newOffset = anchorItem.start + anchorOffset
654+
// Clamp to the reachable range's lower bound — anchorOffset may
655+
// have been derived from a transiently negative scrollOffset
656+
// (rubber-band), and a negative tracked offset never self-heals
657+
// when the element cannot scroll (#1229).
658+
const newOffset = Math.max(0, anchorItem.start + anchorOffset)
655659
if (newOffset !== this.scrollOffset) {
656660
anchorDelta = newOffset - this.scrollOffset
657661
this.scrollOffset = newOffset
@@ -703,6 +707,13 @@ export class Virtualizer<
703707
// `scrollAdjustments` to keep their sum invariant.
704708
if (this.scrollOffset !== null) {
705709
this.scrollOffset += this.scrollAdjustments
710+
// Clamp only the lower bound: a negative offset is unreachable, and
711+
// on an unscrollable element (content fits the viewport) no scroll
712+
// event ever fires to correct it, permanently skewing
713+
// getDistanceFromEnd() and wedging _flushIosDeferredIfReady (#1229).
714+
// Upper-bound overflow stays untouched — it is transiently
715+
// legitimate mid-prepend while the consumer's sizer catches up.
716+
if (this.scrollOffset < 0) this.scrollOffset = 0
706717
this.scrollAdjustments = 0
707718
}
708719
}

‎packages/virtual-core/tests/index.test.ts‎

Lines changed: 108 additions & 0 deletions

0 commit comments

Comments
 (0)