Skip to content

Commit

Permalink
fix(repeat): correctly handle mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
bigopon committed Mar 12, 2019
1 parent eafbfe1 commit 2a9eaf5
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 327 deletions.
8 changes: 3 additions & 5 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,9 @@ module.exports = function(config) {
mochaReporter: {
ignoreSkipped: true
},
// webpackMiddleware: {
// // webpack-dev-middleware configuration
// // i. e.
// stats: 'errors-only'
// }
webpackMiddleware: {
logLevel: 'silent'
},
});
};

Expand Down
67 changes: 39 additions & 28 deletions src/array-virtual-repeat-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { ICollectionObserverSplice, mergeSplice } from 'aurelia-binding';
import { ViewSlot } from 'aurelia-templating';
import { ArrayRepeatStrategy, createFullOverrideContext } from 'aurelia-templating-resources';
import { IView, IVirtualRepeatStrategy } from './interfaces';
import { getElementDistanceToBottomViewPort, Math$abs, Math$floor, Math$max, Math$min, rebindAndMoveView, updateVirtualOverrideContexts, updateVirtualRepeatContexts } from './utilities';
import {
getElementDistanceToBottomViewPort,
Math$abs,
Math$floor,
Math$max,
Math$min,
rebindAndMoveView,
updateAllViews
} from './utilities';
import { VirtualRepeat } from './virtual-repeat';
import { getDistanceToParent } from './utilities-dom';

Expand Down Expand Up @@ -74,6 +82,7 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
}
const local = repeat.local;
const lastIndex = currItemCount - 1;
// console.log({firstIndex, lastIndex, currItemCount, realViewsCount});
if (firstIndex + realViewsCount > lastIndex) {
// first = currItemCount - realViewsCount instead of: first = currItemCount - 1 - realViewsCount;
// this is because during view update
Expand Down Expand Up @@ -110,7 +119,7 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
for (let i = realViewsCount; i < minLength; i++) {
const overrideContext = createFullOverrideContext(repeat, items[i], i, currItemCount);
repeat.addView(overrideContext.bindingContext, overrideContext);
}
}
}

/**@internal */
Expand Down Expand Up @@ -253,7 +262,7 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
firstIndexAfterMutation = Math$max(0, firstIndexAfterMutation - removeDelta);
}
}
console.log({firstIndexAfterMutation});
// console.log({firstIndexAfterMutation});
newViewCount = 0;
// if array size is less than or equal to number of elements in View
// the nadjust first index to 0
Expand Down Expand Up @@ -299,7 +308,7 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
}
}
const newBotBufferItemCount = Math$max(0, newArraySize - newTopBufferItemCount - newViewCount);
console.log({ currViewCount, newViewCount, viewsRequiredCount, viewCountDelta, newBotBufferItemCount})
// console.log({ currViewCount, newViewCount, viewsRequiredCount, viewCountDelta, newBotBufferItemCount})

// first update will be to mimic the behavior of a normal repeat mutation
// where real views are inserted, removed
Expand All @@ -308,20 +317,20 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
repeat._isScrolling = false;
repeat._scrollingDown = repeat._scrollingUp = false;
repeat._first = firstIndexAfterMutation;
repeat._previousFirst = firstIndex
repeat._previousFirst = firstIndex;
repeat._lastRebind = firstIndexAfterMutation + newViewCount;
repeat._topBufferHeight = newTopBufferItemCount * itemHeight;
repeat._bottomBufferHeight = newBotBufferItemCount * itemHeight;
repeat._updateBufferElements(/*skip update?*/true);
}
// console.log({ firstIndexAfterMutation, newTopBufferItemCount, newBotBufferItemCount});
console.log({
first: repeat._first,
prevFirst: repeat._previousFirst,
last: repeat._lastRebind,
top: repeat._topBufferHeight,
bot: repeat._bottomBufferHeight
});
// console.log({
// first: repeat._first,
// prevFirst: repeat._previousFirst,
// last: repeat._lastRebind,
// top: repeat._topBufferHeight,
// bot: repeat._bottomBufferHeight
// });

const scrollerInfo = repeat.getScrollerInfo();
const topBufferDistance = getDistanceToParent(repeat.topBufferEl, scrollerInfo.scroller);
Expand All @@ -331,12 +340,14 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
? 0
: (scrollerInfo.scrollTop - topBufferDistance)
);
console.log({
top: scrollerInfo.scrollTop,
height: scrollerInfo.scrollHeight
}, repeat.topBufferEl.style.height, repeat.bottomBufferEl.style.height, repeat._bottomBufferHeight);

