feat(infinite): add scroll in opposite direction (#8099)

* feat(infinite): add scroll in opposite direction

fixes

* test(infinite-scroll): opposite direction e2e test

* fix(infinite-scroll): keep scroll position

* feat(content): scroll down on load

* fix(infinite-scroll): scroll the content down on load

* Requested changes
This commit is contained in:
Job
2017-03-15 14:58:45 +01:00
committed by Manu MA
parent f9f9a1b441
commit 6918275bd3
7 changed files with 298 additions and 10 deletions

View File

@ -170,6 +170,8 @@ export class Content extends Ion implements OnDestroy, OnInit {
_viewCtrlReadSub: any;
/** @internal */
_viewCtrlWriteSub: any;
/** @internal */
_scrollDownOnLoad: boolean = false;
private _imgReqBfr: number;
private _imgRndBfr: number;
@ -478,13 +480,25 @@ export class Content extends Ion implements OnDestroy, OnInit {
*/
@Input()
get fullscreen(): boolean {
return !!this._fullscreen;
return this._fullscreen;
}
set fullscreen(val: boolean) {
this._fullscreen = isTrueProperty(val);
}
/**
* @input {boolean} If true, the content will scroll down on load.
*/
@Input()
get scrollDownOnLoad(): boolean {
return this._scrollDownOnLoad;
}
set scrollDownOnLoad(val: boolean) {
this._scrollDownOnLoad = isTrueProperty(val);
}
/**
* @private
*/
@ -830,6 +844,12 @@ export class Content extends Ion implements OnDestroy, OnInit {
this._tabs.setTabbarPosition(-1, 0);
}
}
// Scroll the page all the way down after setting dimensions
if (this._scrollDownOnLoad) {
this.scrollToBottom(0);
this._scrollDownOnLoad = false;
}
}
/**

View File

@ -0,0 +1,32 @@
import { Component, NgModule } from '@angular/core';
import { IonicApp, IonicModule } from '../../../../../ionic-angular';
@Component({
templateUrl: 'main.html'
})
export class E2EPage {}
@Component({
template: '<ion-nav [root]="root"></ion-nav>'
})
export class E2EApp {
root = E2EPage;
}
@NgModule({
declarations: [
E2EApp,
E2EPage,
],
imports: [
IonicModule.forRoot(E2EApp)
],
bootstrap: [IonicApp],
entryComponents: [
E2EApp,
E2EPage,
]
})
export class AppModule {}

View File

@ -0,0 +1,40 @@
<ion-content scrollDownOnLoad="true">
<b>This page should scroll down on load</b>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi scelerisque dolor lacus, ut vehicula arcu dapibus id. Morbi iaculis fermentum blandit. Curabitur tempus, ante et vehicula tempor, urna velit rutrum massa, quis suscipit purus lacus eget est. Sed nisi nulla, tempus id dictum a, cursus ut felis. Aliquam orci magna, rutrum nec tempor ac, fermentum quis eros. Sed ullamcorper felis sit amet tristique sagittis. Nullam sed tempus mi. Morbi sit amet lacinia leo. Nunc facilisis orci id consectetur dignissim. Integer dictum consectetur enim. Vivamus auctor, turpis ut eleifend pharetra, purus magna mattis arcu, vel pharetra tellus orci eget ex. Integer blandit posuere vehicula. Ut ipsum lorem, efficitur vitae eleifend tincidunt, fermentum nec lacus. Ut nec fermentum dui.
</p>
<b>It worked!</b>
</ion-content>

View File

@ -8,7 +8,7 @@ import { DomController } from '../../platform/dom-controller';
* @name InfiniteScroll
* @description
* The Infinite Scroll allows you to perform an action when the user
* scrolls a specified distance from the bottom of the page.
* scrolls a specified distance from the bottom or top of the page.
*
* The expression assigned to the `infinite` event is called when
* the user scrolls to the specified distance. When this expression
@ -148,6 +148,7 @@ export class InfiniteScroll {
_thr: string = '15%';
_thrPx: number = 0;
_thrPc: number = 0.15;
_position: string = POSITION_BOTTOM;
_init: boolean = false;
@ -192,6 +193,23 @@ export class InfiniteScroll {
this.enable(shouldEnable);
}
/**
* @input {string} The position of the infinite scroll element.
* The value can be either `top` or `bottom`.
* Default is `bottom`.
*/
@Input()
get position(): string {
return this._position;
}
set position(val: string) {
if (val === POSITION_TOP || val === POSITION_BOTTOM) {
this._position = val;
} else {
console.error(`Invalid value for ion-infinite-scroll's position input. Its value should be '${POSITION_BOTTOM}' or '${POSITION_TOP}'.`);
}
}
/**
* @output {event} Emitted when the scroll reaches
* the threshold distance. From within your infinite handler,
@ -229,17 +247,20 @@ export class InfiniteScroll {
// ******** DOM READ ****************
const d = this._content.getContentDimensions();
const height = d.contentHeight;
let reloadY = d.contentHeight;
if (this._thrPc) {
reloadY += (reloadY * this._thrPc);
} else {
reloadY += this._thrPx;
}
const threshold = this._thrPc ? (height * this._thrPc) : this._thrPx;
// ******** DOM READS ABOVE / DOM WRITES BELOW ****************
const distanceFromInfinite = ((d.scrollHeight - infiniteHeight) - d.scrollTop) - reloadY;
let distanceFromInfinite: number;
if (this._position === POSITION_BOTTOM) {
distanceFromInfinite = ((d.scrollHeight - infiniteHeight) - d.scrollTop) - height - threshold;
} else if (this._position === POSITION_TOP) {
distanceFromInfinite = d.scrollTop - infiniteHeight - threshold;
}
if (distanceFromInfinite < 0) {
// ******** DOM WRITE ****************
this._dom.write(() => {
@ -267,7 +288,26 @@ export class InfiniteScroll {
* to `enabled`.
*/
complete() {
if (this.state === STATE_LOADING) {
if (this._position === POSITION_TOP) {
// ******** DOM READ ****************
// Save the current content dimensions before the UI updates
const prevDim = this._content.getContentDimensions();
// ******** DOM READ ****************
this._dom.read(() => {
// UI has updated, save the new content dimensions
const newDim = this._content.getContentDimensions();
// New content was added on top, so the scroll position should be changed immediately to prevent it from jumping around
const newScrollTop = newDim.scrollHeight - (prevDim.scrollHeight - prevDim.scrollTop);
// ******** DOM WRITE ****************
this._dom.write(() => {
this._content.scrollTop = newScrollTop;
this.state = STATE_ENABLED;
});
});
} else {
this.state = STATE_ENABLED;
}
}
@ -319,6 +359,10 @@ export class InfiniteScroll {
ngAfterContentInit() {
this._init = true;
this._setListeners(this.state !== STATE_DISABLED);
if (this._position === POSITION_TOP) {
this._content.scrollDownOnLoad = true;
}
}
/**
@ -333,3 +377,6 @@ export class InfiniteScroll {
const STATE_ENABLED = 'enabled';
const STATE_DISABLED = 'disabled';
const STATE_LOADING = 'loading';
const POSITION_TOP = 'top';
const POSITION_BOTTOM = 'bottom';

View File

@ -91,6 +91,29 @@ describe('Infinite Scroll', () => {
});
describe('position', () => {
it('should default to bottom', () => {
expect(inf._position).toEqual('bottom');
});
it('should set to top', () => {
inf.position = 'top';
expect(inf._position).toEqual('top');
});
it('should set to bottom', () => {
inf.position = 'bottom';
expect(inf._position).toEqual('bottom');
});
it('should not set to anything else', () => {
inf.position = 'derp';
expect(inf._position).toEqual('bottom');
});
});
let config = mockConfig();
let inf: InfiniteScroll;

View File

@ -0,0 +1,95 @@
import { Component, ViewChild, NgModule } from '@angular/core';
import { Content, IonicApp, IonicModule, InfiniteScroll, NavController } from '../../../../../ionic-angular';
@Component({
templateUrl: 'main.html'
})
export class E2EPage1 {
@ViewChild(InfiniteScroll) infiniteScroll: InfiniteScroll;
@ViewChild(Content) content: Content;
items: number[] = [];
enabled: boolean = true;
constructor(public navCtrl: NavController) {
for (var i = 0; i < 30; i++) {
this.items.unshift( this.items.length );
}
}
doInfinite(infiniteScroll: InfiniteScroll) {
console.log('Begin async operation');
getAsyncData().then(newData => {
for (var i = 0; i < newData.length; i++) {
this.items.unshift( this.items.length );
}
console.log('Finished receiving data, async operation complete');
infiniteScroll.complete();
if (this.items.length > 90) {
this.enabled = false;
}
});
}
goToPage2() {
this.navCtrl.push(E2EPage2);
}
toggleInfiniteScroll() {
this.enabled = !this.enabled;
}
}
@Component({
template: '<ion-content><button ion-button (click)="navCtrl.pop()">Pop</button></ion-content>'
})
export class E2EPage2 {
constructor(public navCtrl: NavController) {}
}
@Component({
template: '<ion-nav [root]="root"></ion-nav>'
})
export class E2EApp {
root = E2EPage1;
}
@NgModule({
declarations: [
E2EApp,
E2EPage1,
E2EPage2
],
imports: [
IonicModule.forRoot(E2EApp)
],
bootstrap: [IonicApp],
entryComponents: [
E2EApp,
E2EPage1,
E2EPage2
]
})
export class AppModule {}
function getAsyncData(): Promise<any[]> {
// async return mock data
return new Promise(resolve => {
setTimeout(() => {
let data: number[] = [];
for (var i = 0; i < 30; i++) {
data.unshift(i);
}
resolve(data);
}, 2000);
});
}

View File

@ -0,0 +1,31 @@
<ion-header>
<ion-toolbar>
<ion-title>Infinite Scroll</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-infinite-scroll (ionInfinite)="doInfinite($event)" position="top" [enabled]="enabled">
<ion-infinite-scroll-content>
</ion-infinite-scroll-content>
</ion-infinite-scroll>
<ion-list>
<button ion-item (click)="goToPage2()" *ngFor="let item of items">
{{ item }}
</button>
</ion-list>
<p>
InfiniteScroll is enabled: {{enabled}}
</p>
<button ion-button (click)="toggleInfiniteScroll()" block>
Toggle InfiniteScroll Enabled
</button>
</ion-content>