tp-table/pagination.js

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();
}
});
}
}
};
});