let first_index_after_scroll_adjustment = realScrolltop === 0 ? 0 : Math$floor(realScrolltop / itemHeight);
// console.log({
// top: scrollerInfo.scrollTop,
// height: scrollerInfo.scrollHeight
// }, repeat.topBufferEl.style.height, repeat.bottomBufferEl.style.height, repeat._bottomBufferHeight);

let first_index_after_scroll_adjustment = realScrolltop === 0
? 0
: Math$floor(realScrolltop / itemHeight);
// if first index after scroll adjustment doesn't fit with number of possible view
// it means the scroller has been too far down to the bottom and nolonger suitable to start from this index
// rollback until all views fit into new collection, or until has enough collection item to render
Expand Down Expand Up @@ -371,24 +382,23 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
// botBufferHeight: bot_buffer_item_count_after_scroll_adjustment * itemHeight,
// isAtBottom: bot_buffer_item_count_after_scroll_adjustment * itemHeight === 0
// });
console.log({
first: repeat._first,
prevFirst: repeat._previousFirst,
last: repeat._lastRebind,
top: repeat._topBufferHeight,
bot: repeat._bottomBufferHeight
});
// console.log({
// first: repeat._first,
// prevFirst: repeat._previousFirst,
// last: repeat._lastRebind,
// top: repeat._topBufferHeight,
// bot: repeat._bottomBufferHeight
// });
repeat._handlingMutations = false;
// prevent scroller update
// prevent scroller update
repeat.revertScrollCheckGuard();
repeat._updateBufferElements();
updateVirtualRepeatContexts(repeat, 0);
updateAllViews(repeat, 0);
// requestAnimationFrame(() => repeat._handleScroll());
// repeat._handleScroll();
// console.log('-end: %crunSplices', 'color: orangered');
// repeat._onScroll();


