@notiz/ngx-tablo
Version:
JSON powered material table for Angular
530 lines (510 loc) • 24.2 kB
JavaScript
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Pipe, input, model, computed, output, viewChild, effect, Component } from '@angular/core';
import * as i3 from '@angular/material/paginator';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import * as i4 from '@angular/material/sort';
import { MatSort, MatSortModule } from '@angular/material/sort';
import * as i2 from '@angular/material/table';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { Subject, Observable, takeUntil, BehaviorSubject } from 'rxjs';
import { _isNumberValue } from '@angular/cdk/coercion';
function isDefined(value) {
return value !== undefined && value !== null;
}
const resolveNestedValue = (row, path) => path.split(/\./).reduce((o, p) => o && o[p], row);
const resolveNestedColumnValue = (row, columnName) => {
if (columnName.match(/\./)) {
return resolveNestedValue(row, columnName);
}
else {
return row[columnName];
}
};
class CellValuePipe {
transform(row, column) {
if (isDefined(column.cellTemplate)) {
return column.cellTemplate;
}
if (column.cell && isDefined(column.cell(row))) {
return column.cell(row);
}
const resolvedNestedColumnValue = resolveNestedColumnValue(row, column.columnName);
if (isDefined(resolveNestedColumnValue)) {
return resolvedNestedColumnValue;
}
return undefined;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: CellValuePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.8", ngImport: i0, type: CellValuePipe, isStandalone: true, name: "cellValue" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: CellValuePipe, decorators: [{
type: Pipe,
args: [{ name: 'cellValue', standalone: true }]
}] });
class HasCellValuePipe {
transform(row, column) {
if (isDefined(column.cellTemplate) ||
(column.cell && isDefined(column.cell(row))) ||
isDefined(resolveNestedColumnValue(row, column.columnName))) {
return true;
}
return false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: HasCellValuePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.8", ngImport: i0, type: HasCellValuePipe, isStandalone: true, name: "hasCellValue" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: HasCellValuePipe, decorators: [{
type: Pipe,
args: [{ name: 'hasCellValue', standalone: true }]
}] });
class NestedColumnNamePipe {
transform(value, columnName) {
return resolveNestedColumnValue(value, columnName);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: NestedColumnNamePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.8", ngImport: i0, type: NestedColumnNamePipe, isStandalone: true, name: "nestedColumnName" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: NestedColumnNamePipe, decorators: [{
type: Pipe,
args: [{ name: 'nestedColumnName', standalone: true }]
}] });
class Tablo {
constructor() {
this.tableClasses = input('');
/**
* Add custom dataSource.
*
* Default: MatTableDataSource
*/
this.dataSource = input(new MatTableDataSource());
this.data = model();
this.columns = input([]);
this.displayedColumns = computed(() => this.columns().map((column) => column.columnName));
this.mode = input('client');
/**
* Sorting
*/
this.sortActive = model('');
this.sortDirection = model('');
/**
* Custom sorting function for `MatTableDataSource`.
*
* Use this if data properties don't match column names or for complex data objects.
*
* Default expects column `xyz` to represent `data['xyz']`.
*
*/
this.sortingDataAccessor = input();
/**
* Filter value for `MatTableDataSource`.
*/
this.filter = input();
/**
* Custom filter function for `MatTableDataSource`.
*/
this.filterPredicate = input();
/**
* Paging
*/
this.showPaging = model(false);
this.pagingDisabled = model(false);
this.hidePageSize = model(false);
this.showFirstLastButtons = model(true);
this.pageSize = model(0);
this.pageIndex = model(0);
this.length = model(0);
this.pageSizeOptions = model([10, 20, 50, 100]);
this.pageAriaLabel = model('Select page');
/**
* default to false
*/
this.resetPageOnSort = model(false);
/**
* Outputs
*/
this.rowClick = output();
this.pageChange = output();
this.sortChange = output();
this.paginator = viewChild(MatPaginator);
this.sort = viewChild.required(MatSort);
this.destroy$ = new Subject();
effect(() => {
this.fillData();
}, { allowSignalWrites: true });
effect(() => {
const filter = this.filter();
if (this.dataSource() instanceof MatTableDataSource) {
this.getMatTableDataSource().filter = filter || '';
}
}, { allowSignalWrites: true });
}
ngOnInit() {
this.columns().forEach((column) => {
if (column.lifecycle?.onInit) {
column.lifecycle.onInit(column);
}
});
if (this.dataSource() instanceof MatTableDataSource) {
if (this.filterPredicate()) {
this.getMatTableDataSource().filterPredicate = this.filterPredicate();
}
if (this.sortingDataAccessor()) {
this.getMatTableDataSource().sortingDataAccessor =
this.sortingDataAccessor();
}
}
}
ngAfterViewInit() {
this.columns().forEach((column) => {
if (column.lifecycle?.afterViewInit) {
column.lifecycle.afterViewInit(column);
}
});
this.sort().sortChange.subscribe((sort) => {
this.sortChange.emit(sort);
if (this.resetPageOnSort()) {
this.firstPage();
}
});
if (this.paginator()) {
this.paginator().page.subscribe((page) => this.pageChange.emit(page));
}
if (this.mode() === 'client' &&
this.dataSource() instanceof MatTableDataSource) {
this.getMatTableDataSource().paginator = this.paginator();
this.getMatTableDataSource().sort = this.sort();
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
firstPage() {
if (this.paginator()) {
this.paginator().firstPage();
}
}
lastPage() {
if (this.paginator()) {
this.paginator().lastPage();
}
}
clearFilter() {
if (this.dataSource() instanceof MatTableDataSource) {
this.getMatTableDataSource().filter = '';
}
}
fillData() {
if (this.data() && this.dataSource() instanceof MatTableDataSource) {
if (this.data() instanceof Observable) {
this.data()
.pipe(takeUntil(this.destroy$))
.subscribe((data) => (this.getMatTableDataSource().data = data));
}
else {
this.getMatTableDataSource().data = this.data();
}
}
}
getMatTableDataSource() {
return this.dataSource();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: Tablo, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: Tablo, isStandalone: true, selector: "ngx-tablo", inputs: { tableClasses: { classPropertyName: "tableClasses", publicName: "tableClasses", isSignal: true, isRequired: false, transformFunction: null }, dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, sortActive: { classPropertyName: "sortActive", publicName: "sortActive", isSignal: true, isRequired: false, transformFunction: null }, sortDirection: { classPropertyName: "sortDirection", publicName: "sortDirection", isSignal: true, isRequired: false, transformFunction: null }, sortingDataAccessor: { classPropertyName: "sortingDataAccessor", publicName: "sortingDataAccessor", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterPredicate: { classPropertyName: "filterPredicate", publicName: "filterPredicate", isSignal: true, isRequired: false, transformFunction: null }, showPaging: { classPropertyName: "showPaging", publicName: "showPaging", isSignal: true, isRequired: false, transformFunction: null }, pagingDisabled: { classPropertyName: "pagingDisabled", publicName: "pagingDisabled", isSignal: true, isRequired: false, transformFunction: null }, hidePageSize: { classPropertyName: "hidePageSize", publicName: "hidePageSize", isSignal: true, isRequired: false, transformFunction: null }, showFirstLastButtons: { classPropertyName: "showFirstLastButtons", publicName: "showFirstLastButtons", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, pageAriaLabel: { classPropertyName: "pageAriaLabel", publicName: "pageAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, resetPageOnSort: { classPropertyName: "resetPageOnSort", publicName: "resetPageOnSort", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { data: "dataChange", sortActive: "sortActiveChange", sortDirection: "sortDirectionChange", showPaging: "showPagingChange", pagingDisabled: "pagingDisabledChange", hidePageSize: "hidePageSizeChange", showFirstLastButtons: "showFirstLastButtonsChange", pageSize: "pageSizeChange", pageIndex: "pageIndexChange", length: "lengthChange", pageSizeOptions: "pageSizeOptionsChange", pageAriaLabel: "pageAriaLabelChange", resetPageOnSort: "resetPageOnSortChange", rowClick: "rowClick", pageChange: "pageChange", sortChange: "sortChange" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true, isSignal: true }], ngImport: i0, template: `
<table
[class]="tableClasses()"
mat-table
[dataSource]="dataSource()"
matSort
[matSortActive]="sortActive()"
[matSortDirection]="sortDirection()"
>
(column of columns(); track column; let i = $index) {
<ng-container [matColumnDef]="column.columnName">
<th
mat-header-cell
[mat-sort-header]="column.sortHeader || column.columnName"
[disabled]="!column.sort"
*matHeaderCellDef
>
(column.headerTemplate; as content) {
<ng-container
[ngTemplateOutlet]="headerContentTemplate"
[ngTemplateOutletContext]="{
content: content,
column: column
}"
></ng-container>
} {
(
(column.headerHTMLContent &&
column.headerHTMLContent(column)) ||
column.header;
as content
) {
<div
[class]="column.headerClassName"
[innerHTML]="content"
></div>
}
}
</th>
<td mat-cell *matCellDef="let row">
(row | hasCellValue: column; as content) {
<ng-container
[ngTemplateOutlet]="cellContentTemplate"
[ngTemplateOutletContext]="{
content: row | cellValue: column,
row: row,
className: column.className,
format: column.format
}"
>
</ng-container>
}
</td>
</ng-container>
}
<tr mat-header-row *matHeaderRowDef="displayedColumns()"></tr>
<tr
mat-row
*matRowDef="let row; columns: displayedColumns()"
(click)="rowClick.emit(row)"
></tr>
</table>
(showPaging()) {
<mat-paginator
[disabled]="pagingDisabled()"
[hidePageSize]="hidePageSize()"
[pageSizeOptions]="pageSizeOptions()"
[pageSize]="pageSize()"
[pageIndex]="pageIndex()"
[length]="length()"
[showFirstLastButtons]="showFirstLastButtons()"
[attr.aria-label]="pageAriaLabel()"
>
</mat-paginator>
}
<ng-template
#headerContentTemplate
let-content="content"
let-column="column"
>
<ng-container
[ngTemplateOutlet]="content"
[ngTemplateOutletContext]="{ column: column }"
></ng-container>
</ng-template>
<ng-template
#cellContentTemplate
let-content="content"
let-row="row"
let-className="className"
let-format="format"
>
(!content.createEmbeddedView) {
(format) {
<div [class]="className">
<!-- NOTE: each specified format will be displayed currently -->
(format.date) {
{{ content | date: format.date }}
}
(format.decimal) {
{{ content | number: format.decimal }}
}
(format.percent) {
{{ content | percent: format.percent }}
}
(format.currency) {
{{ content | currency: format.currency }}
}
(format.titleCase) {
{{ content | titlecase }}
}
</div>
} {
<div [class]="className" [innerHTML]="content"></div>
}
} {
<ng-container
[ngTemplateOutlet]="content"
[ngTemplateOutletContext]="{ row: row }"
></ng-container>
}
</ng-template>
`, isInline: true, styles: ["table{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.PercentPipe, name: "percent" }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i2.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i2.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i2.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i2.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i2.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i2.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i2.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i2.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i2.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i2.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "pipe", type: HasCellValuePipe, name: "hasCellValue" }, { kind: "pipe", type: CellValuePipe, name: "cellValue" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: Tablo, decorators: [{
type: Component,
args: [{ selector: 'ngx-tablo', standalone: true, imports: [
CommonModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
NestedColumnNamePipe,
HasCellValuePipe,
CellValuePipe,
], template: `
<table
[class]="tableClasses()"
mat-table
[dataSource]="dataSource()"
matSort
[matSortActive]="sortActive()"
[matSortDirection]="sortDirection()"
>
(column of columns(); track column; let i = $index) {
<ng-container [matColumnDef]="column.columnName">
<th
mat-header-cell
[mat-sort-header]="column.sortHeader || column.columnName"
[disabled]="!column.sort"
*matHeaderCellDef
>
(column.headerTemplate; as content) {
<ng-container
[ngTemplateOutlet]="headerContentTemplate"
[ngTemplateOutletContext]="{
content: content,
column: column
}"
></ng-container>
} {
(
(column.headerHTMLContent &&
column.headerHTMLContent(column)) ||
column.header;
as content
) {
<div
[class]="column.headerClassName"
[innerHTML]="content"
></div>
}
}
</th>
<td mat-cell *matCellDef="let row">
(row | hasCellValue: column; as content) {
<ng-container
[ngTemplateOutlet]="cellContentTemplate"
[ngTemplateOutletContext]="{
content: row | cellValue: column,
row: row,
className: column.className,
format: column.format
}"
>
</ng-container>
}
</td>
</ng-container>
}
<tr mat-header-row *matHeaderRowDef="displayedColumns()"></tr>
<tr
mat-row
*matRowDef="let row; columns: displayedColumns()"
(click)="rowClick.emit(row)"
></tr>
</table>
(showPaging()) {
<mat-paginator
[disabled]="pagingDisabled()"
[hidePageSize]="hidePageSize()"
[pageSizeOptions]="pageSizeOptions()"
[pageSize]="pageSize()"
[pageIndex]="pageIndex()"
[length]="length()"
[showFirstLastButtons]="showFirstLastButtons()"
[attr.aria-label]="pageAriaLabel()"
>
</mat-paginator>
}
<ng-template
#headerContentTemplate
let-content="content"
let-column="column"
>
<ng-container
[ngTemplateOutlet]="content"
[ngTemplateOutletContext]="{ column: column }"
></ng-container>
</ng-template>
<ng-template
#cellContentTemplate
let-content="content"
let-row="row"
let-className="className"
let-format="format"
>
(!content.createEmbeddedView) {
(format) {
<div [class]="className">
<!-- NOTE: each specified format will be displayed currently -->
(format.date) {
{{ content | date: format.date }}
}
(format.decimal) {
{{ content | number: format.decimal }}
}
(format.percent) {
{{ content | percent: format.percent }}
}
(format.currency) {
{{ content | currency: format.currency }}
}
(format.titleCase) {
{{ content | titlecase }}
}
</div>
} {
<div [class]="className" [innerHTML]="content"></div>
}
} {
<ng-container
[ngTemplateOutlet]="content"
[ngTemplateOutletContext]="{ row: row }"
></ng-container>
}
</ng-template>
`, styles: ["table{width:100%}\n"] }]
}], ctorParameters: () => [] });
class LoadingDataSource {
constructor() {
this.dataSubject = new BehaviorSubject([]);
this.loadingSubject = new BehaviorSubject(false);
this.disconnect$ = new Subject();
this.loading$ = this.loadingSubject.asObservable();
}
connect(collectionViewer) {
return this.dataSubject.asObservable();
}
disconnect(collectionViewer) {
this.dataSubject.complete();
this.loadingSubject.complete();
this.disconnect$.next();
this.disconnect$.complete();
}
}
/**
* MatTableDataSource that supports sorting for nested objects.
*/
class NestedObjectDataSource extends MatTableDataSource {
constructor(initialData) {
super(initialData);
this.sortingDataAccessor = (data, sortHeaderId) => {
const value = resolveNestedValue(data, sortHeaderId);
return _isNumberValue(value) ? Number(value) : value;
};
}
}
const nestedObjectDataSource = (initialData) => new NestedObjectDataSource(initialData);
/*
* Public API Surface of ngx-tablo
*/
/**
* Generated bundle index. Do not edit.
*/
export { LoadingDataSource, NestedColumnNamePipe, NestedObjectDataSource, Tablo, nestedObjectDataSource, resolveNestedColumnValue, resolveNestedValue };
//# sourceMappingURL=notiz-ngx-tablo.mjs.map