Skip to content
Permalink
Browse files

fix(repeat): keep scroll up/down states when handling scroll

  • Loading branch information...
bigopon committed Mar 16, 2019
1 parent 0b9e6f7 commit a6d6bfa6b443f64e34d32df260f3978f15699a41
Showing with 100 additions and 24 deletions.
  1. +8 −5 src/array-virtual-repeat-strategy.ts
  2. +1 −1 src/interfaces.ts
  3. +77 −0 src/resize-observer.ts
  4. +14 −18 src/virtual-repeat.ts
@@ -201,15 +201,18 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
// if all splices removal are followed by same amount of add,
// optimise by just replacing affected visible views
if (allSplicesAreInplace) {
const lastIndex = repeat._lastViewIndex();
const repeatViewSlot = repeat.viewSlot;
for (i = 0; spliceCount > i; i++) {
splice = splices[i];
for (let collectionIndex = splice.index; collectionIndex < splice.index + splice.addedCount; collectionIndex++) {
if (!this._isIndexBeforeViewSlot(repeat, repeatViewSlot, collectionIndex)
&& !this._isIndexAfterViewSlot(repeat, repeatViewSlot, collectionIndex)
) {
let viewIndex = this._getViewIndex(repeat, repeatViewSlot, collectionIndex);
let overrideContext = createFullOverrideContext(repeat, newArray[collectionIndex], collectionIndex, newArraySize);
// if (!this._isIndexBeforeViewSlot(repeat, repeatViewSlot, collectionIndex)
// && !this._isIndexAfterViewSlot(repeat, repeatViewSlot, collectionIndex)
// ) {
// const viewIndex = this._getViewIndex(repeat, repeatViewSlot, collectionIndex);
if (collectionIndex >= firstIndex && collectionIndex <= lastIndex) {
const viewIndex = collectionIndex - firstIndex;
const overrideContext = createFullOverrideContext(repeat, newArray[collectionIndex], collectionIndex, newArraySize);
repeat.removeView(viewIndex, /*return to cache?*/true, /*skip animation?*/true);
repeat.insertView(viewIndex, overrideContext.bindingContext, overrideContext);
}
@@ -105,7 +105,7 @@ export interface ITemplateStrategy {
* Get the first element(or view) between top buffer and bottom buffer
* Note: [virtual-repeat] only supports single root node repeat
*/
getFirstElement(topBufer: Element, topBuffer: Element): Element;
getFirstElement(topBufer: Element, botBuffer: Element): Element;
/**
* Get the last element(or view) between top buffer and bottom buffer
* Note: [virtual-repeat] only supports single root node repeat
@@ -0,0 +1,77 @@
import { PLATFORM } from 'aurelia-pal';

/**
* Copied from https://gist.github.com/strothj/708afcf4f01dd04de8f49c92e88093c3
*/

export const getResizeObserverClass = (): ResizeObserverConstructor => PLATFORM.global.ResizeObserver;

export interface ResizeObserverConstructor {
new (callback: ResizeObserverCallback): ResizeObserver;
prototype: ResizeObserver;
}

/**
* The ResizeObserver interface is used to observe changes to Element's content
* rect.
*
* It is modeled after MutationObserver and IntersectionObserver.
*/
export interface ResizeObserver {
new (callback: ResizeObserverCallback);

/**
* Adds target to the list of observed elements.
*/
observe: (target: Element) => void;

/**
* Removes target from the list of observed elements.
*/
unobserve: (target: Element) => void;

/**
* Clears both the observationTargets and activeTargets lists.
*/
disconnect: () => void;
}

/**
* This callback delivers ResizeObserver's notifications. It is invoked by a
* broadcast active observations algorithm.
*/
export interface ResizeObserverCallback {
(entries: ResizeObserverEntry[], observer: ResizeObserver): void;
}

export interface ResizeObserverEntry {
/**
* @param target The Element whose size has changed.
*/
new (target: Element);

/**
* The Element whose size has changed.
*/
readonly target: Element;

/**
* Element's content rect when ResizeObserverCallback is invoked.
*/
readonly contentRect: DOMRectReadOnly;
}

export declare class DOMRectReadOnly {
static fromRect(other: DOMRectInit | undefined): DOMRectReadOnly;

readonly x: number;
readonly y: number;
readonly width: number;
readonly height: number;
readonly top: number;
readonly right: number;
readonly bottom: number;
readonly left: number;

toJSON: () => any;
}
@@ -214,7 +214,7 @@ export class VirtualRepeat extends AbstractRepeater {
_prevItemsCount: number;

/**@internal */
containerEl: HTMLElement;
scrollerEl: HTMLElement;

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

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

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

const scroller = this.containerEl;
const scroller = this.scrollerEl;
if (shouldCalculateSize) {
const currentItemCount = items.length;
if (currentItemCount > 0 && this.viewCount() === 0) {
@@ -528,7 +528,7 @@ export class VirtualRepeat extends AbstractRepeater {
*/
getScroller(): HTMLElement {
return this._fixedHeightContainer
? this.containerEl
? this.scrollerEl
: document.documentElement;
}

@@ -541,7 +541,7 @@ export class VirtualRepeat extends AbstractRepeater {
scroller: scroller,
scrollHeight: scroller.scrollHeight,
scrollTop: scroller.scrollTop,
height: scroller.clientHeight
height: calcScrollHeight(scroller)
};
}

@@ -595,7 +595,7 @@ export class VirtualRepeat extends AbstractRepeater {
return;
}
const topBufferEl = this.topBufferEl;
const scrollerEl = this.containerEl;
const scrollerEl = this.scrollerEl;
const itemHeight = this.itemHeight;
/**
* 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 {
this._switchedDirection = false;
this._topBufferHeight = currentTopBufferHeight + adjustHeight;
this._bottomBufferHeight = Math$max(currentBottomBufferHeight - adjustHeight, 0);
if (this._bottomBufferHeight >= 0) {
this._updateBufferElements(true);
}
this._updateBufferElements(true);
} else if (this._scrollingUp) {
const isLastIndex = this.isLastIndex;
let viewsToMoveCount = currLastReboundIndex - firstIndex;
@@ -689,9 +687,7 @@ export class VirtualRepeat extends AbstractRepeater {
this._switchedDirection = false;
this._topBufferHeight = Math$max(currentTopBufferHeight - adjustHeight, 0);
this._bottomBufferHeight = currentBottomBufferHeight + adjustHeight;
if (this._topBufferHeight >= 0) {
this._updateBufferElements(true);
}
this._updateBufferElements(true);
}
this._previousFirst = firstIndex;
this._isScrolling = false;
@@ -777,8 +773,8 @@ export class VirtualRepeat extends AbstractRepeater {
const { _first, _scrollingUp, _scrollingDown, _previousFirst } = this;

let isScrolling = false;
let isScrollingDown = false;
let isScrollingUp = false;
let isScrollingDown = _scrollingDown;
let isScrollingUp = _scrollingUp;
let isSwitchedDirection = false;

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

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

0 comments on commit a6d6bfa

Please sign in to comment.
You can’t perform that action at this time.