UNPKG

@pascalhonegger/ng-datatable

Version:
436 lines (427 loc) 22.8 kB
import * as i0 from '@angular/core'; import { input, model, computed, effect, untracked, Directive, inject, signal, DestroyRef, ChangeDetectionStrategy, Component, NgModule } from '@angular/core'; import { Subject } from 'rxjs'; class DataTable { /** Array of data to display in table */ inputData = input.required({ ...(ngDevMode ? { debugName: "inputData" } : {}), alias: 'mfData', transform: (i) => i ?? [] }); /** Sort by parameter */ sortBy = model('', { ...(ngDevMode ? { debugName: "sortBy" } : {}), alias: 'mfSortBy' }); /** Sort order parameter (either `asc` or `desc`, default: `asc`) */ sortOrder = model('asc', { ...(ngDevMode ? { debugName: "sortOrder" } : {}), alias: 'mfSortOrder' }); /** Number of rows should be displayed on page (default: `1000`) */ rowsOnPage = model(1000, { ...(ngDevMode ? { debugName: "rowsOnPage" } : {}), alias: 'mfRowsOnPage' }); /** Page number (default: `1`) */ activePage = model(1, { ...(ngDevMode ? { debugName: "activePage" } : {}), alias: 'mfActivePage' }); inputDataLength = computed(() => this.inputData().length, ...(ngDevMode ? [{ debugName: "inputDataLength" }] : [])); data = computed(() => { const offset = (this.activePage() - 1) * this.rowsOnPage(); return [...this.inputData()] .sort(this.sorter(this.sortBy(), this.sortOrder())) .slice(offset, offset + this.rowsOnPage()); }, ...(ngDevMode ? [{ debugName: "data" }] : [])); onSortChange = new Subject(); onPageChange = new Subject(); constructor() { // Events which were published based on the old API, could probably be deleted at some point effect(() => { const sortBy = this.sortBy(); const sortOrder = this.sortOrder(); if (sortBy) { this.onSortChange.next({ sortBy: sortBy, sortOrder: sortOrder }); } }); effect(() => { this.setPage(untracked(this.activePage), this.rowsOnPage()); }); effect(() => { const inputDataLength = this.inputDataLength(); const rowsOnPage = this.rowsOnPage(); const activePage = untracked(this.activePage); const lastPage = Math.ceil(inputDataLength / rowsOnPage); const newActivePage = (lastPage < activePage ? lastPage : activePage) || 1; this.activePage.set(newActivePage); }); effect(() => { this.onPageChange.next({ activePage: this.activePage(), rowsOnPage: this.rowsOnPage(), dataLength: this.inputDataLength(), }); }); } getSort() { return { sortBy: this.sortBy(), sortOrder: this.sortOrder() }; } setSort(sortBy, sortOrder) { this.sortBy.set(sortBy); this.sortOrder.set(['asc', 'desc'].includes(sortOrder) ? sortOrder : 'asc'); } getPage() { return { activePage: this.activePage(), rowsOnPage: this.rowsOnPage(), dataLength: this.inputDataLength(), }; } setPage(activePage, rowsOnPage) { if (this.rowsOnPage() !== rowsOnPage || this.activePage() !== activePage) { this.rowsOnPage.set(rowsOnPage); this.activePage.set(this.activePage() !== activePage ? activePage : this.calculateNewActivePage(this.rowsOnPage(), rowsOnPage)); } } calculateNewActivePage(previousRowsOnPage, currentRowsOnPage) { const firstRowOnPage = (this.activePage() - 1) * previousRowsOnPage + 1; const newActivePage = Math.ceil(firstRowOnPage / currentRowsOnPage); return newActivePage; } 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: "21.0.6", ngImport: i0, type: DataTable, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: DataTable, isStandalone: true, selector: "table[mfData]", inputs: { inputData: { classPropertyName: "inputData", publicName: "mfData", isSignal: true, isRequired: true, transformFunction: null }, sortBy: { classPropertyName: "sortBy", publicName: "mfSortBy", isSignal: true, isRequired: false, transformFunction: null }, sortOrder: { classPropertyName: "sortOrder", publicName: "mfSortOrder", isSignal: true, isRequired: false, transformFunction: null }, rowsOnPage: { classPropertyName: "rowsOnPage", publicName: "mfRowsOnPage", isSignal: true, isRequired: false, transformFunction: null }, activePage: { classPropertyName: "activePage", publicName: "mfActivePage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortBy: "mfSortByChange", sortOrder: "mfSortOrderChange", rowsOnPage: "mfRowsOnPageChange", activePage: "mfActivePageChange" }, exportAs: ["mfDataTable"], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataTable, decorators: [{ type: Directive, args: [{ selector: 'table[mfData]', exportAs: 'mfDataTable', }] }], ctorParameters: () => [], propDecorators: { inputData: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfData", required: true }] }], sortBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfSortBy", required: false }] }, { type: i0.Output, args: ["mfSortByChange"] }], sortOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfSortOrder", required: false }] }, { type: i0.Output, args: ["mfSortOrderChange"] }], rowsOnPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfRowsOnPage", required: false }] }, { type: i0.Output, args: ["mfRowsOnPageChange"] }], activePage: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfActivePage", required: false }] }, { type: i0.Output, args: ["mfActivePageChange"] }] } }); class Paginator { injectMfTable = inject(DataTable, { optional: true }); /** explicitly specify reference data table, by default the parent `mfData` is injected */ inputMfTable = input(undefined, { ...(ngDevMode ? { debugName: "inputMfTable" } : {}), alias: 'mfTable' }); mfTable = computed(() => this.inputMfTable() ?? this.injectMfTable, ...(ngDevMode ? [{ debugName: "mfTable" }] : [])); activePage = signal(0, ...(ngDevMode ? [{ debugName: "activePage" }] : [])); rowsOnPage = signal(0, ...(ngDevMode ? [{ debugName: "rowsOnPage" }] : [])); dataLength = signal(0, ...(ngDevMode ? [{ debugName: "dataLength" }] : [])); lastPage = computed(() => { const rowsOnPage = this.rowsOnPage(); const dataLength = this.dataLength(); return rowsOnPage === 0 ? 0 : Math.ceil(dataLength / rowsOnPage); }, ...(ngDevMode ? [{ debugName: "lastPage" }] : [])); constructor() { let currentSubscription = undefined; effect(() => { const currentTable = this.mfTable(); this.onPageChangeSubscriber(currentTable.getPage()); currentSubscription?.unsubscribe(); currentSubscription = currentTable.onPageChange.subscribe(this.onPageChangeSubscriber); }); inject(DestroyRef).onDestroy(() => { currentSubscription?.unsubscribe(); }); } setPage(pageNumber) { this.mfTable().setPage(pageNumber, this.rowsOnPage()); } setRowsOnPage(rowsOnPage) { this.mfTable().setPage(this.activePage(), rowsOnPage); } onPageChangeSubscriber = (event) => { this.activePage.set(event.activePage); this.rowsOnPage.set(event.rowsOnPage); this.dataLength.set(event.dataLength); }; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Paginator, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: Paginator, isStandalone: true, selector: "mfPaginator", inputs: { inputMfTable: { classPropertyName: "inputMfTable", publicName: "mfTable", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: Paginator, decorators: [{ type: Component, args: [{ selector: 'mfPaginator', template: `<ng-content />`, changeDetection: ChangeDetectionStrategy.OnPush, }] }], ctorParameters: () => [], propDecorators: { inputMfTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfTable", required: false }] }] } }); class BootstrapPaginator { /** Specify values for buttons to change number of diplayed rows, e.g. [5, 10, 15] */ rowsOnPageSet = input.required(...(ngDevMode ? [{ debugName: "rowsOnPageSet" }] : [])); /** explicitly specify reference data table, by default the parent `mfData` is injected */ mfTable = input(...(ngDevMode ? [undefined, { debugName: "mfTable" }] : [])); minRowsOnPage = computed(() => this.rowsOnPageSet().reduce((previous, current) => (current < previous ? current : previous), 0), ...(ngDevMode ? [{ debugName: "minRowsOnPage" }] : [])); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BootstrapPaginator, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: BootstrapPaginator, isStandalone: true, selector: "mfBootstrapPaginator", inputs: { rowsOnPageSet: { classPropertyName: "rowsOnPageSet", publicName: "rowsOnPageSet", isSignal: true, isRequired: true, transformFunction: null }, mfTable: { classPropertyName: "mfTable", publicName: "mfTable", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ` <mfPaginator #p [mfTable]="mfTable()"> @if (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">&laquo;</span></a> </li> @if (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> } @if (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> } @if (p.activePage() > 2) { <li class="page-item" (click)="p.setPage(p.activePage() - 2)"> <a class="page-link">{{ p.activePage() - 2 }}</a> </li> } @if (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> @if (p.activePage() + 1 <= p.lastPage()) { <li class="page-item" (click)="p.setPage(p.activePage() + 1)"> <a class="page-link">{{ p.activePage() + 1 }}</a> </li> } @if (p.activePage() + 2 <= p.lastPage()) { <li class="page-item" (click)="p.setPage(p.activePage() + 2)"> <a class="page-link">{{ p.activePage() + 2 }}</a> </li> } @if (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> } @if (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">&raquo;</span></a> </li> </ul> } @if (p.dataLength() > minRowsOnPage()) { <ul class="pagination float-end"> @for (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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BootstrapPaginator, decorators: [{ type: Component, args: [{ selector: 'mfBootstrapPaginator', template: ` <mfPaginator #p [mfTable]="mfTable()"> @if (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">&laquo;</span></a> </li> @if (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> } @if (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> } @if (p.activePage() > 2) { <li class="page-item" (click)="p.setPage(p.activePage() - 2)"> <a class="page-link">{{ p.activePage() - 2 }}</a> </li> } @if (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> @if (p.activePage() + 1 <= p.lastPage()) { <li class="page-item" (click)="p.setPage(p.activePage() + 1)"> <a class="page-link">{{ p.activePage() + 1 }}</a> </li> } @if (p.activePage() + 2 <= p.lastPage()) { <li class="page-item" (click)="p.setPage(p.activePage() + 2)"> <a class="page-link">{{ p.activePage() + 2 }}</a> </li> } @if (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> } @if (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">&raquo;</span></a> </li> </ul> } @if (p.dataLength() > minRowsOnPage()) { <ul class="pagination float-end"> @for (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], changeDetection: ChangeDetectionStrategy.OnPush, styles: [".page-link{cursor:pointer}\n"] }] }], propDecorators: { rowsOnPageSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsOnPageSet", required: true }] }], mfTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "mfTable", required: false }] }] } }); class DefaultSorter { mfTable = inject((DataTable)); /** Specify how to sort data (argument for lodash function [\_.sortBy ](https://lodash.com/docs#sortBy)) */ sortBy = input.required({ ...(ngDevMode ? { debugName: "sortBy" } : {}), alias: 'by' }); isSortedByMeAsc = computed(() => { const tableSortBy = this.mfTable.sortBy(); const tableSortOrder = this.mfTable.sortOrder(); const mySort = this.sortBy(); return tableSortBy == mySort && tableSortOrder === 'asc'; }, ...(ngDevMode ? [{ debugName: "isSortedByMeAsc" }] : [])); isSortedByMeDesc = computed(() => { const tableSortBy = this.mfTable.sortBy(); const tableSortOrder = this.mfTable.sortOrder(); const mySort = this.sortBy(); return tableSortBy == mySort && tableSortOrder === 'desc'; }, ...(ngDevMode ? [{ debugName: "isSortedByMeDesc" }] : [])); 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: "21.0.6", ngImport: i0, type: DefaultSorter, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 /> @if (isSortedByMeAsc()) { <span aria-hidden="true" aria-label="asc">▲</span> } @else if (isSortedByMeDesc()) { <span aria-hidden="true" aria-label="desc">▼</span> } </a>`, isInline: true, styles: ["a{cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", 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 /> @if (isSortedByMeAsc()) { <span aria-hidden="true" aria-label="asc">▲</span> } @else if (isSortedByMeDesc()) { <span aria-hidden="true" aria-label="desc">▼</span> } </a>`, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["a{cursor:pointer}\n"] }] }], propDecorators: { sortBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "by", required: true }] }] } }); /** * Optional module which exports all components * @deprecated Should be replaced with component imports (DataTable, DefaultSorter, Paginator, BootstrapPaginator) */ class DataTableModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: DataTableModule, imports: [DataTable, DefaultSorter, Paginator, BootstrapPaginator], exports: [DataTable, DefaultSorter, Paginator, BootstrapPaginator] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DataTableModule }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", 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