client/paging: fix loading on small page sizes

Fixes #126
This commit is contained in:
rr- 2017-02-19 13:33:13 +01:00
parent 33b49ebffd
commit 5dfdfd49e9
2 changed files with 60 additions and 32 deletions

View file

@ -1,5 +1,7 @@
<div class='pager'> <div class='pager'>
<div class='page-header-holder'></div> <div class='page-header-holder'></div>
<div class='messages'></div> <div class='messages'></div>
<div class='page-guard top'></div>
<div class='pages-holder'></div> <div class='pages-holder'></div>
<div class='page-guard bottom'></div>
</div> </div>

View file

@ -6,6 +6,13 @@ const views = require('../util/views.js');
const holderTemplate = views.getTemplate('endless-pager'); const holderTemplate = views.getTemplate('endless-pager');
const pageTemplate = views.getTemplate('endless-pager-page'); const pageTemplate = views.getTemplate('endless-pager-page');
function isScrolledIntoView(el) {
const elemTop = el.getBoundingClientRect().top;
const elemBottom = el.getBoundingClientRect().bottom;
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
return isVisible;
}
class EndlessPageView { class EndlessPageView {
constructor(ctx) { constructor(ctx) {
this._hostNode = document.getElementById('content-holder'); this._hostNode = document.getElementById('content-holder');
@ -13,28 +20,35 @@ class EndlessPageView {
} }
run(ctx) { run(ctx) {
this._destroy();
this._active = true; this._active = true;
this._working = 0; this._runningRequests = 0;
this._init = false; this._initialPageLoad = true;
this.clearMessages(); this.clearMessages();
views.emptyContent(this._pagesHolderNode); views.emptyContent(this._pagesHolderNode);
this.threshold = window.innerHeight / 3;
this.minOffsetShown = null; this.minOffsetShown = null;
this.maxOffsetShown = null; this.maxOffsetShown = null;
this.totalRecords = null; this.totalRecords = null;
this.currentOffset = 0; this.currentOffset = 0;
this.defaultLimit = parseInt(ctx.parameters.limit || ctx.defaultLimit);
const offset = parseInt(ctx.parameters.offset || 0); const initialOffset = parseInt(ctx.parameters.offset || 0);
const limit = parseInt(ctx.parameters.limit || ctx.defaultLimit); this._loadPage(ctx, initialOffset, this.defaultLimit, true)
this._loadPage(ctx, offset, limit, true)
.then(pageNode => { .then(pageNode => {
if (offset !== 0) { if (initialOffset !== 0) {
pageNode.scrollIntoView(); pageNode.scrollIntoView();
} }
}); });
this._probePageLoad(ctx);
this._timeout = window.setInterval(() => {
window.requestAnimationFrame(() => {
this._probePageLoad(ctx);
this._syncUrl(ctx);
});
}, 250);
views.monitorNodeRemoval(this._pagesHolderNode, () => this._destroy()); views.monitorNodeRemoval(this._pagesHolderNode, () => this._destroy());
} }
@ -43,27 +57,24 @@ class EndlessPageView {
return this._hostNode.querySelector('.page-header-holder'); return this._hostNode.querySelector('.page-header-holder');
} }
get topPageGuardNode() {
return this._hostNode.querySelector('.page-guard.top');
}
get bottomPageGuardNode() {
return this._hostNode.querySelector('.page-guard.bottom');
}
get _pagesHolderNode() { get _pagesHolderNode() {
return this._hostNode.querySelector('.pages-holder'); return this._hostNode.querySelector('.pages-holder');
} }
_destroy() { _destroy() {
window.clearInterval(this._timeout);
this._active = false; this._active = false;
} }
_probePageLoad(ctx) { _syncUrl(ctx) {
if (this._active) {
window.setTimeout(() => {
window.requestAnimationFrame(() => {
this._probePageLoad(ctx);
});
}, 250);
}
if (this._working) {
return;
}
let topPageNode = null; let topPageNode = null;
let element = document.elementFromPoint( let element = document.elementFromPoint(
window.innerWidth / 2, window.innerWidth / 2,
@ -89,6 +100,12 @@ class EndlessPageView {
false); false);
this.currentOffset = topOffset; this.currentOffset = topOffset;
} }
}
_probePageLoad(ctx) {
if (!this._active || this._runningRequests) {
return;
}
if (this.totalRecords === null) { if (this.totalRecords === null) {
return; return;
@ -97,32 +114,41 @@ class EndlessPageView {
document.documentElement.scrollHeight - document.documentElement.scrollHeight -
document.documentElement.clientHeight; document.documentElement.clientHeight;
if (this.minOffsetShown > 0 && window.scrollY < this.threshold) { if (this.minOffsetShown > 0 &&
isScrolledIntoView(this.topPageGuardNode)) {
this._loadPage( this._loadPage(
ctx, this.minOffsetShown - topLimit, topLimit, false); ctx,
} else if (this.maxOffsetShown < this.totalRecords && this.minOffsetShown - this.defaultLimit,
window.scrollY + this.threshold > scrollHeight) { this.defaultLimit,
false);
}
if (this.maxOffsetShown < this.totalRecords &&
isScrolledIntoView(this.bottomPageGuardNode)) {
this._loadPage( this._loadPage(
ctx, this.maxOffsetShown, topLimit, true); ctx,
this.maxOffsetShown,
this.defaultLimit,
true);
} }
} }
_loadPage(ctx, offset, limit, append) { _loadPage(ctx, offset, limit, append) {
this._working++; this._runningRequests++;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ctx.requestPage(offset, limit).then(response => { ctx.requestPage(offset, limit).then(response => {
if (!this._active) { if (!this._active) {
this._working--; this._runningRequests--;
return Promise.reject(); return Promise.reject();
} }
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
let pageNode = this._renderPage(ctx, append, response); let pageNode = this._renderPage(ctx, append, response);
this._working--; this._runningRequests--;
resolve(pageNode); resolve(pageNode);
}); });
}, error => { }, error => {
this.showError(error.message); this.showError(error.message);
this._working--; this._runningRequests--;
reject(); reject();
}); });
}); });
@ -165,7 +191,7 @@ class EndlessPageView {
if (append) { if (append) {
this._pagesHolderNode.appendChild(pageNode); this._pagesHolderNode.appendChild(pageNode);
if (!this._init && response.offset > 0) { if (this._initialPageLoad && response.offset > 0) {
window.scroll(0, pageNode.getBoundingClientRect().top); window.scroll(0, pageNode.getBoundingClientRect().top);
} }
} else { } else {
@ -179,7 +205,7 @@ class EndlessPageView {
this.showInfo('No data to show'); this.showInfo('No data to show');
} }
this._init = true; this._initialPageLoad = false;
return pageNode; return pageNode;
} }