Skip to content

Commit

Permalink
feat(all): support infinite scroll
Browse files Browse the repository at this point in the history
Provide a means to allow the user to continuously insert more data into the array being viewed.

resolve: #5
  • Loading branch information
AStoker committed Jul 5, 2016
1 parent 25fd4e1 commit 9a3b965
Show file tree
Hide file tree
Showing 31 changed files with 727 additions and 72 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,28 @@ With a surrounding fixed height container with overflow scroll. Note that `overf

If you are running the plugin in the `skeleton-naviagion` project, make sure to remove `overflow-x: hidden;` and `overflow-y: auto;` from `.page-host` in `styles.css`.

#### infinite scroll
```html
<template>
<div virtual-repeat.for="item of items" virtual-repeat-next="getMore">
${$index} ${item}
</div>
</template>
```

```javascript
export class MyVirtualList {
items = ['Foo', 'Bar', 'Baz'];
getMore() {
for(let i = 0; i < 100; ++i) {
this.items.push('item' + i);
}
}
}
```
The `virtual-repeat-next` attribute can accept a function, a promise, or a function that returns a promise.
The bound function will be called when the scroll container has reached a point where there are no more items to move into the DOM (i.e. when it reaches the end of a list).

## [Demo](http://aurelia.io/ui-virtualization/)

## Platform Support
Expand Down
10 changes: 10 additions & 0 deletions build/tasks/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ gulp.task('test', function (done) {
}, done).start();
});

/**
* Run test and watch for changes
*/
gulp.task('test:watch', function (done) {
new Karma({
configFile: __dirname + '/../../karma.conf.js',
singleRun: false
}, done).start();
});

/**
* Watch for file changes and re-run tests on each change
*/
Expand Down
22 changes: 11 additions & 11 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ System.config({
},

map: {
"aurelia-binding": "npm:[email protected].0",
"aurelia-bootstrapper": "npm:[email protected]beta.2.0.1",
"aurelia-binding": "npm:[email protected].2",
"aurelia-bootstrapper": "npm:[email protected]rc.1.0.1",
"aurelia-dependency-injection": "npm:[email protected]",
"aurelia-framework": "npm:[email protected].0",
"aurelia-framework": "npm:[email protected].1",
"aurelia-logging": "npm:[email protected]",
"aurelia-logging-console": "npm:[email protected]",
"aurelia-pal": "npm:[email protected]",
Expand Down Expand Up @@ -56,15 +56,15 @@ System.config({
"process": "github:jspm/[email protected]",
"util": "npm:[email protected]"
},
"npm:[email protected].0": {
"npm:[email protected].2": {
"aurelia-logging": "npm:[email protected]",
"aurelia-metadata": "npm:[email protected]",
"aurelia-pal": "npm:[email protected]",
"aurelia-task-queue": "npm:[email protected]"
},
"npm:[email protected]beta.2.0.1": {
"npm:[email protected]rc.1.0.1": {
"aurelia-event-aggregator": "npm:[email protected]",
"aurelia-framework": "npm:[email protected].0",
"aurelia-framework": "npm:[email protected].1",
"aurelia-history": "npm:[email protected]",
"aurelia-history-browser": "npm:[email protected]",
"aurelia-loader-default": "npm:[email protected]",
Expand All @@ -85,8 +85,8 @@ System.config({
"npm:[email protected]": {
"aurelia-logging": "npm:[email protected]"
},
"npm:[email protected].0": {
"aurelia-binding": "npm:[email protected].0",
"npm:[email protected].1": {
"aurelia-binding": "npm:[email protected].2",
"aurelia-dependency-injection": "npm:[email protected]",
"aurelia-loader": "npm:[email protected]",
"aurelia-logging": "npm:[email protected]",
Expand Down Expand Up @@ -136,12 +136,12 @@ System.config({
"aurelia-pal": "npm:[email protected]"
},
"npm:[email protected]": {
"aurelia-binding": "npm:[email protected].0",
"aurelia-binding": "npm:[email protected].2",
"aurelia-logging": "npm:[email protected]",
"aurelia-templating": "npm:[email protected]"
},
"npm:[email protected]": {
"aurelia-binding": "npm:[email protected].0",
"aurelia-binding": "npm:[email protected].2",
"aurelia-dependency-injection": "npm:[email protected]",
"aurelia-loader": "npm:[email protected]",
"aurelia-logging": "npm:[email protected]",
Expand All @@ -161,7 +161,7 @@ System.config({
"aurelia-templating": "npm:[email protected]"
},
"npm:[email protected]": {
"aurelia-binding": "npm:[email protected].0",
"aurelia-binding": "npm:[email protected].2",
"aurelia-dependency-injection": "npm:[email protected]",
"aurelia-loader": "npm:[email protected]",
"aurelia-logging": "npm:[email protected]",
Expand Down
7 changes: 4 additions & 3 deletions dist/amd/aurelia-ui-virtualization.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
define(['exports', './virtual-repeat'], function (exports, _virtualRepeat) {
define(['exports', './virtual-repeat', './virtual-repeat-next'], function (exports, _virtualRepeat, _virtualRepeatNext) {
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VirtualRepeat = undefined;
exports.VirtualRepeatNext = exports.VirtualRepeat = undefined;
exports.configure = configure;
function configure(config) {
config.globalResources('./virtual-repeat');
config.globalResources('./virtual-repeat', './virtual-repeat-next');
}

exports.VirtualRepeat = _virtualRepeat.VirtualRepeat;
exports.VirtualRepeatNext = _virtualRepeatNext.VirtualRepeatNext;
});
6 changes: 4 additions & 2 deletions dist/amd/template-strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ define(['exports', 'aurelia-pal', 'aurelia-templating', './utilities'], function
};

TableStrategy.prototype.moveViewFirst = function moveViewFirst(view, topBuffer) {
(0, _utilities.insertBeforeNode)(view, _aureliaPal.DOM.nextElementSibling(topBuffer.parentNode).previousSibling);
(0, _utilities.insertBeforeNode)(view, _aureliaPal.DOM.nextElementSibling(topBuffer.parentNode));
};

TableStrategy.prototype.moveViewLast = function moveViewLast(view, bottomBuffer) {
(0, _utilities.insertBeforeNode)(view, bottomBuffer.parentNode);
var previousSibling = bottomBuffer.parentNode.previousSibling;
var referenceNode = previousSibling.nodeType === 8 && previousSibling.data === 'anchor' ? previousSibling : bottomBuffer.parentNode;
(0, _utilities.insertBeforeNode)(view, referenceNode);
};

TableStrategy.prototype.createTopBufferElement = function createTopBufferElement(element) {
Expand Down
26 changes: 26 additions & 0 deletions dist/amd/virtual-repeat-next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
define(['exports', 'aurelia-templating'], function (exports, _aureliaTemplating) {
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VirtualRepeatNext = undefined;



var _dec, _class;

var VirtualRepeatNext = exports.VirtualRepeatNext = (_dec = (0, _aureliaTemplating.customAttribute)('virtual-repeat-next'), _dec(_class = function () {
function VirtualRepeatNext() {

}

VirtualRepeatNext.prototype.attached = function attached() {};

VirtualRepeatNext.prototype.bind = function bind(bindingContext, overrideContext) {
this.scope = { bindingContext: bindingContext, overrideContext: overrideContext };
};

return VirtualRepeatNext;
}()) || _class);
});
51 changes: 49 additions & 2 deletions dist/amd/virtual-repeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
});
exports.VirtualRepeat = undefined;

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
};

function _initDefineProp(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
Expand Down Expand Up @@ -103,6 +109,7 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
_this._fixedHeightContainer = false;
_this._hasCalculatedSizes = false;
_this._isAtTop = true;
_this._calledGetMore = false;

_initDefineProp(_this, 'items', _descriptor, _this);

Expand Down Expand Up @@ -276,6 +283,7 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
this._lastRebind = this._first;
var movedViewsCount = this._moveViews(viewsToMove);
var adjustHeight = movedViewsCount < viewsToMove ? this._bottomBufferHeight : itemHeight * movedViewsCount;
this._getMore();
this._switchedDirection = false;
this._topBufferHeight = this._topBufferHeight + adjustHeight;
this._bottomBufferHeight = this._bottomBufferHeight - adjustHeight;
Expand Down Expand Up @@ -308,6 +316,45 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
this._ticking = false;
};

VirtualRepeat.prototype._getMore = function _getMore() {
var _this5 = this;

if (this.isLastIndex) {
if (!this._calledGetMore) {
var _ret = function () {
var getMoreFunc = _this5.view(0).firstChild.getAttribute('virtual-repeat-next');
if (!getMoreFunc) {
return {
v: void 0
};
}
var getMore = _this5.scope.overrideContext.bindingContext[getMoreFunc];

_this5.observerLocator.taskQueue.queueMicroTask(function () {
_this5._calledGetMore = true;
if (getMore instanceof Promise) {
return getMore.then(function () {
_this5._calledGetMore = false;
});
} else if (typeof getMore === 'function') {
var result = getMore.bind(_this5.scope.overrideContext.bindingContext)();
if (result instanceof Promise) {
return result.then(function () {
_this5._calledGetMore = false;
});
} else {
_this5._calledGetMore = false;
return;
}
}
});
}();

if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
}
}
};

VirtualRepeat.prototype._checkScrolling = function _checkScrolling() {
if (this._first > this._previousFirst && (this._bottomBufferHeight > 0 || !this.isLastIndex)) {
if (!this._scrollingDown) {
Expand Down Expand Up @@ -346,15 +393,15 @@ define(['exports', 'aurelia-dependency-injection', 'aurelia-binding', 'aurelia-t
};

VirtualRepeat.prototype._moveViews = function _moveViews(length) {
var _this5 = this;
var _this6 = this;

var getNextIndex = this._scrollingDown ? function (index, i) {
return index + i;
} : function (index, i) {
return index - i;
};
var isAtFirstOrLastIndex = function isAtFirstOrLastIndex() {
return _this5._scrollingDown ? _this5.isLastIndex : _this5._isAtTop;
return _this6._scrollingDown ? _this6.isLastIndex : _this6._isAtTop;
};
var childrenLength = this.viewCount();
var viewIndex = this._scrollingDown ? 0 : childrenLength - 1;
Expand Down
27 changes: 17 additions & 10 deletions dist/aurelia-ui-virtualization.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import {
customAttribute,
View,
BoundViewFactory,
ViewSlot,
ViewResources,
TargetInstruction,
bindable,
templateController
} from 'aurelia-templating';
import {
updateOverrideContext,
ArrayRepeatStrategy,
Expand All @@ -10,16 +20,6 @@ import {
updateOneTimeBinding,
viewsRequireLifecycle
} from 'aurelia-templating-resources';
import {
View,
BoundViewFactory,
ViewSlot,
ViewResources,
TargetInstruction,
customAttribute,
bindable,
templateController
} from 'aurelia-templating';
import {
DOM
} from 'aurelia-pal';
Expand All @@ -43,6 +43,13 @@ export declare class DomHelper {
getElementDistanceToTopOfDocument(element: Element): number;
hasOverflowScroll(element: Element): boolean;
}

//Placeholder attribute to prohibit use of this attribute name in other places
export declare class VirtualRepeatNext {
constructor();
attached(): any;
bind(bindingContext?: any, overrideContext?: any): void;
}
export declare function calcOuterHeight(element: Element): number;
export declare function insertBeforeNode(view: View, bottomBuffer: number): void;

Expand Down
Loading

0 comments on commit 9a3b965

Please sign in to comment.