From dce80cfcded86b6f4a6915c11d6da4fd89c13507 Mon Sep 17 00:00:00 2001 From: MGE Date: Sat, 8 Feb 2025 13:21:39 +0100 Subject: [PATCH] fix(template): keep scroll offset when using keepScrolledIndexOnPrepend This fix prevents items from jumping from the anchor items current offset to it's scrollTop when scrolling upwards. --- .../autosize-virtual-scroll-strategy.ts | 31 ++++++++++++++----- .../dynamic-size-virtual-scroll-strategy.ts | 10 ++++-- .../fixed-size-virtual-scroll-strategy.ts | 14 +++++++-- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts index 1a47e64d79..ef02e7486a 100644 --- a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts +++ b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts @@ -234,6 +234,7 @@ export class AutoSizeVirtualScrollStrategy< private scrollToTrigger$ = new Subject<{ scrollTop: number; behavior?: ScrollBehavior; + offset: number; }>(); /** @internal */ private _scrolledIndex = 0; @@ -252,6 +253,7 @@ export class AutoSizeVirtualScrollStrategy< * @internal * */ private _scrollToIndex: number | null = null; + private _scrollToOffset: number | null = null; /** @internal */ private containerSize = 0; @@ -355,7 +357,11 @@ export class AutoSizeVirtualScrollStrategy< this.detached$.next(); } - scrollToIndex(index: number, behavior?: ScrollBehavior): void { + scrollToIndex( + index: number, + behavior?: ScrollBehavior, + offset: number = 0, + ): void { const _index = Math.min( Math.max(index, 0), Math.max(0, this.contentLength - 1), @@ -363,17 +369,22 @@ export class AutoSizeVirtualScrollStrategy< if (_index !== this.scrolledIndex) { const scrollTop = this.calcInitialPosition(_index); this._scrollToIndex = _index; - this.scrollToTrigger$.next({ scrollTop, behavior }); + this._scrollToOffset = offset; + this.scrollToTrigger$.next({ scrollTop, behavior, offset }); } } - private scrollTo(scrollTo: number, behavior?: ScrollBehavior): void { + private scrollTo( + scrollTo: number, + behavior?: ScrollBehavior, + offset: number = 0, + ): void { this.waitForScroll = scrollTo !== this.scrollTop && this.contentSize > this.containerSize; if (this.waitForScroll) { this.isStable$.next(false); } - this.viewport!.scrollTo(this.viewportOffset + scrollTo, behavior); + this.viewport!.scrollTo(this.viewportOffset + scrollTo + offset, behavior); } /** @@ -486,7 +497,11 @@ export class AutoSizeVirtualScrollStrategy< keepScrolledIndexOnPrepend && this.anchorItem.index !== anchorItemIndex ) { - this.scrollToIndex(anchorItemIndex); + this.scrollToIndex( + anchorItemIndex, + undefined, + this.anchorItem.offset, + ); } else if (dataLength === 0) { this.anchorItem = { index: 0, @@ -708,7 +723,7 @@ export class AutoSizeVirtualScrollStrategy< virtualItem.position = position; } if (this._scrollToIndex === itemIndex) { - scrollToAnchorPosition = position; + scrollToAnchorPosition = position + this._scrollToOffset; } position += size; // immediately activate the ResizeObserver after initial positioning @@ -825,8 +840,8 @@ export class AutoSizeVirtualScrollStrategy< ), this.until$(), ) - .subscribe(({ scrollTop, behavior }) => { - this.scrollTo(scrollTop, behavior); + .subscribe(({ scrollTop, behavior, offset }) => { + this.scrollTo(scrollTop, behavior, offset); }); } /** @internal */ diff --git a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/dynamic-size-virtual-scroll-strategy.ts b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/dynamic-size-virtual-scroll-strategy.ts index 735ab22798..97eb06e43a 100644 --- a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/dynamic-size-virtual-scroll-strategy.ts +++ b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/dynamic-size-virtual-scroll-strategy.ts @@ -273,13 +273,17 @@ export class DynamicSizeVirtualScrollStrategy< this.detached$.next(); } - scrollToIndex(index: number, behavior?: ScrollBehavior): void { + scrollToIndex( + index: number, + behavior?: ScrollBehavior, + offset: number = 0, + ): void { const _index = Math.min(Math.max(index, 0), this.contentLength - 1); let scrollTo = 0; for (let i = 0; i < _index; i++) { scrollTo += this._virtualItems[i].size; } - this.scrollTo(scrollTo, behavior); + this.scrollTo(scrollTo + offset, behavior); } private scrollTo(scrollTo: number, behavior?: ScrollBehavior): void { @@ -394,7 +398,7 @@ export class DynamicSizeVirtualScrollStrategy< valueCache = {}; valueArray.forEach((v, i) => (valueCache[trackBy(i, v)] = v)); if (scrollTo !== this.scrolledIndex) { - this.scrollToIndex(scrollTo); + this.scrollToIndex(scrollTo, undefined, this.anchorItem.offset); } }); } diff --git a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/fixed-size-virtual-scroll-strategy.ts b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/fixed-size-virtual-scroll-strategy.ts index 11bb34c296..a050398d4d 100644 --- a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/fixed-size-virtual-scroll-strategy.ts +++ b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/fixed-size-virtual-scroll-strategy.ts @@ -265,7 +265,11 @@ export class FixedSizeVirtualScrollStrategy< valueCache = {}; valueArray.forEach((v, i) => (valueCache[trackBy(i, v)] = v)); if (scrollTo !== this.scrolledIndex) { - this.scrollToIndex(scrollTo); + this.scrollToIndex( + scrollTo, + undefined, + this.scrollTop % this._itemSize, + ); } }); const dataLengthChanged$ = valueArray$.pipe( @@ -359,9 +363,13 @@ export class FixedSizeVirtualScrollStrategy< .subscribe((range) => (this.renderedRange = range)); } - scrollToIndex(index: number, behavior?: ScrollBehavior): void { + scrollToIndex( + index: number, + behavior?: ScrollBehavior, + offset: number = 0, + ): void { const scrollTop = this.itemSize * index; - this.viewport!.scrollTo(this.viewportOffset + scrollTop, behavior); + this.viewport!.scrollTo(this.viewportOffset + scrollTop + offset, behavior); } private untilDetached$(): MonoTypeOperatorFunction {