diff --git a/ionic/components/content/content.js b/ionic/components/content/content.js
index 26c8aca918..8015fd88cb 100644
--- a/ionic/components/content/content.js
+++ b/ionic/components/content/content.js
@@ -15,5 +15,19 @@ export class Content {
// but we should be able to stamp out this behavior with a base IonicComponent
// or something, so all elements have a domElement reference or a getElement() method
this.domElement = elementRef.domElement;
+
+ setTimeout(() => {
+ this.scrollElement = this.domElement.children[0];
+ });
+ }
+
+ addScrollEventListener(handler) {
+ if(!this.scrollElement) { return; }
+
+ this.scrollElement.addEventListener('scroll', handler);
+
+ return () => {
+ this.scrollElement.removeEventListener('scroll', handler);
+ }
}
}
diff --git a/ionic/components/list/list.js b/ionic/components/list/list.js
index 91b64410fa..9e751e9de9 100644
--- a/ionic/components/list/list.js
+++ b/ionic/components/list/list.js
@@ -3,26 +3,52 @@ import {Component, Directive} from 'angular2/src/core/annotations_impl/annotatio
import {View} from 'angular2/src/core/annotations_impl/view';
import {IonicComponent} from 'ionic/config/component'
+import {ListVirtualScroll} from './virtual'
+
+import * as util from 'ionic/util';
@Component({
- selector: 'ion-list'
+ selector: 'ion-list',
+ properties: [
+ 'items',
+ 'virtual',
+ 'content'
+ ]
})
@View({
template: ``
})
export class List {
- constructor() {
-
- }
constructor(
elementRef: ElementRef
) {
this.domElement = elementRef.domElement;
this.config = List.config.invoke(this);
+
+ setTimeout(() => {
+ console.log('Content', this.content);
+ console.log('Virtual?', this.virtual);
+ console.log('Items?', this.items.length, 'of \'em');
+
+ if(util.isDefined(this.virtual)) {
+ this._initVirtualScrolling();
+ }
+ })
+ }
+
+ _initVirtualScrolling() {
+ if(!this.content) {
+ return;
+ }
+
+ this._virtualScrollingManager = new ListVirtualScroll(this);
+ }
+
+ setItemTemplate(item) {
+ this.itemTemplate = item;
}
}
-
new IonicComponent(List, {
propClasses: ['inset']
})
diff --git a/ionic/components/list/test/infinite/index.js b/ionic/components/list/test/infinite/index.js
new file mode 100644
index 0000000000..6477e055e2
--- /dev/null
+++ b/ionic/components/list/test/infinite/index.js
@@ -0,0 +1,50 @@
+import {bootstrap, NgFor, ProtoViewRef, ViewContainerRef} from 'angular2/angular2'
+import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
+import {View} from 'angular2/src/core/annotations_impl/view';
+import {Parent} from 'angular2/src/core/annotations_impl/visibility';
+
+import {Content, List, Item} from 'ionic/ionic';
+
+
+@Component({ selector: 'ion-app' })
+@View({
+ templateUrl: 'main.html',
+ directives: [Content, List, Item, ItemCellTemplate, NgFor]
+})
+class IonicApp {
+ constructor() {
+ console.log('IonicApp Start')
+
+ this.items = []
+ for(let i = 0; i < 1000; i++) {
+ this.items.push({
+ title: 'Item ' + i
+ })
+ }
+ }
+}
+
+/*
+ Used to find and register headers in a view, and this directive's
+ content will be moved up to the common navbar location, and created
+ using the same context as the view's content area.
+*/
+@Directive({
+ selector: 'template[cell]'
+})
+export class ItemCellTemplate {
+ constructor(@Parent() list: List, viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
+ console.log('Item cell template', list, viewContainer, protoViewRef);
+
+ this.protoViewRef = protoViewRef;
+ this.viewContainer = viewContainer;
+
+ list.setItemTemplate(this);
+ }
+}
+
+
+
+export function main() {
+ bootstrap(IonicApp);
+}
diff --git a/ionic/components/list/test/infinite/main.html b/ionic/components/list/test/infinite/main.html
new file mode 100644
index 0000000000..049fad1bc4
--- /dev/null
+++ b/ionic/components/list/test/infinite/main.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ {{$item.title}}
+
+
+
+
+
+
+
diff --git a/ionic/components/list/virtual.js b/ionic/components/list/virtual.js
new file mode 100644
index 0000000000..17877bf237
--- /dev/null
+++ b/ionic/components/list/virtual.js
@@ -0,0 +1,109 @@
+
+
+export class ListVirtualScroll {
+ constructor(list) {
+ this.list = list;
+ this.content = this.list.content;
+
+ this.viewportHeight = this.content.domElement.offsetHeight;
+
+ this.viewContainer = this.list.itemTemplate.viewContainer;
+
+ this.itemHeight = 60;
+
+ this.shownItems = {};
+ this.enteringItems = [];
+ this.leavingItems = [];
+
+ // Compute the initial sizes
+ this.resize();
+
+ this.content.addScrollEventListener((event) => {
+ this._handleVirtualScroll(event);
+ });
+ }
+
+ resize() {
+ this.viewportHeight = this.content.domElement.offsetHeight;
+ this.viewportScrollHeight = this.content.scrollElement.scrollHeight;
+
+ this.virtualHeight = this.list.items.length * this.itemHeight;
+ this.itemsPerScreen = this.viewportHeight / this.itemHeight;
+
+ console.log('VIRTUAL: resize(viewportHeight:', this.viewportHeight,
+ 'viewportScrollHeight:', this.viewportScrollHeight, 'virtualHeight:', this.virtualHeight,
+ ', itemsPerScreen:', this.itemsPerScreen, ')');
+ }
+
+ _handleVirtualScroll(event) {
+ let item;
+ let shownItemRef;
+
+ let st = event.target.scrollTop;
+ let sh = event.target.scrollHeight;
+
+ let topIndex = Math.floor(st / this.itemHeight);
+ let bottomIndex = Math.floor((st / this.itemHeight) + this.itemsPerScreen);
+
+ let items = this.list.items;
+
+ // Key iterate the shown items map
+ // and compare the index to our index range,
+ // pushing the items to remove to our leaving
+ // list if they're ouside this range.
+ for(let i in this.shownItems) {
+ if(i < topIndex || i > bottomIndex) {
+ this.leavingItems.push(this.shownItems[i]);
+ delete this.shownItems[i];
+ }
+ }
+
+ let realIndex = 0;
+ // Iterate the set of items that will be rendered, using the
+ // index from the actual items list as the map for the
+ // virtual items we draw
+ for(let i = topIndex, realIndex = 0; i < bottomIndex && i < items.length; i++, realIndex++) {
+ item = items[i];
+ console.log('Drawing item', i);
+
+ shownItemRef = this.shownItems[i];
+
+ // Is this a new item?
+ if(!shownItemRef) {
+ let itemView = this.viewContainer.create(this.list.itemTemplate.protoViewRef, realIndex);
+ itemView.setLocal('\$implicit', item);
+ itemView.setLocal('\$item', item);
+ shownItemRef = new VirtualItemRef(item, i, realIndex, itemView);
+
+ this.shownItems[i] = shownItemRef;
+ this.enteringItems.push(shownItemRef);
+ }
+
+
+ //tuple.view = viewContainer.create(protoViewRef, tuple.record.currentIndex);
+
+ }
+
+ while(this.leavingItems.length) {
+ let itemRef = this.leavingItems.pop();
+ this.viewContainer.remove(itemRef.realIndex);
+ }
+
+ console.log('VIRTUAL SCROLL: scroll(scrollTop:', st, 'topIndex:', topIndex, 'bottomIndex:', bottomIndex, ')');
+ console.log('Container has', this.list.domElement.children.length, 'children');
+ }
+
+ cellAtIndex(index) {
+
+ }
+
+}
+
+class VirtualItemRef {
+ constructor(item, index, realIndex, view) {
+ this.item = item;
+ this.index = index;
+ this.realIndex = realIndex;
+ this.view = view;
+ }
+}