@pascalhonegger/ng-datatable
Version:
DataTable component for Angular framework
458 lines (449 loc) • 20.6 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, inject, IterableDiffers, Input, Output, Directive, input, Component, computed, NgModule } from '@angular/core';
import { ReplaySubject } from 'rxjs';
class DataTable {
diff;
inputData = [];
sortBy = "";
sortOrder = "asc";
sortByChange = new EventEmitter();
sortOrderChange = new EventEmitter();
rowsOnPage = 1000;
activePage = 1;
mustRecalculateData = false;
data;
onSortChange = new ReplaySubject(1);
onPageChange = new EventEmitter();
constructor() {
const differs = inject(IterableDiffers);
this.diff = differs.find([]).create();
}
getSort() {
return { sortBy: this.sortBy, sortOrder: this.sortOrder };
}
setSort(sortBy, sortOrder) {
if (this.sortBy !== sortBy || this.sortOrder !== sortOrder) {
this.sortBy = sortBy;
this.sortOrder = ["asc", "desc"].indexOf(sortOrder) >= 0 ? sortOrder : "asc";
this.mustRecalculateData = true;
this.onSortChange.next({ sortBy: this.sortBy, sortOrder: this.sortOrder });
this.sortByChange.emit(this.sortBy);
this.sortOrderChange.emit(this.sortOrder);
}
}
getPage() {
return { activePage: this.activePage, rowsOnPage: this.rowsOnPage, dataLength: this.inputData.length };
}
setPage(activePage, rowsOnPage) {
if (this.rowsOnPage !== rowsOnPage || this.activePage !== activePage) {
this.activePage = this.activePage !== activePage ? activePage : this.calculateNewActivePage(this.rowsOnPage, rowsOnPage);
this.rowsOnPage = rowsOnPage;
this.mustRecalculateData = true;
this.onPageChange.emit({
activePage: this.activePage,
rowsOnPage: this.rowsOnPage,
dataLength: this.inputData ? this.inputData.length : 0
});
}
}
calculateNewActivePage(previousRowsOnPage, currentRowsOnPage) {
const firstRowOnPage = (this.activePage - 1) * previousRowsOnPage + 1;
const newActivePage = Math.ceil(firstRowOnPage / currentRowsOnPage);
return newActivePage;
}
recalculatePage() {
const lastPage = Math.ceil(this.inputData.length / this.rowsOnPage);
this.activePage = lastPage < this.activePage ? lastPage : this.activePage;
this.activePage = this.activePage || 1;
this.onPageChange.emit({
activePage: this.activePage,
rowsOnPage: this.rowsOnPage,
dataLength: this.inputData.length
});
}
ngOnChanges(changes) {
if (changes["rowsOnPage"]) {
this.rowsOnPage = changes["rowsOnPage"].previousValue;
this.setPage(this.activePage, changes["rowsOnPage"].currentValue);
this.mustRecalculateData = true;
}
if (changes["sortBy"] || changes["sortOrder"]) {
if (["asc", "desc"].indexOf(this.sortOrder) < 0) {
console.warn("ng-datatable: value for input mfSortOrder must be one of ['asc', 'desc'], but is:", this.sortOrder);
this.sortOrder = "asc";
}
if (this.sortBy) {
this.onSortChange.next({ sortBy: this.sortBy, sortOrder: this.sortOrder });
}
this.mustRecalculateData = true;
}
if (changes["inputData"]) {
this.inputData = changes["inputData"].currentValue || [];
this.diff.diff(this.inputData); // Update diff to prevent duplicate update in ngDoCheck
this.recalculatePage();
this.mustRecalculateData = true;
}
}
ngDoCheck() {
const changes = this.diff.diff(this.inputData);
if (changes) {
this.recalculatePage();
this.mustRecalculateData = true;
}
if (this.mustRecalculateData) {
this.fillData();
this.mustRecalculateData = false;
}
}
fillData() {
// this.activePage = this.activePage;
// this.rowsOnPage = this.rowsOnPage;
const offset = (this.activePage - 1) * this.rowsOnPage;
// let data = this.inputData;
// const sortBy = this.sortBy;
// if (typeof sortBy === "string" || sortBy instanceof String) {
// data = orderBy(data, this.caseInsensitiveIteratee(sortBy as string), [this.sortOrder]);
// } else {
// data = orderBy(data, sortBy, [this.sortOrder]);
// }
// data = slice(data, offset, offset + this.rowsOnPage);
this.data = [...this.inputData]
.sort(this.sorter(this.sortBy, this.sortOrder))
.slice(offset, offset + this.rowsOnPage);
}
caseInsensitiveIteratee(sortBy) {
return (row) => {
let value = row;
if (typeof sortBy === "string" || sortBy instanceof String) {
for (const sortByProperty of sortBy.split(".")) {
if (value) {
value = value[sortByProperty];
}
}
}
else if (typeof sortBy === "function") {
value = sortBy(value);
}
if (value && typeof value === "string" || value instanceof String) {
return value.toLowerCase();
}
return value;
};
}
compare(left, right) {
if (left === right) {
return 0;
}
if (left == null && right != null) {
return -1;
}
if (right == null) {
return 1;
}
return left > right ? 1 : -1;
}
sorter(sortBy, sortOrder) {
const order = sortOrder === "desc" ? -1 : 1;
if (Array.isArray(sortBy)) {
const iteratees = sortBy.map((entry) => this.caseInsensitiveIteratee(entry));
return (left, right) => {
for (const iteratee of iteratees) {
const comparison = this.compare(iteratee(left), iteratee(right)) * order;
if (comparison !== 0) {
return comparison;
}
}
return 0;
};
}
else {
const iteratee = this.caseInsensitiveIteratee(sortBy);
return (left, right) => this.compare(iteratee(left), iteratee(right)) * order;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DataTable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.4", type: DataTable, isStandalone: true, selector: "table[mfData]", inputs: { inputData: ["mfData", "inputData"], sortBy: ["mfSortBy", "sortBy"], sortOrder: ["mfSortOrder", "sortOrder"], rowsOnPage: ["mfRowsOnPage", "rowsOnPage"], activePage: ["mfActivePage", "activePage"] }, outputs: { sortByChange: "mfSortByChange", sortOrderChange: "mfSortOrderChange" }, exportAs: ["mfDataTable"], usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DataTable, decorators: [{
type: Directive,
args: [{
selector: "table[mfData]",
exportAs: "mfDataTable"
}]
}], ctorParameters: () => [], propDecorators: { inputData: [{
type: Input,
args: ["mfData"]
}], sortBy: [{
type: Input,
args: ["mfSortBy"]
}], sortOrder: [{
type: Input,
args: ["mfSortOrder"]
}], sortByChange: [{
type: Output,
args: ["mfSortByChange"]
}], sortOrderChange: [{
type: Output,
args: ["mfSortOrderChange"]
}], rowsOnPage: [{
type: Input,
args: ["mfRowsOnPage"]
}], activePage: [{
type: Input,
args: ["mfActivePage"]
}] } });
class Paginator {
injectMfTable = inject(DataTable, { optional: true });
inputMfTable = input(undefined, { alias: "mfTable" });
mfTable;
activePage;
rowsOnPage;
dataLength = 0;
lastPage;
ngOnChanges() {
this.mfTable = this.inputMfTable() ?? this.injectMfTable;
this.onPageChangeSubscriber(this.mfTable.getPage());
this.mfTable.onPageChange.subscribe(this.onPageChangeSubscriber);
}
setPage(pageNumber) {
this.mfTable.setPage(pageNumber, this.rowsOnPage);
}
setRowsOnPage(rowsOnPage) {
this.mfTable.setPage(this.activePage, rowsOnPage);
}
onPageChangeSubscriber = (event) => {
this.activePage = event.activePage;
this.rowsOnPage = event.rowsOnPage;
this.dataLength = event.dataLength;
this.lastPage = Math.ceil(this.dataLength / this.rowsOnPage);
};
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Paginator, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.4", type: Paginator, isStandalone: true, selector: "mfPaginator", inputs: { inputMfTable: { classPropertyName: "inputMfTable", publicName: "mfTable", isSignal: true, isRequired: false, transformFunction: null } }, usesOnChanges: true, ngImport: i0, template: `<ng-content></ng-content>`, isInline: true });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Paginator, decorators: [{
type: Component,
args: [{
selector: "mfPaginator",
template: `<ng-content></ng-content>`
}]
}] });
class BootstrapPaginator {
rowsOnPageSet = input([]);
mfTable = input();
minRowsOnPage = computed(() => this.rowsOnPageSet().reduce((previous, current) => current < previous ? current : previous, 0));
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: BootstrapPaginator, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: BootstrapPaginator, isStandalone: true, selector: "mfBootstrapPaginator", inputs: { rowsOnPageSet: { classPropertyName: "rowsOnPageSet", publicName: "rowsOnPageSet", isSignal: true, isRequired: false, transformFunction: null }, mfTable: { classPropertyName: "mfTable", publicName: "mfTable", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
<mfPaginator #p [mfTable]="mfTable()">
(p.dataLength > p.rowsOnPage) {
<ul class="pagination float-start">
<li class="page-item" [class.disabled]="p.activePage <= 1" (click)="p.setPage(1)">
<a class="page-link" aria-label="Previous"><span aria-hidden="true">«</span></a>
</li>
(p.activePage > 4 && p.activePage + 1 > p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage - 4)">
<a class="page-link">{{p.activePage-4}}</a>
</li>
}
(p.activePage > 3 && p.activePage + 2 > p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage - 3)">
<a class="page-link">{{p.activePage-3}}</a>
</li>
}
(p.activePage > 2) {
<li class="page-item" (click)="p.setPage(p.activePage - 2)">
<a class="page-link">{{p.activePage-2}}</a>
</li>
}
(p.activePage > 1) {
<li class="page-item" (click)="p.setPage(p.activePage - 1)">
<a class="page-link">{{p.activePage-1}}</a>
</li>
}
<li class="page-item active">
<a class="page-link">{{p.activePage}}</a>
</li>
(p.activePage + 1 <= p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage + 1)">
<a class="page-link">{{p.activePage+1}}</a>
</li>
}
(p.activePage + 2 <= p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage + 2)">
<a class="page-link">{{p.activePage+2}}</a>
</li>
}
(p.activePage + 3 <= p.lastPage && p.activePage < 3) {
<li class="page-item" (click)="p.setPage(p.activePage + 3)">
<a class="page-link">{{p.activePage+3}}</a>
</li>
}
(p.activePage + 4 <= p.lastPage && p.activePage < 2) {
<li class="page-item" (click)="p.setPage(p.activePage + 4)">
<a class="page-link">{{p.activePage+4}}</a>
</li>
}
<li class="page-item" [class.disabled]="p.activePage >= p.lastPage" (click)="p.setPage(p.lastPage)">
<a class="page-link" aria-label="Next"><span aria-hidden="true">»</span></a>
</li>
</ul>
}
(p.dataLength > minRowsOnPage()) {
<ul class="pagination float-end">
(rows of rowsOnPageSet(); track rows) {
<li class="page-item" [class.active]="p.rowsOnPage===rows" (click)="p.setRowsOnPage(rows)">
<a class="page-link">{{rows}}</a>
</li>
}
</ul>
}
</mfPaginator>
`, isInline: true, styles: [".page-link{cursor:pointer}\n"], dependencies: [{ kind: "component", type: Paginator, selector: "mfPaginator", inputs: ["mfTable"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: BootstrapPaginator, decorators: [{
type: Component,
args: [{ selector: "mfBootstrapPaginator", template: `
<mfPaginator #p [mfTable]="mfTable()">
(p.dataLength > p.rowsOnPage) {
<ul class="pagination float-start">
<li class="page-item" [class.disabled]="p.activePage <= 1" (click)="p.setPage(1)">
<a class="page-link" aria-label="Previous"><span aria-hidden="true">«</span></a>
</li>
(p.activePage > 4 && p.activePage + 1 > p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage - 4)">
<a class="page-link">{{p.activePage-4}}</a>
</li>
}
(p.activePage > 3 && p.activePage + 2 > p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage - 3)">
<a class="page-link">{{p.activePage-3}}</a>
</li>
}
(p.activePage > 2) {
<li class="page-item" (click)="p.setPage(p.activePage - 2)">
<a class="page-link">{{p.activePage-2}}</a>
</li>
}
(p.activePage > 1) {
<li class="page-item" (click)="p.setPage(p.activePage - 1)">
<a class="page-link">{{p.activePage-1}}</a>
</li>
}
<li class="page-item active">
<a class="page-link">{{p.activePage}}</a>
</li>
(p.activePage + 1 <= p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage + 1)">
<a class="page-link">{{p.activePage+1}}</a>
</li>
}
(p.activePage + 2 <= p.lastPage) {
<li class="page-item" (click)="p.setPage(p.activePage + 2)">
<a class="page-link">{{p.activePage+2}}</a>
</li>
}
(p.activePage + 3 <= p.lastPage && p.activePage < 3) {
<li class="page-item" (click)="p.setPage(p.activePage + 3)">
<a class="page-link">{{p.activePage+3}}</a>
</li>
}
(p.activePage + 4 <= p.lastPage && p.activePage < 2) {
<li class="page-item" (click)="p.setPage(p.activePage + 4)">
<a class="page-link">{{p.activePage+4}}</a>
</li>
}
<li class="page-item" [class.disabled]="p.activePage >= p.lastPage" (click)="p.setPage(p.lastPage)">
<a class="page-link" aria-label="Next"><span aria-hidden="true">»</span></a>
</li>
</ul>
}
(p.dataLength > minRowsOnPage()) {
<ul class="pagination float-end">
(rows of rowsOnPageSet(); track rows) {
<li class="page-item" [class.active]="p.rowsOnPage===rows" (click)="p.setRowsOnPage(rows)">
<a class="page-link">{{rows}}</a>
</li>
}
</ul>
}
</mfPaginator>
`, imports: [Paginator], styles: [".page-link{cursor:pointer}\n"] }]
}] });
class DefaultSorter {
mfTable = inject((DataTable));
sortBy = input.required({ alias: "by" });
isSortedByMeAsc = false;
isSortedByMeDesc = false;
ngOnInit() {
this.mfTable.onSortChange.subscribe((event) => {
this.isSortedByMeAsc = (event.sortBy == this.sortBy() && event.sortOrder === "asc");
this.isSortedByMeDesc = (event.sortBy == this.sortBy() && event.sortOrder === "desc");
});
}
sort() {
if (this.isSortedByMeAsc) {
this.mfTable.setSort(this.sortBy(), "desc");
}
else {
this.mfTable.setSort(this.sortBy(), "asc");
}
return false;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DefaultSorter, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: DefaultSorter, isStandalone: true, selector: "mfDefaultSorter", inputs: { sortBy: { classPropertyName: "sortBy", publicName: "by", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
<a (click)="sort()" (keydown.enter)="sort()" (keydown.space)="sort()" class="text-nowrap text-decoration-none" tabindex="0">
<ng-content></ng-content>
(isSortedByMeAsc) {
<span aria-hidden="true" aria-label="asc">▲</span>
} if (isSortedByMeDesc) {
<span aria-hidden="true" aria-label="desc">▼</span>
}
</a>`, isInline: true, styles: ["a{cursor:pointer}\n"] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DefaultSorter, decorators: [{
type: Component,
args: [{ selector: "mfDefaultSorter", template: `
<a (click)="sort()" (keydown.enter)="sort()" (keydown.space)="sort()" class="text-nowrap text-decoration-none" tabindex="0">
<ng-content></ng-content>
(isSortedByMeAsc) {
<span aria-hidden="true" aria-label="asc">▲</span>
} if (isSortedByMeDesc) {
<span aria-hidden="true" aria-label="desc">▼</span>
}
</a>`, styles: ["a{cursor:pointer}\n"] }]
}] });
class DataTableModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DataTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: DataTableModule, imports: [DataTable,
DefaultSorter,
Paginator,
BootstrapPaginator], exports: [DataTable,
DefaultSorter,
Paginator,
BootstrapPaginator] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DataTableModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DataTableModule, decorators: [{
type: NgModule,
args: [{
imports: [
DataTable,
DefaultSorter,
Paginator,
BootstrapPaginator
],
exports: [
DataTable,
DefaultSorter,
Paginator,
BootstrapPaginator
]
}]
}] });
/*
* Public API Surface of ng-datatable
*/
/**
* Generated bundle index. Do not edit.
*/
export { BootstrapPaginator, DataTable, DataTableModule, DefaultSorter, Paginator };
//# sourceMappingURL=pascalhonegger-ng-datatable.mjs.map