Skip to content

Commit 9dda64f

Browse files
chore(all): prepare release 1.0.0-beta.3.1.0
1 parent ab22140 commit 9dda64f

23 files changed

+495
-99
lines changed

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aurelia-ui-virtualization",
3-
"version": "1.0.0-beta.3.0.2",
3+
"version": "1.0.0-beta.3.1.0",
44
"description": "A plugin that provides a virtualized repeater and other virtualization services.",
55
"keywords": [
66
"aurelia",

dist/amd/array-virtual-repeat-strategy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ define(['exports', 'aurelia-templating-resources', './utilities'], function (exp
4747
};
4848

4949
ArrayVirtualRepeatStrategy.prototype.instanceChanged = function instanceChanged(repeat, items) {
50-
this._inPlaceProcessItems(repeat, items);
50+
this._inPlaceProcessItems(repeat, items, arguments.length <= 2 ? undefined : arguments[2]);
5151
};
5252

5353
ArrayVirtualRepeatStrategy.prototype._standardProcessInstanceChanged = function _standardProcessInstanceChanged(repeat, items) {
@@ -57,10 +57,9 @@ define(['exports', 'aurelia-templating-resources', './utilities'], function (exp
5757
}
5858
};
5959

60-
ArrayVirtualRepeatStrategy.prototype._inPlaceProcessItems = function _inPlaceProcessItems(repeat, items) {
60+
ArrayVirtualRepeatStrategy.prototype._inPlaceProcessItems = function _inPlaceProcessItems(repeat, items, first) {
6161
var itemsLength = items.length;
6262
var viewsLength = repeat.viewCount();
63-
var first = repeat._getIndexOfFirstView();
6463

6564
while (viewsLength > itemsLength) {
6665
viewsLength--;
@@ -81,6 +80,7 @@ define(['exports', 'aurelia-templating-resources', './utilities'], function (exp
8180
view.bindingContext[local] = items[i + first];
8281
view.overrideContext.$middle = middle;
8382
view.overrideContext.$last = last;
83+
view.overrideContext.$index = i + first;
8484
repeat.updateBindings(view);
8585
}
8686

dist/amd/template-strategy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-pal', 'aurelia-templ
8888
TableStrategy.prototype.getFirstElement = function getFirstElement(topBuffer) {
8989
var tbody = this._getTbodyElement(_aureliaPal.DOM.nextElementSibling(topBuffer));
9090
var tr = tbody.firstChild;
91-
return _aureliaPal.DOM.nextElementSibling(tr);
91+
return tr;
9292
};
9393

9494
TableStrategy.prototype.getLastElement = function getLastElement(bottomBuffer) {

dist/amd/virtual-repeat.js

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
148148
}, 500);
149149

150150
this.distanceToTop = this.domHelper.getElementDistanceToTopOfDocument(this.templateStrategy.getFirstElement(this.topBuffer));
151+
151152
this.topBufferDistance = this.templateStrategy.getTopBufferDistance(this.topBuffer);
152153

153154
if (this.domHelper.hasOverflowScroll(this.scrollContainer)) {
@@ -202,17 +203,44 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
202203
if (!this.scope) {
203204
return;
204205
}
206+
var reducingItems = false;
207+
var previousLastViewIndex = this._getIndexOfLastView();
208+
205209
var items = this.items;
206210
this.strategy = this.strategyLocator.getStrategy(items);
207211
if (items.length > 0 && this.viewCount() === 0) {
208212
this.strategy.createFirstItem(this);
209213
}
214+
215+
if (this._itemsLength >= items.length) {
216+
this._skipNextScrollHandle = true;
217+
reducingItems = true;
218+
}
219+
this._checkFixedHeightContainer();
210220
this._calcInitialHeights(items.length);
211221
if (!this.isOneTime && !this._observeInnerCollection()) {
212222
this._observeCollection();
213223
}
224+
this.strategy.instanceChanged(this, items, this._first);
225+
this._lastRebind = this._first;
226+
227+
if (reducingItems && previousLastViewIndex > this.items.length - 1) {
228+
if (this.scrollContainer.tagName === 'TBODY') {
229+
var realScrollContainer = this.scrollContainer.parentNode.parentNode;
230+
realScrollContainer.scrollTop = realScrollContainer.scrollTop + this.viewCount() * this.itemHeight;
231+
} else {
232+
this.scrollContainer.scrollTop = this.scrollContainer.scrollTop + this.viewCount() * this.itemHeight;
233+
}
234+
}
235+
if (!reducingItems) {
236+
this._previousFirst = this._first;
237+
this._scrollingDown = true;
238+
this._scrollingUp = false;
214239

215-
this.strategy.instanceChanged(this, items, this._viewsLength);
240+
this.isLastIndex = this._getIndexOfLastView() >= this.items.length - 1;
241+
}
242+
243+
this._handleScroll();
216244
};
217245

218246
VirtualRepeat.prototype.unbind = function unbind() {
@@ -265,6 +293,10 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
265293
if (!this._isAttached) {
266294
return;
267295
}
296+
if (this._skipNextScrollHandle) {
297+
this._skipNextScrollHandle = false;
298+
return;
299+
}
268300
var itemHeight = this.itemHeight;
269301
var scrollTop = this._fixedHeightContainer ? this.scrollContainer.scrollTop : pageYOffset - this.distanceToTop;
270302
this._first = Math.floor(scrollTop / itemHeight);
@@ -398,6 +430,12 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
398430
}
399431
};
400432

433+
VirtualRepeat.prototype._checkFixedHeightContainer = function _checkFixedHeightContainer() {
434+
if (this.domHelper.hasOverflowScroll(this.scrollContainer)) {
435+
this._fixedHeightContainer = true;
436+
}
437+
};
438+
401439
VirtualRepeat.prototype._adjustBufferHeights = function _adjustBufferHeights() {
402440
this.topBuffer.style.height = this._topBufferHeight + 'px';
403441
this.bottomBuffer.style.height = this._bottomBufferHeight + 'px';
@@ -484,20 +522,34 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
484522
}, 500);
485523
return;
486524
}
525+
487526
this._itemsLength = itemsLength;
488527
this.scrollContainerHeight = this._fixedHeightContainer ? this._calcScrollHeight(this.scrollContainer) : document.documentElement.clientHeight;
489528
this.elementsInView = Math.ceil(this.scrollContainerHeight / this.itemHeight) + 1;
490529
this._viewsLength = this.elementsInView * 2 + this._bufferSize;
491-
this._bottomBufferHeight = this.itemHeight * itemsLength - this.itemHeight * this._viewsLength;
492-
if (this._bottomBufferHeight < 0) {
493-
this._bottomBufferHeight = 0;
530+
531+
var newBottomBufferHeight = this.itemHeight * itemsLength - this.itemHeight * this._viewsLength;
532+
if (newBottomBufferHeight < 0) {
533+
newBottomBufferHeight = 0;
494534
}
495-
this.bottomBuffer.style.height = this._bottomBufferHeight + 'px';
496-
this._topBufferHeight = 0;
497-
this.topBuffer.style.height = this._topBufferHeight + 'px';
535+
if (this._topBufferHeight >= newBottomBufferHeight) {
536+
this._topBufferHeight = newBottomBufferHeight;
537+
this._bottomBufferHeight = 0;
538+
this._first = this._itemsLength - this._viewsLength;
539+
if (this._first < 0) {
540+
this._first = 0;
541+
}
542+
} else {
543+
this._first = this._getIndexOfFirstView();
544+
var adjustedTopBufferHeight = this._first * this.itemHeight;
545+
this._topBufferHeight = adjustedTopBufferHeight;
498546

499-
this.scrollContainer.scrollTop = 0;
500-
this._first = 0;
547+
this._bottomBufferHeight = newBottomBufferHeight - adjustedTopBufferHeight;
548+
if (this._bottomBufferHeight < 0) {
549+
this._bottomBufferHeight = 0;
550+
}
551+
}
552+
this._adjustBufferHeights();
501553
return;
502554
};
503555

dist/aurelia-ui-virtualization.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export declare class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy {
7878
* @param repeat The repeater instance.
7979
* @param items The new array instance.
8080
*/
81-
instanceChanged(repeat: VirtualRepeat, items: Array<any>): void;
81+
instanceChanged(repeat: VirtualRepeat, items: Array<any>, ...rest: any[]): void;
8282

8383
/**
8484
* Handle the repeat's collection instance mutating.
@@ -133,6 +133,7 @@ export declare class VirtualRepeat extends AbstractRepeater {
133133
handleInnerCollectionMutated(collection?: any, changes?: any): void;
134134

135135
// @override AbstractRepeater
136+
// How will these behaviors need to change since we are in a virtual list instead?
136137
viewCount(): any;
137138
views(): any;
138139
view(index?: any): any;

dist/aurelia-ui-virtualization.js

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy {
112112
* @param repeat The repeater instance.
113113
* @param items The new array instance.
114114
*/
115-
instanceChanged(repeat: VirtualRepeat, items: Array<any>): void {
116-
this._inPlaceProcessItems(repeat, items);
115+
instanceChanged(repeat: VirtualRepeat, items: Array<any>, ...rest): void {
116+
this._inPlaceProcessItems(repeat, items, rest[0]);
117117
}
118118

119119
_standardProcessInstanceChanged(repeat: VirtualRepeat, items: Array<any>): void {
@@ -123,10 +123,16 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy {
123123
}
124124
}
125125

126-
_inPlaceProcessItems(repeat: VirtualRepeat, items: Array<any>): void {
126+
_inPlaceProcessItems(repeat: VirtualRepeat, items: Array<any>, first: number): void {
127127
let itemsLength = items.length;
128128
let viewsLength = repeat.viewCount();
129-
let first = repeat._getIndexOfFirstView();
129+
/*
130+
Get index of first view is looking at the view which is from the ViewSlot
131+
The view slot has not yet been updated with the new list
132+
New first has to be the calculated "first" in our view slot, so the first one that's going to be rendered
133+
To figure out that one, we're going to have to know where we are in our scrolling so we can know how far down we've gone to show the first view
134+
That "first" is calculated and passed into here
135+
*/
130136
// remove unneeded views.
131137
while (viewsLength > itemsLength) {
132138
viewsLength--;
@@ -148,6 +154,7 @@ export class ArrayVirtualRepeatStrategy extends ArrayRepeatStrategy {
148154
view.bindingContext[local] = items[i + first];
149155
view.overrideContext.$middle = middle;
150156
view.overrideContext.$last = last;
157+
view.overrideContext.$index = i + first;
151158
repeat.updateBindings(view);
152159
}
153160
// add new views
@@ -457,7 +464,7 @@ export class TableStrategy {
457464
getFirstElement(topBuffer: Element): Element {
458465
const tbody = this._getTbodyElement(DOM.nextElementSibling(topBuffer));
459466
const tr = tbody.firstChild;
460-
return DOM.nextElementSibling(tr);
467+
return tr; //since the buffer is outside table, first element _is_ first element.
461468
}
462469

463470
getLastElement(bottomBuffer: Element): Element {
@@ -613,6 +620,7 @@ export class VirtualRepeat extends AbstractRepeater {
613620
}, 500);
614621

615622
this.distanceToTop = this.domHelper.getElementDistanceToTopOfDocument(this.templateStrategy.getFirstElement(this.topBuffer));
623+
// When dealing with tables, there can be gaps between elements, causing distances to be messed up. Might need to handle this case here.
616624
this.topBufferDistance = this.templateStrategy.getTopBufferDistance(this.topBuffer);
617625

618626
if (this.domHelper.hasOverflowScroll(this.scrollContainer)) {
@@ -667,17 +675,53 @@ export class VirtualRepeat extends AbstractRepeater {
667675
if (!this.scope) {
668676
return;
669677
}
678+
let reducingItems = false;
679+
let previousLastViewIndex = this._getIndexOfLastView();
680+
670681
let items = this.items;
671682
this.strategy = this.strategyLocator.getStrategy(items);
672683
if (items.length > 0 && this.viewCount() === 0) {
673684
this.strategy.createFirstItem(this);
674685
}
686+
// Skip scroll handling if we are decreasing item list
687+
// Otherwise if expanding list, call the handle scroll below
688+
if (this._itemsLength >= items.length) {
689+
//Scroll handle is redundant in this case since the instanceChanged will re-evaluate orderings
690+
// Also, when items are reduced, we're not having to move any bindings, just a straight rebind of the items in the list
691+
this._skipNextScrollHandle = true;
692+
reducingItems = true;
693+
}
694+
this._checkFixedHeightContainer();
675695
this._calcInitialHeights(items.length);
676696
if (!this.isOneTime && !this._observeInnerCollection()) {
677697
this._observeCollection();
678698
}
699+
this.strategy.instanceChanged(this, items, this._first);
700+
this._lastRebind = this._first; //Reset rebinding
701+
702+
if (reducingItems && previousLastViewIndex > this.items.length - 1) {
703+
//Do we need to set scrolltop so that we appear at the bottom of the list to match scrolling as far as we could?
704+
//We only want to execute this line if we're reducing such that it brings us to the bottom of the new list
705+
//Make sure we handle the special case of tables
706+
if (this.scrollContainer.tagName === 'TBODY') {
707+
let realScrollContainer = this.scrollContainer.parentNode.parentNode; //tbody > table > container
708+
realScrollContainer.scrollTop = realScrollContainer.scrollTop + (this.viewCount() * this.itemHeight);
709+
} else {
710+
this.scrollContainer.scrollTop = this.scrollContainer.scrollTop + (this.viewCount() * this.itemHeight);
711+
}
712+
}
713+
if (!reducingItems) {
714+
// If we're expanding our items, then we need to reset our previous first for the next go around of scroll handling
715+
this._previousFirst = this._first;
716+
this._scrollingDown = true; //Simulating the down scroll event to load up data appropriately
717+
this._scrollingUp = false;
718+
719+
//Make sure we fix any state (we could have been at the last index before, but this doesn't get set until too late for scrolling)
720+
this.isLastIndex = this._getIndexOfLastView() >= this.items.length - 1;
721+
}
679722

680-
this.strategy.instanceChanged(this, items, this._viewsLength);
723+
//Need to readjust the scroll position to "move" us back to the appropriate position, since moving the views will shift our view port's percieved location
724+
this._handleScroll();
681725
}
682726

683727
unbind(): void {
@@ -728,6 +772,10 @@ export class VirtualRepeat extends AbstractRepeater {
728772
if (!this._isAttached) {
729773
return;
730774
}
775+
if (this._skipNextScrollHandle) {
776+
this._skipNextScrollHandle = false;
777+
return;
778+
}
731779
let itemHeight = this.itemHeight;
732780
let scrollTop = this._fixedHeightContainer ? this.scrollContainer.scrollTop : pageYOffset - this.distanceToTop;
733781
this._first = Math.floor(scrollTop / itemHeight);
@@ -859,6 +907,12 @@ export class VirtualRepeat extends AbstractRepeater {
859907
}
860908
}
861909

910+
_checkFixedHeightContainer(): void {
911+
if (this.domHelper.hasOverflowScroll(this.scrollContainer)) {
912+
this._fixedHeightContainer = true;
913+
}
914+
}
915+
862916
_adjustBufferHeights(): void {
863917
this.topBuffer.style.height = `${this._topBufferHeight}px`;
864918
this.bottomBuffer.style.height = `${this._bottomBufferHeight}px`;
@@ -935,20 +989,38 @@ export class VirtualRepeat extends AbstractRepeater {
935989
}, 500);
936990
return;
937991
}
992+
938993
this._itemsLength = itemsLength;
939994
this.scrollContainerHeight = this._fixedHeightContainer ? this._calcScrollHeight(this.scrollContainer) : document.documentElement.clientHeight;
940995
this.elementsInView = Math.ceil(this.scrollContainerHeight / this.itemHeight) + 1;
941996
this._viewsLength = (this.elementsInView * 2) + this._bufferSize;
942-
this._bottomBufferHeight = this.itemHeight * itemsLength - this.itemHeight * this._viewsLength;
943-
if (this._bottomBufferHeight < 0) {
997+
998+
//Look at top buffer height (how far we've scrolled down)
999+
//If top buffer height is greater than the new bottom buffer height (how far we *can* scroll down)
1000+
// Then set top buffer height to max it can be (bottom buffer height - views in length?) and bottom buffer height to 0
1001+
let newBottomBufferHeight = this.itemHeight * itemsLength - this.itemHeight * this._viewsLength; //How much buffer room to the bottom if you were at the top
1002+
if (newBottomBufferHeight < 0) { // In case of small lists, ensure that we never set the buffer heights to impossible values
1003+
newBottomBufferHeight = 0;
1004+
}
1005+
if (this._topBufferHeight >= newBottomBufferHeight) { //Use case when items are removed (we've scrolled past where we can)
1006+
this._topBufferHeight = newBottomBufferHeight;
9441007
this._bottomBufferHeight = 0;
1008+
this._first = this._itemsLength - this._viewsLength;
1009+
if (this._first < 0) { // In case of small lists, ensure that we never set first to less than possible
1010+
this._first = 0;
1011+
}
1012+
} else { //Use case when items are added (we are adding scrollable space to the bottom)
1013+
// We need to re-evaluate which is the true "first". If we've added items, then the previous "first" is actually too far down the list
1014+
this._first = this._getIndexOfFirstView();
1015+
let adjustedTopBufferHeight = this._first * this.itemHeight; //appropriate buffer height for top, might be 1 too long...
1016+
this._topBufferHeight = adjustedTopBufferHeight;
1017+
//But what about when we've only scrolled slightly down the list? We need to readjust the top buffer height then
1018+
this._bottomBufferHeight = newBottomBufferHeight - adjustedTopBufferHeight;
1019+
if (this._bottomBufferHeight < 0) {
1020+
this._bottomBufferHeight = 0;
1021+
}
9451022
}
946-
this.bottomBuffer.style.height = `${this._bottomBufferHeight}px`;
947-
this._topBufferHeight = 0;
948-
this.topBuffer.style.height = `${this._topBufferHeight}px`;
949-
// TODO This will cause scrolling back to top when swapping collection instances that have different lengths - instead should keep the scroll position
950-
this.scrollContainer.scrollTop = 0;
951-
this._first = 0;
1023+
this._adjustBufferHeights();
9521024
return;
9531025
}
9541026

@@ -993,6 +1065,7 @@ export class VirtualRepeat extends AbstractRepeater {
9931065
}
9941066

9951067
// @override AbstractRepeater
1068+
// How will these behaviors need to change since we are in a virtual list instead?
9961069
viewCount() { return this.viewSlot.children.length; }
9971070
views() { return this.viewSlot.children; }
9981071
view(index) { return this.viewSlot.children[index]; }

0 commit comments

Comments
 (0)