Skip to content

Commit a6d6bfa

Browse files
committed
fix(repeat): keep scroll up/down states when handling scroll
1 parent 0b9e6f7 commit a6d6bfa

File tree

4 files changed

+100
-24
lines changed

4 files changed

+100
-24
lines changed

src/array-virtual-repeat-strategy.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,18 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
201201
// if all splices removal are followed by same amount of add,
202202
// optimise by just replacing affected visible views
203203
if (allSplicesAreInplace) {
204+
const lastIndex = repeat._lastViewIndex();
204205
const repeatViewSlot = repeat.viewSlot;
205206
for (i = 0; spliceCount > i; i++) {
206207
splice = splices[i];
207208
for (let collectionIndex = splice.index; collectionIndex < splice.index + splice.addedCount; collectionIndex++) {
208-
if (!this._isIndexBeforeViewSlot(repeat, repeatViewSlot, collectionIndex)
209-
&& !this._isIndexAfterViewSlot(repeat, repeatViewSlot, collectionIndex)
210-
) {
211-
let viewIndex = this._getViewIndex(repeat, repeatViewSlot, collectionIndex);
212-
let overrideContext = createFullOverrideContext(repeat, newArray[collectionIndex], collectionIndex, newArraySize);
209+
// if (!this._isIndexBeforeViewSlot(repeat, repeatViewSlot, collectionIndex)
210+
// && !this._isIndexAfterViewSlot(repeat, repeatViewSlot, collectionIndex)
211+
// ) {
212+
// const viewIndex = this._getViewIndex(repeat, repeatViewSlot, collectionIndex);
213+
if (collectionIndex >= firstIndex && collectionIndex <= lastIndex) {
214+
const viewIndex = collectionIndex - firstIndex;
215+
const overrideContext = createFullOverrideContext(repeat, newArray[collectionIndex], collectionIndex, newArraySize);
213216
repeat.removeView(viewIndex, /*return to cache?*/true, /*skip animation?*/true);
214217
repeat.insertView(viewIndex, overrideContext.bindingContext, overrideContext);
215218
}

src/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export interface ITemplateStrategy {
105105
* Get the first element(or view) between top buffer and bottom buffer
106106
* Note: [virtual-repeat] only supports single root node repeat
107107
*/
108-
getFirstElement(topBufer: Element, topBuffer: Element): Element;
108+
getFirstElement(topBufer: Element, botBuffer: Element): Element;
109109
/**
110110
* Get the last element(or view) between top buffer and bottom buffer
111111
* Note: [virtual-repeat] only supports single root node repeat

src/resize-observer.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { PLATFORM } from 'aurelia-pal';
2+
3+
/**
4+
* Copied from https://gist.github.com/strothj/708afcf4f01dd04de8f49c92e88093c3
5+
*/
6+
7+
export const getResizeObserverClass = (): ResizeObserverConstructor => PLATFORM.global.ResizeObserver;
8+
9+
export interface ResizeObserverConstructor {
10+
new (callback: ResizeObserverCallback): ResizeObserver;
11+
prototype: ResizeObserver;
12+
}
13+
14+
/**
15+
* The ResizeObserver interface is used to observe changes to Element's content
16+
* rect.
17+
*
18+
* It is modeled after MutationObserver and IntersectionObserver.
19+
*/
20+
export interface ResizeObserver {
21+
new (callback: ResizeObserverCallback);
22+
23+
/**
24+
* Adds target to the list of observed elements.
25+
*/
26+
observe: (target: Element) => void;
27+
28+
/**
29+
* Removes target from the list of observed elements.
30+
*/
31+
unobserve: (target: Element) => void;
32+
33+
/**
34+
* Clears both the observationTargets and activeTargets lists.
35+
*/
36+
disconnect: () => void;
37+
}
38+
39+
/**
40+
* This callback delivers ResizeObserver's notifications. It is invoked by a
41+
* broadcast active observations algorithm.
42+
*/
43+
export interface ResizeObserverCallback {
44+
(entries: ResizeObserverEntry[], observer: ResizeObserver): void;
45+
}
46+
47+
export interface ResizeObserverEntry {
48+
/**
49+
* @param target The Element whose size has changed.
50+
*/
51+
new (target: Element);
52+
53+
/**
54+
* The Element whose size has changed.
55+
*/
56+
readonly target: Element;
57+
58+
/**
59+
* Element's content rect when ResizeObserverCallback is invoked.
60+
*/
61+
readonly contentRect: DOMRectReadOnly;
62+
}
63+
64+
export declare class DOMRectReadOnly {
65+
static fromRect(other: DOMRectInit | undefined): DOMRectReadOnly;
66+
67+
readonly x: number;
68+
readonly y: number;
69+
readonly width: number;
70+
readonly height: number;
71+
readonly top: number;
72+
readonly right: number;
73+
readonly bottom: number;
74+
readonly left: number;
75+
76+
toJSON: () => any;
77+
}

src/virtual-repeat.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ export class VirtualRepeat extends AbstractRepeater {
214214
_prevItemsCount: number;
215215

216216
/**@internal */
217-
containerEl: HTMLElement;
217+
scrollerEl: HTMLElement;
218218

219219
/**@internal */
220220
private scrollListener: () => any;
@@ -339,7 +339,7 @@ export class VirtualRepeat extends AbstractRepeater {
339339
const scrollListener = this.scrollListener = () => {
340340
this._onScroll();
341341
};
342-
const containerEl = this.containerEl = templateStrategy.getScrollContainer(element);
342+
const containerEl = this.scrollerEl = templateStrategy.getScrollContainer(element);
343343
const [topBufferEl, bottomBufferEl] = templateStrategy.createBuffers(element);
344344

345345
this.topBufferEl = topBufferEl;
@@ -377,7 +377,7 @@ export class VirtualRepeat extends AbstractRepeater {
377377

378378
/**@override */
379379
detached(): void {
380-
const scrollCt = this.containerEl;
380+
const scrollCt = this.scrollerEl;
381381
const scrollListener = this.scrollListener;
382382
if (hasOverflowScroll(scrollCt)) {
383383
scrollCt.removeEventListener('scroll', scrollListener);
@@ -390,7 +390,7 @@ export class VirtualRepeat extends AbstractRepeater {
390390
this._unsubscribeCollection();
391391
this._resetCalculation();
392392
this.templateStrategy.removeBuffers(this.element, this.topBufferEl, this.bottomBufferEl);
393-
this.topBufferEl = this.bottomBufferEl = this.containerEl = this.scrollListener = null;
393+
this.topBufferEl = this.bottomBufferEl = this.scrollerEl = this.scrollListener = null;
394394
this.removeAllViews(/*return to cache?*/true, /*skip animation?*/false);
395395
const $clearInterval = PLATFORM.global.clearInterval;
396396
$clearInterval(this._calcDistanceToTopInterval);
@@ -439,7 +439,7 @@ export class VirtualRepeat extends AbstractRepeater {
439439
throw new Error('Value is not iterateable for virtual repeat.');
440440
}
441441

442-
const scroller = this.containerEl;
442+
const scroller = this.scrollerEl;
443443
if (shouldCalculateSize) {
444444
const currentItemCount = items.length;
445445
if (currentItemCount > 0 && this.viewCount() === 0) {
@@ -528,7 +528,7 @@ export class VirtualRepeat extends AbstractRepeater {
528528
*/
529529
getScroller(): HTMLElement {
530530
return this._fixedHeightContainer
531-
? this.containerEl
531+
? this.scrollerEl
532532
: document.documentElement;
533533
}
534534

@@ -541,7 +541,7 @@ export class VirtualRepeat extends AbstractRepeater {
541541
scroller: scroller,
542542
scrollHeight: scroller.scrollHeight,
543543
scrollTop: scroller.scrollTop,
544-
height: scroller.clientHeight
544+
height: calcScrollHeight(scroller)
545545
};
546546
}
547547

@@ -595,7 +595,7 @@ export class VirtualRepeat extends AbstractRepeater {
595595
return;
596596
}
597597
const topBufferEl = this.topBufferEl;
598-
const scrollerEl = this.containerEl;
598+
const scrollerEl = this.scrollerEl;
599599
const itemHeight = this.itemHeight;
600600
/**
601601
* Real scroll top calculated based on current scroll top of scroller and top buffer {height + distance to top}
@@ -661,9 +661,7 @@ export class VirtualRepeat extends AbstractRepeater {
661661
this._switchedDirection = false;
662662
this._topBufferHeight = currentTopBufferHeight + adjustHeight;
663663
this._bottomBufferHeight = Math$max(currentBottomBufferHeight - adjustHeight, 0);
664-
if (this._bottomBufferHeight >= 0) {
665-
this._updateBufferElements(true);
666-
}
664+
this._updateBufferElements(true);
667665
} else if (this._scrollingUp) {
668666
const isLastIndex = this.isLastIndex;
669667
let viewsToMoveCount = currLastReboundIndex - firstIndex;
@@ -689,9 +687,7 @@ export class VirtualRepeat extends AbstractRepeater {
689687
this._switchedDirection = false;
690688
this._topBufferHeight = Math$max(currentTopBufferHeight - adjustHeight, 0);
691689
this._bottomBufferHeight = currentBottomBufferHeight + adjustHeight;
692-
if (this._topBufferHeight >= 0) {
693-
this._updateBufferElements(true);
694-
}
690+
this._updateBufferElements(true);
695691
}
696692
this._previousFirst = firstIndex;
697693
this._isScrolling = false;
@@ -777,8 +773,8 @@ export class VirtualRepeat extends AbstractRepeater {
777773
const { _first, _scrollingUp, _scrollingDown, _previousFirst } = this;
778774

779775
let isScrolling = false;
780-
let isScrollingDown = false;
781-
let isScrollingUp = false;
776+
let isScrollingDown = _scrollingDown;
777+
let isScrollingUp = _scrollingUp;
782778
let isSwitchedDirection = false;
783779

784780
if (_first > _previousFirst
@@ -927,9 +923,9 @@ export class VirtualRepeat extends AbstractRepeater {
927923
this._prevItemsCount = itemsLength;
928924
const itemHeight = this.itemHeight;
929925
const scrollContainerHeight = this._fixedHeightContainer
930-
? calcScrollHeight(this.containerEl)
926+
? calcScrollHeight(this.scrollerEl)
931927
: document.documentElement.clientHeight;
932-
const elementsInView = this.elementsInView = Math$ceil(scrollContainerHeight / itemHeight) + 1;
928+
const elementsInView = this.elementsInView = Math$floor(scrollContainerHeight / itemHeight) + 1;
933929
const viewsCount = this._viewsLength = elementsInView * 2;
934930

935931
// Look at top buffer height (how far we've scrolled down)

0 commit comments

Comments
 (0)