// for (i = 0; spliceCount > i; ++i) {
// const splice = splices[i];
// const removedSize = splice.removed.length;
Expand Down Expand Up @@ -511,6 +521,7 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy implements I
let addIndex = splice.index;
let end = splice.index + splice.addedCount;
for (; end > addIndex; ++addIndex) {
// tslint:disable-next-line
const hasDistanceToBottomViewPort = getElementDistanceToBottomViewPort(repeat.templateStrategy.getLastElement(repeat.topBufferEl, repeat.bottomBufferEl)) > 0;
if (repeat.viewCount() === 0
|| (!this._isIndexBeforeViewSlot(repeat, viewSlot, addIndex)
Expand Down
3 changes: 1 addition & 2 deletions src/null-virtual-repeat-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { NullRepeatStrategy, RepeatStrategy } from 'aurelia-templating-resources
import { VirtualRepeat } from './virtual-repeat';
import { IVirtualRepeatStrategy } from './interfaces';


export class NullVirtualRepeatStrategy extends NullRepeatStrategy implements IVirtualRepeatStrategy {

createFirstItem() {/**/}

instanceMutated() {/**/}
Expand Down
6 changes: 3 additions & 3 deletions src/utilities-dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const getScrollContainer = (element: Node): HTMLElement => {
current = current.parentNode as HTMLElement;
}
return document.documentElement;
}
};

/**
* Determine real distance of an element to top of current document
Expand All @@ -34,7 +34,7 @@ export const getElementDistanceToTopOfDocument = (element: Element): number => {
export const hasOverflowScroll = (element: HTMLElement): boolean => {
let style = element.style;
return style.overflowY === 'scroll' || style.overflow === 'scroll' || style.overflowY === 'auto' || style.overflow === 'auto';
}
};

/**
* A naive utility to calculate distance of a child element to one of its ancestor, typically used for scroller/buffer combo
Expand Down Expand Up @@ -65,4 +65,4 @@ export const getDistanceToParent = (child: HTMLElement, parent: HTMLElement) =>
return childOffsetTop + getDistanceToParent(offsetParent, parent);
}
}
}
};
59 changes: 28 additions & 31 deletions src/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { updateOverrideContext, createFullOverrideContext } from 'aurelia-templating-resources';
import { updateOverrideContext } from 'aurelia-templating-resources';
import { View } from 'aurelia-templating';
import { VirtualRepeat } from './virtual-repeat';
import { OverrideContext } from 'aurelia-binding';
import { IView } from './interfaces';

/**
* Get total value of a list of css style property on an element
*/
export function getStyleValues(element: Element, ...styles: string[]): number {
export const getStyleValues = (element: Element, ...styles: string[]): number => {
let currentStyle = window.getComputedStyle(element);
let value: number = 0;
let styleValue: number = 0;
Expand All @@ -16,60 +15,62 @@ export function getStyleValues(element: Element, ...styles: string[]): number {
value += $isNaN(styleValue) ? 0 : styleValue;
}
return value;
}
};

export function calcOuterHeight(element: Element): number {
export const calcOuterHeight = (element: Element): number => {
let height = element.getBoundingClientRect().height;
height += getStyleValues(element, 'marginTop', 'marginBottom');
return height;
}
};

export function insertBeforeNode(view: View, bottomBuffer: Element): void {
let parentElement = bottomBuffer.parentElement || bottomBuffer.parentNode;
parentElement.insertBefore(view.lastChild, bottomBuffer);
}
export const insertBeforeNode = (view: View, bottomBuffer: Element): void => {
// todo: account for anchor comment
bottomBuffer.parentNode.insertBefore(view.lastChild, bottomBuffer);
};

/**
* Update the override context.
* @param startIndex index in collection where to start updating.
*/
export function updateVirtualOverrideContexts(repeat: VirtualRepeat, startIndex: number): void {
let views = repeat.viewSlot.children;
let viewLength = views.length;
let collectionLength = repeat.items.length;
export const updateVirtualOverrideContexts = (repeat: VirtualRepeat, startIndex: number): void => {
const views = repeat.viewSlot.children;
const viewLength = views.length;
const collectionLength = repeat.items.length;

if (startIndex > 0) {
startIndex = startIndex - 1;
}

let delta = repeat._topBufferHeight / repeat.itemHeight;
const delta = repeat._topBufferHeight / repeat.itemHeight;

console.log('updating virtual repeat views', startIndex, viewLength);
for (; startIndex < viewLength; ++startIndex) {
for (; viewLength > startIndex; ++startIndex) {
updateOverrideContext(views[startIndex].overrideContext, startIndex + delta, collectionLength);
}
}
};

export const updateVirtualRepeatContexts = (repeat: VirtualRepeat, startIndex: number): void => {
export const updateAllViews = (repeat: VirtualRepeat, startIndex: number): void => {
const views = repeat.viewSlot.children;
const viewLength = views.length;
const collection = repeat.items;

const delta = repeat._topBufferHeight / repeat.itemHeight;
const delta = Math$floor(repeat._topBufferHeight / repeat.itemHeight);
let collectionIndex = 0;
let view;

for (; viewLength > startIndex; ++startIndex) {
collectionIndex = startIndex + delta;
rebindView(repeat, repeat.view(startIndex), collectionIndex, collection);
view = repeat.view(startIndex);
rebindView(repeat, view, collectionIndex, collection);
repeat.updateBindings(view);
}
}
};

export const rebindView = (repeat: VirtualRepeat, view: IView, collectionIndex: number, collection: any[]): void => {
view.bindingContext[repeat.local] = collection[collectionIndex];
updateOverrideContext(view.overrideContext, collectionIndex, collection.length);
}
};

export function rebindAndMoveView(repeat: VirtualRepeat, view: View, index: number, moveToBottom: boolean): void {
export const rebindAndMoveView = (repeat: VirtualRepeat, view: View, index: number, moveToBottom: boolean): void => {
const items = repeat.items;
const viewSlot = repeat.viewSlot;

Expand All @@ -82,16 +83,12 @@ export function rebindAndMoveView(repeat: VirtualRepeat, view: View, index: numb
viewSlot.children.unshift(viewSlot.children.splice(-1, 1)[0]);
repeat.templateStrategy.moveViewFirst(view, repeat.topBufferEl);
}
}
};


export function getElementDistanceToBottomViewPort(element: Element): number {
export const getElementDistanceToBottomViewPort = (element: Element): number => {
return document.documentElement.clientHeight - element.getBoundingClientRect().bottom;
}

export function getElementDistanceToTopViewPort(element: Element): number {
return element.getBoundingClientRect().top;
}
};

export const Math$abs = Math.abs;
export const Math$max = Math.max;
Expand Down
Loading

0 comments on commit 2a9eaf5

Please sign in to comment.