227 lines
6.4 KiB
JavaScript
227 lines
6.4 KiB
JavaScript
/**
|
|
@license
|
|
Copyright (c) 2022 trading_peter
|
|
This program is available under Apache License Version 2.0
|
|
*/
|
|
|
|
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
|
|
import { Helper } from '@era-core/era-mixins/era-helper-mixin.js';
|
|
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
import { timeOut } from '@polymer/polymer/lib/utils/async.js';
|
|
|
|
/**
|
|
* # ListLoader
|
|
*
|
|
* Methods to load list contents page by page.
|
|
*
|
|
* @polymerBehavior ListLoader
|
|
*/
|
|
export const Pagination = dedupingMixin(function(superClass) {
|
|
return class extends Helper(superClass) {
|
|
static get properties() {
|
|
return {
|
|
_entries: { type: Array },
|
|
_entriesSet: { type: Object },
|
|
_page: { type: Number },
|
|
_filter: { type: String },
|
|
_filterPage: { type: Number },
|
|
_limit: { type: Number }
|
|
};
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
this._entries = [];
|
|
this._entriesSet = new Set();
|
|
this._page = 1;
|
|
this._filterPage = 1;
|
|
this._limit = 350;
|
|
}
|
|
|
|
_fetch(query, cb) {
|
|
// Override
|
|
}
|
|
|
|
_isInFlight() {
|
|
// Override
|
|
}
|
|
|
|
get _hasFilter() {
|
|
return typeof this._filter === 'string' && this._filter.length > 0;
|
|
}
|
|
|
|
_reloadList() {
|
|
this._resetList();
|
|
this.__currentPage = 0;
|
|
this._lastPage = false;
|
|
this._listStateChanged();
|
|
}
|
|
|
|
_listStateChanged() {
|
|
if (this._hasFilter) {
|
|
this._fetchFiltered();
|
|
} else {
|
|
this._fetchPage();
|
|
}
|
|
}
|
|
|
|
_scrollThresholdTriggered(e) {
|
|
if (this.active && this._listOverflows()) {
|
|
this.__list = e.composedPath()[0].scrollTarget;
|
|
this.__listScrollOffset = this.__list.scrollTop;
|
|
|
|
if (this._hasFilter) {
|
|
this._filterPage++;
|
|
this._fetchFiltered();
|
|
} else {
|
|
this._page++;
|
|
this._fetchPage();
|
|
}
|
|
}
|
|
}
|
|
|
|
_resetList() {
|
|
if (typeof this.setProperties === 'function') {
|
|
this.setProperties({ _page: 1, _filterPage: 1, _entries: [] });
|
|
} else {
|
|
this._page = 1;
|
|
this._filterPage = 1;
|
|
this._entries = [];
|
|
}
|
|
}
|
|
|
|
_shouldFetch() {
|
|
if (this._page === undefined || this._limit === undefined) return false;
|
|
const pageChanged = this._page !== this.__currentPage;
|
|
|
|
let optionsChanged = false;
|
|
optionsChanged = JSON.stringify(this._statusFilter) !== JSON.stringify(this.__currentStatusFilter) || optionsChanged;
|
|
optionsChanged = JSON.stringify(this._sorting) !== JSON.stringify(this.__currentSorting) || optionsChanged;
|
|
|
|
this.__currentStatusFilter = this._statusFilter;
|
|
this.__currentSorting = this._sorting;
|
|
|
|
if (optionsChanged === true) {
|
|
this._resetList();
|
|
}
|
|
|
|
if ((pageChanged === false || this._lastPage || this._isInFlight()) && optionsChanged === false) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
_fetchPage() {
|
|
this._filterDebouncer = Debouncer.debounce(
|
|
this._filterDebouncer,
|
|
timeOut.after(20),
|
|
() => {
|
|
if (this._shouldFetch() === false) return;
|
|
|
|
this._fetch({ page: this._page, limit: this._limit, options: { statusFilter: this._statusFilter, sorting: this._sorting } }, (docs, pages) => {
|
|
if (this._page === 1) {
|
|
this._entriesSet = new Set();
|
|
}
|
|
|
|
docs = docs.filter(doc => this._entriesSet.has(doc._id) === false);
|
|
docs.forEach(doc => this._entriesSet.add(doc._id));
|
|
|
|
if (this._page === 1) {
|
|
this._entries = docs;
|
|
} else {
|
|
this._entries = this._entries.concat(docs);
|
|
}
|
|
|
|
// Restore scrolling position in the list.
|
|
if (this.__list !== undefined) {
|
|
this.__list.scrollTop = this.__listScrollOffset || 0;
|
|
}
|
|
|
|
this.__currentPage = this._page;
|
|
|
|
// Check if we reached the last page.
|
|
this._lastPage = pages <= this._page;
|
|
|
|
// Clear scroll threshold so the next page can be loaded.
|
|
this.clearTriggers();
|
|
|
|
this.__fillList();
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
_fetchFiltered() {
|
|
this._filterDebouncer = Debouncer.debounce(
|
|
this._filterDebouncer,
|
|
timeOut.after(300),
|
|
() => {
|
|
if (!this._hasFilter) {
|
|
this._lastPage = false;
|
|
this.__currentPage = false;
|
|
this._lastFilter = null;
|
|
this._page = 1;
|
|
this._filterPage = 1;
|
|
this.__listScrollOffset = 0;
|
|
this._fetchPage();
|
|
return;
|
|
}
|
|
|
|
if (this._shouldFetch() === false && this._lastPage === true && this._filter === this._lastFilter) return;
|
|
|
|
if (this._filter !== this._lastFilter) {
|
|
this._filterPage = 1;
|
|
}
|
|
|
|
this._lastFilter = this._filter;
|
|
|
|
this._fetch({ filter: this._filter, page: this._filterPage, limit: this._limit, options: {
|
|
statusFilter: this._statusFilter,
|
|
sorting: this._sorting
|
|
} }, (result, resp) => {
|
|
if (resp && resp.statusCode === 200) {
|
|
if (this._filterPage === 1) {
|
|
this._entries = result.entries.map(entry => entry.doc);
|
|
} else {
|
|
this._entries = this._entries.concat(result.entries.map(entry => entry.doc));
|
|
}
|
|
|
|
// Restore scrolling position in the list.
|
|
if (this.__list !== undefined) {
|
|
this.__list.scrollTop = this.__listScrollOffset || 0;
|
|
}
|
|
|
|
// Check if we reached the last page.
|
|
this._lastPage = result.pages <= this._filterPage || result.pages === 0;
|
|
|
|
// Clear scroll threshold so the next page can be loaded.
|
|
this.clearTriggers();
|
|
|
|
this.__fillList();
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
// Load pages until the list fills the screen.
|
|
// We check the list height after next render to see if the list already fills the screen.
|
|
// If not, load the next page.
|
|
// This needs to be triggered async so that the current `_fetchPage` request
|
|
// is finished and no longer considered "in flight".
|
|
__fillList() {
|
|
if (!this._lastPage && !this.listOverflows()) {
|
|
setTimeout(() => {
|
|
if (this._hasFilter) {
|
|
this._filterPage++;
|
|
this._fetchFiltered();
|
|
} else {
|
|
this._page++;
|
|
this._fetchPage();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
});
|