dynamic-mat-table
Version:
dynamic-mat-table is an Angular component for presenting large and complex data with a lightning fast performance (at least 10x faster) and excellent level of control over the presentation.
1,157 lines (1,146 loc) • 163 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, Directive, ChangeDetectorRef, ViewChild, Input, HostBinding, Output, Component, ChangeDetectionStrategy, ContentChildren, Inject, Injector, Renderer2, Compiler, ComponentFactoryResolver, ViewContainerRef, NgModule, HostListener, forwardRef, NgZone, ContentChild, ElementRef, TemplateRef, COMPILER_OPTIONS, CompilerFactory } from '@angular/core';
import { Observable, merge, of, combineLatest, Subscription, ReplaySubject, Subject, BehaviorSubject } from 'rxjs';
import { map, delay, filter, distinctUntilChanged, tap, takeWhile, takeUntil, switchMap } from 'rxjs/operators';
import { MatTableDataSource, MatTable, MatTableModule } from '@angular/material/table';
import { CdkVirtualScrollViewport, VIRTUAL_SCROLL_STRATEGY, ScrollingModule } from '@angular/cdk/scrolling';
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import { SelectionModel } from '@angular/cdk/collections';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatPaginator, PageEvent, MatPaginatorIntl, MatPaginatorModule } from '@angular/material/paginator';
import { trigger, transition, query, style, stagger, animate, state } from '@angular/animations';
import { MatMenuTrigger, MatMenuModule } from '@angular/material/menu';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { Overlay, OverlayContainer, OverlayPositionBuilder, OverlayModule } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatDividerModule } from '@angular/material/divider';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatRippleModule } from '@angular/material/core';
// |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// |||||||||||||||||||||||||||||||||||||| Utils ||||||||||||||||||||||||||||||||||||||||||||||||||
/**
* check object is null or undefined
*/
function isNullorUndefined(value) {
if (value === null || value === undefined) {
return true;
}
else {
return false;
}
}
/**
* clone object but reference variable not change
*/
function clone(obj) {
if (obj === null || obj === undefined) {
return obj;
}
else if (Array.isArray(obj)) {
const array = [];
obj.forEach(item => array.push(Object.assign({}, item)));
return array;
}
else {
return Object.assign({}, obj);
}
}
/**
* clone object and all reference variable but may be there is a circle loop.
*/
function deepClone(obj) {
if (obj === null || obj === undefined) {
return obj;
}
else if (Array.isArray(obj)) {
const array = [];
obj.forEach(item => array.push(deepClone(item)));
return array;
}
else {
const c = Object.assign({}, obj);
const fields = Object.getOwnPropertyNames(obj);
fields.forEach(f => {
const field = obj[f];
if (field !== null && typeof field === 'object') {
c[f] = deepClone(field);
}
});
return c;
}
}
function getObjectProp(fieldName, defaultValue, ...variable) {
for (const v in variable) {
if (variable[v] && !isNullorUndefined(variable[v][fieldName])) {
return variable[v][fieldName];
}
}
return defaultValue;
}
function copy(from, to, forced = false, nullSkip = true, undefinedSkip = true) {
if (from === null || from === undefined) {
return;
}
if (to === null || to === undefined) {
to = {};
}
const f = Object.keys(from);
const t = Object.keys(to);
f.forEach(fi => {
if (forced === true || t.includes(fi) === true) {
if (!(from[fi] === null && nullSkip === true) && !(from[fi] === undefined && undefinedSkip === true)) {
to[fi] = from[fi];
}
}
});
}
/**
* Simplifies a string (trims and lowerCases)
*/
function simplify(s) {
return `${s}`.trim().toLowerCase();
}
/**
* Transforms a camelCase string into a readable text format
* @example textify('helloWorld!')
* // Hello world!
*/
function textify(text) {
return text
.replace(/([A-Z])/g, char => ` ${char.toLowerCase()}`)
.replace(/^([a-z])/, char => char.toUpperCase());
}
/**
* Transforms a text string into a title case text format
* @example titleCase('hello world!')
* // Hello World!
*/
function titleCase(value) {
const sentence = value.toLowerCase().split(' ');
for (let i = 0; i < sentence.length; i++) {
sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1);
}
return sentence.join(' ');
}
class TableVirtualScrollDataSource extends MatTableDataSource {
constructor() {
super(...arguments);
this.filterMap = {};
this.columns = [];
}
get allData() {
return this.data;
}
toTranslate() {
const tranList = [];
const keys = Object.keys(this.filterMap);
for (const k of keys) {
let fieldTotalTran = '';
for (const f of this.filterMap[k]) {
fieldTotalTran += f.toPrint();
}
if (fieldTotalTran !== '') {
tranList.push({ key: titleCase(k), value: fieldTotalTran });
}
}
return tranList;
}
getFilter(fieldName) {
return this.filterMap[fieldName];
}
setFilter(fieldName, filters) {
this.filterMap[fieldName] = filters;
return new Observable(subscriber => {
setTimeout(() => {
this.refreshFilterPredicate();
subscriber.next();
subscriber.complete();
}, 200); // for show progress
});
}
clearFilter(fieldName = null) {
if (fieldName != null) {
delete this.filterMap[fieldName];
}
else {
this.filterMap = {};
}
this.refreshFilterPredicate();
}
clearData() {
this.data = [];
}
refreshFilterPredicate() {
let conditionsString = '';
Object.keys(this.filterMap).forEach(key => {
let fieldCondition = '';
this.filterMap[key].forEach((fieldFilter, row, array) => {
if (row < array.length - 1) {
fieldCondition += fieldFilter.toString(key) + (fieldFilter.type === 'and' ? ' && ' : ' || ');
}
else {
fieldCondition += fieldFilter.toString(key);
}
});
if (fieldCondition !== '') {
conditionsString += ` ${conditionsString === '' ? '' : ' && '} ( ${fieldCondition} )`;
}
});
if (conditionsString !== '') {
const filterFunction = new Function('_a$', 'return ' + conditionsString);
this.filterPredicate = (data, filter) => filterFunction(data);
}
else {
this.filterPredicate = (data, filter) => true;
}
this.filter = conditionsString;
}
// When client paging active use for retrieve paging data
pagingData(data) {
const p = this._paginator;
if (p && p !== null) {
const end = (p.pageIndex + 1) * p.pageSize;
const start = p.pageIndex * p.pageSize;
return data.slice(start, end);
}
return data;
}
_updateChangeSubscription() {
var _a;
this.initStreams();
const sort = this._sort;
const paginator = this._paginator;
const internalPageChanges = this._internalPageChanges;
const filter = this._filter;
const renderData = this._renderData;
const dataStream = this._data;
const sortChange = sort ?
merge(sort.sortChange, sort.initialized) : of(null);
const pageChange = paginator ?
merge(paginator.page, internalPageChanges, paginator.initialized) : of(null);
// First Filter
const filteredData = combineLatest([dataStream, filter]).pipe(map(([data]) => this._filterData(data)));
// Second Order
const orderedData = combineLatest([filteredData, sortChange]).pipe(map(([data, sortColumn]) => {
const sc = sortColumn;
if (!sc) {
return data;
}
else if (sc.active !== '') {
const column = this.columns.filter(c => c.name == sc.active)[0];
if (column.sort === 'server-side') {
return data;
}
else if (column.sort === 'client-side') {
return this._orderData(data);
}
}
}));
// Last Paging
const paginatedData = combineLatest([orderedData, pageChange]).pipe(map(([data]) => this.pagingData(data)));
(_a = this._renderChangesSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
this._renderChangesSubscription = new Subscription();
this._renderChangesSubscription.add(paginatedData.subscribe(data => this.dataToRender$.next(data)));
this._renderChangesSubscription.add(this.dataOfRange$.subscribe(data => renderData.next(data)));
}
initStreams() {
if (!this.streamsReady) {
this.dataToRender$ = new ReplaySubject(1);
this.dataOfRange$ = new ReplaySubject(1);
this.streamsReady = true;
}
}
}
class TableService {
constructor() { }
/************************************* Local Export *****************************************/
static getFormattedTime() {
const today = new Date();
const y = today.getFullYear();
const m = today.getMonth() + 1;
const d = today.getDate();
const h = today.getHours();
const mi = today.getMinutes();
const s = today.getSeconds();
return y + "-" + m + "-" + d + "-" + h + "-" + mi + "-" + s;
}
// private downloadBlob(blob: any, filename: string) {
// if (navigator.msSaveBlob) { // IE 10+
// navigator.msSaveBlob(blob, filename);
// } else {
// const link = document.createElement('a');
// if (link.download !== undefined) {
// // Browsers that support HTML5 download attribute
// const link = window.document.createElement('a');
// const date = new Date();
// link.className = 'download' + date.getUTCFullYear() + date.getUTCMonth() + date.getUTCSeconds();
// link.setAttribute('href', blob);
// link.setAttribute('download', filename);
// link.style.visibility = 'hidden';
// link.click();
// // setTimeout(() => {
// // const g = document.body.getElementsByClassName(link.className);
// // document.body.removeChild(link);
// // });
// }
// }
// }
downloadBlob(blob, filename) {
if (navigator.msSaveBlob) {
// IE 10+
navigator.msSaveBlob(blob, filename);
}
else {
const link = document.createElement("a");
if (link.download !== undefined) {
// Browsers that support HTML5 download attribute
const url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
exportToCsv(columns, rows, selectionModel, filename = "") {
filename = filename === "" ? this.tableName + TableService.getFormattedTime() + ".csv" : filename;
if (!rows || !rows.length) {
return;
}
const fields = columns.filter((c) => c.exportable !== false && c.display !== 'hidden');
const separator = ",";
const CR_LF = "\n"; //'\u0D0A';
const keys = fields.map(f => f.name);
const headers = fields.map(f => f.header);
const csvContent = headers.join(separator) + CR_LF +
rows
.map((row) => {
return fields.map((f) => {
let cell = f.toExport(row, "csv") || "";
cell = cell instanceof Date ? cell.toLocaleString() : cell.toString().replace(/"/g, '""');
if (cell.search(/("|,|\n)/g) >= 0) {
cell = `"${cell}"`;
}
return cell;
}).join(separator);
}).join(CR_LF);
const blob = new Blob([
new Uint8Array([0xEF, 0xBB, 0xBF]),
csvContent
], { type: 'text/csv;charset=utf-8' });
this.downloadBlob(blob, filename);
}
exportToJson(rows, filename = "") {
filename =
filename === ""
? this.tableName + TableService.getFormattedTime() + ".json"
: filename;
const blob = new Blob([JSON.stringify(rows)], {
type: "text/csv;charset=utf-8;",
});
this.downloadBlob(blob, filename);
}
/************************************* Save Setting into storage *****************************************/
loadSavedColumnInfo(columnInfo, saveName) {
// Only load if a save name is passed in
if (saveName) {
if (!localStorage) {
return;
}
const loadedInfo = localStorage.getItem(`${saveName}-columns`);
if (loadedInfo) {
return JSON.parse(loadedInfo);
}
this.saveColumnInfo(columnInfo);
return columnInfo;
}
}
saveColumnInfo(columnInfo, saveName = this.tableName) {
if (saveName) {
if (!localStorage) {
return;
}
localStorage.setItem(`${saveName}-columns`, JSON.stringify(columnInfo));
}
}
}
/** @nocollapse */ TableService.ɵprov = i0.ɵɵdefineInjectable({ factory: function TableService_Factory() { return new TableService(); }, token: TableService, providedIn: "root" });
/** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
TableService.decorators = [
{ type: Injectable, args: [{
providedIn: "root",
},] }
];
/**
* @type {function(): !Array<(null|{
* type: ?,
* decorators: (undefined|!Array<{type: !Function, args: (undefined|!Array<?>)}>),
* })>}
* @nocollapse
*/
TableService.ctorParameters = () => [];
class TableSetting {
constructor() {
this.direction = 'ltr';
this.visibleActionMenu = null;
}
}
class TableCoreDirective {
constructor(tableService, cdr, config) {
this.tableService = tableService;
this.cdr = cdr;
this.config = config;
this.backgroundColor = null;
this.contextMenuItems = [];
this.expandColumn = [];
this.defaultWidth = null;
this.minWidth = 120;
/*************************************** I/O parameters *********************************/
this.printConfig = {};
this.rowHeight = 48;
this.headerHeight = 56;
this.footerHeight = 48;
this.headerEnable = true;
this.footerEnable = false;
// tslint:disable-next-line: no-output-on-prefix
this.onTableEvent = new EventEmitter();
this.onRowEvent = new EventEmitter();
this.settingChange = new EventEmitter();
this.paginationChange = new EventEmitter();
this.noData = true;
// Variables //
this.progressColumn = [];
this.displayedColumns = [];
this.displayedFooter = [];
this.tvsDataSource = new TableVirtualScrollDataSource([]);
this._rowSelectionModel = new SelectionModel(true, []);
this._tablePagination = {
pageIndex: 0,
pageSize: 10,
pageSizeOptions: [5, 10, 100, 1000, 10000]
};
this.tablePagingMode = "none";
this.viewportClass = "viewport-with-pagination";
this.showProgress = true;
this.tableSetting = {
direction: "ltr",
columnSetting: null,
visibleActionMenu: null,
};
if (this.config) {
this.tableSetting = Object.assign(Object.assign({}, this.tableSetting), this.config);
}
}
get direction() {
var _a;
return (_a = this.tableSetting) === null || _a === void 0 ? void 0 : _a.direction;
}
set direction(value) {
this.tableSetting.direction = value;
}
get ScrollStrategyType() {
return this.tableSetting.scrollStrategy;
}
set ScrollStrategyType(value) {
this.viewport["_scrollStrategy"].scrollStrategyMode = value;
this.tableSetting.scrollStrategy = value;
}
get pagingMode() {
return this.tablePagingMode;
}
set pagingMode(value) {
this.tablePagingMode = value;
this.updatePagination();
}
get pagination() {
return this._tablePagination;
}
set pagination(value) {
if (value && value !== null) {
this._tablePagination = value;
if (isNullorUndefined(this._tablePagination.pageSizeOptions)) {
this._tablePagination.pageSizeOptions = [5, 10, 25, 100];
}
if (isNullorUndefined(this._tablePagination.pageSize)) {
this._tablePagination.pageSize =
this._tablePagination.pageSizeOptions[0];
}
this.updatePagination();
}
}
get rowSelectionModel() {
return this._rowSelectionModel;
}
set rowSelectionModel(value) {
if (!isNullorUndefined(value)) {
if (this._rowSelectionMode &&
value &&
this._rowSelectionMode !== "none") {
this._rowSelectionMode =
value.isMultipleSelection() === true ? "multi" : "single";
}
this._rowSelectionModel = value;
}
}
get rowSelectionMode() {
return this._rowSelectionMode;
}
set rowSelectionMode(selection) {
var _a, _b;
selection = selection || "none";
const isSelectionColumn = selection === "single" || selection === "multi";
if (this._rowSelectionModel === null ||
(this._rowSelectionModel.isMultipleSelection() === true &&
selection === "single") ||
(this._rowSelectionModel.isMultipleSelection() === false &&
selection === "multi")) {
this._rowSelectionModel = new SelectionModel(selection === "multi", []);
}
if (((_a = this.displayedColumns) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
!isSelectionColumn &&
this.displayedColumns[0] === "row-checkbox") {
this.displayedColumns.shift();
}
else if (((_b = this.displayedColumns) === null || _b === void 0 ? void 0 : _b.length) > 0 &&
isSelectionColumn &&
this.displayedColumns[0] !== "row-checkbox") {
this.displayedColumns.unshift("row-checkbox");
}
this._rowSelectionMode = selection;
}
get tableName() {
return this.tableService.tableName;
}
set tableName(value) {
this.tableService.tableName = value;
}
get showProgress() {
return this.progressColumn.length > 0;
}
set showProgress(value) {
this.progressColumn = [];
if (value === true) {
this.progressColumn.push("progress");
}
}
initSystemField(data) {
if (data) {
data = data.map((item, index) => {
item.id = index;
item.option = item.option || {};
return item;
});
}
}
get expandComponent() {
return this._expandComponent;
}
set expandComponent(value) {
this._expandComponent = value;
if (this._expandComponent !== null && this._expandComponent !== undefined) {
this.expandColumn = ["expandedDetail"];
}
else {
this.expandColumn = [];
}
}
get columns() {
return this.tableColumns;
}
set columns(fields) {
(fields || []).forEach((f, i) => {
// key name error //
if (f.name.toLowerCase() === "id") {
throw 'Field name is reserved.["id"]';
}
const settingFields = (this.tableSetting.columnSetting || []).filter((s) => s.name === f.name);
const settingField = settingFields.length > 0 ? settingFields[0] : null;
/* default value for fields */
f.printable = f.printable || true;
f.exportable = f.exportable || true;
f.toExport =
f.toExport ||
((row, type) => (typeof row === "object" ? row[f.name] : ""));
f.toPrint = (row) => (typeof row === "object" ? row[f.name] : "");
f.enableContextMenu = f.enableContextMenu || true;
f.header = f.header || titleCase(f.name);
f.display = getObjectProp("display", "visible", settingField, f);
f.filter = getObjectProp("filter", "client-side", settingField, f);
f.sort = getObjectProp("sort", "client-side", settingField, f);
f.sticky = getObjectProp("sticky", "none", settingField, f);
f.width = getObjectProp("width", this.defaultWidth, settingField, f);
const unit = f.widthUnit || "px";
const style = unit === "px" ? f.width + "px" : `calc( ${f.widthPercentage}% )`;
if (f.width) {
f.style = Object.assign(Object.assign({}, f.style), { "max-width": style, "min-width": style });
}
});
this.tableColumns = fields;
this.updateColumn();
}
updateColumn() {
if (this.tableColumns) {
// isNullorUndefined(this.tableSetting.columnSetting)
this.tableSetting.columnSetting = clone(this.tableColumns);
}
this.setDisplayedColumns();
}
/**************************************** Methods **********************************************/
updatePagination() {
if (isNullorUndefined(this.tvsDataSource)) {
return;
}
if (this.tablePagingMode === "client-side" ||
this.tablePagingMode === "server-side") {
this.viewportClass = "viewport-with-pagination";
if (!isNullorUndefined(this.tvsDataSource.paginator)) {
let dataLen = this.tvsDataSource.paginator.length;
if (!isNullorUndefined(this._tablePagination.length) &&
this._tablePagination.length > dataLen) {
dataLen = this._tablePagination.length;
}
this.tvsDataSource.paginator.length = dataLen;
}
}
else {
this.viewportClass = "viewport";
if (this.tvsDataSource._paginator !== undefined) {
delete this.tvsDataSource._paginator;
}
}
this.tvsDataSource.refreshFilterPredicate();
}
clearSelection() {
if (this._rowSelectionModel) {
this._rowSelectionModel.clear();
}
}
clear() {
if (!isNullorUndefined(this.tvsDataSource)) {
if (this.viewport) {
this.viewport.scrollTo({ top: 0, behavior: "auto" });
}
this.tvsDataSource.clearData();
this.expandedElement = null;
}
this.clearSelection();
this.cdr.detectChanges();
}
setDisplayedColumns() {
if (this.columns) {
this.displayedColumns.splice(0, this.displayedColumns.length);
this.columns.forEach((column, index) => {
column.index = index;
if (column.display === undefined ||
column.display === "visible" ||
column.display === "prevent-hidden") {
this.displayedColumns.push(column.name);
}
});
if ((this._rowSelectionMode === "multi" ||
this._rowSelectionMode === "single") &&
this.displayedColumns.indexOf("row-checkbox") === -1) {
this.displayedColumns.unshift("row-checkbox");
}
this.displayedFooter = this.columns
.filter((item) => item.footer !== null && item.footer !== undefined)
.map((item) => item.name);
if (this.tableSetting.visibleTableMenu !== false) {
this.displayedColumns.push("table-menu");
}
}
}
/************************************ Drag & Drop Column *******************************************/
refreshGrid() {
this.cdr.detectChanges();
this.refreshColumn(this.tableColumns);
this.table.renderRows();
this.viewport.checkViewportSize();
}
moveRow(from, to) {
if (from >= 0 &&
from < this.tvsDataSource.data.length &&
to >= 0 &&
to < this.tvsDataSource.data.length) {
this.tvsDataSource.data[from].id = to;
this.tvsDataSource.data[to].id = from;
moveItemInArray(this.tvsDataSource.data, from, to);
this.tvsDataSource.data = Object.assign([], this.tvsDataSource.data);
}
}
moveColumn(from, to) {
setTimeout(() => {
moveItemInArray(this.columns, from, to);
this.refreshColumn(this.columns);
});
}
refreshColumn(columns) {
if (this.viewport) {
const currentOffset = this.viewport.measureScrollOffset();
this.columns = columns;
// this.setDisplayedColumns();
setTimeout(() => this.viewport.scrollTo({ top: currentOffset, behavior: "auto" }), 0);
}
}
/************************************ Selection Table Row *******************************************/
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this._rowSelectionModel.selected.length;
const numRows = this.tvsDataSource.filteredData.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
const isAllSelected = this.isAllSelected();
if (isAllSelected === false) {
this.tvsDataSource.filteredData.forEach((row) => this._rowSelectionModel.select(row));
}
else {
this._rowSelectionModel.clear();
}
this.onRowEvent.emit({
event: "MasterSelectionChange",
sender: { selectionModel: this._rowSelectionModel },
});
}
onRowSelectionChange(e, row) {
if (e) {
this._rowSelectionModel.toggle(row);
this.onRowEvent.emit({
event: "RowSelectionChange",
sender: {
selectionModel: this._rowSelectionModel,
row: row
},
});
}
}
}
/** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
TableCoreDirective.decorators = [
{ type: Directive, args: [{
// tslint:disable-next-line:directive-selector
selector: "[core]",
},] }
];
/**
* @type {function(): !Array<(null|{
* type: ?,
* decorators: (undefined|!Array<{type: !Function, args: (undefined|!Array<?>)}>),
* })>}
* @nocollapse
*/
TableCoreDirective.ctorParameters = () => [
{ type: TableService },
{ type: ChangeDetectorRef },
{ type: TableSetting }
];
/** @type {!Object<string, !Array<{type: !Function, args: (undefined|!Array<?>)}>>} */
TableCoreDirective.propDecorators = {
sort: [{ type: ViewChild, args: [MatSort, { static: true },] }],
paginator: [{ type: ViewChild, args: [MatPaginator, { static: true },] }],
dataSource: [{ type: Input }],
backgroundColor: [{ type: Input }],
direction: [{ type: Input }, { type: HostBinding, args: ["style.direction",] }],
contextMenuItems: [{ type: Input }],
ScrollStrategyType: [{ type: Input }],
pagingMode: [{ type: Input }],
pagination: [{ type: Input }],
rowSelectionModel: [{ type: Input }],
rowSelectionMode: [{ type: Input }],
tableName: [{ type: Input }],
showProgress: [{ type: Input }],
expandComponent: [{ type: Input }],
rowContextMenuItems: [{ type: Input }],
defaultWidth: [{ type: Input }],
minWidth: [{ type: Input }],
columns: [{ type: Input }],
printConfig: [{ type: Input }],
sticky: [{ type: Input }],
pending: [{ type: Input }],
rowHeight: [{ type: Input }],
headerHeight: [{ type: Input }],
footerHeight: [{ type: Input }],
headerEnable: [{ type: Input }],
footerEnable: [{ type: Input }],
showNoData: [{ type: Input }],
showReload: [{ type: Input }],
onTableEvent: [{ type: Output }],
onRowEvent: [{ type: Output }],
settingChange: [{ type: Output }],
paginationChange: [{ type: Output }],
table: [{ type: ViewChild, args: [MatTable, { static: true },] }],
viewport: [{ type: ViewChild, args: [CdkVirtualScrollViewport, { static: true },] }]
};
class AbstractFilter {
hasValue() {
if (this.parameters !== null) {
return this.parameters.filter(p => p.value != null && p.value !== undefined && p.value.toString() !== '').length > 0;
}
}
}
const contains = 'a.toString().includes(b)';
const equals$1 = 'a.toString() === b.toString()';
const startsWith = 'a.toString().startsWith(b)';
const endsWith = 'a.toString().endsWith(b.toString())';
const empty$1 = '!a';
const notEmpty$1 = '!!a';
const operations$1 = [contains, equals$1, startsWith, endsWith, empty$1, notEmpty$1];
class TextFilter extends AbstractFilter {
constructor(languagePack) {
super();
this.languagePack = languagePack;
// tslint:disable-next-line:variable-name
this._selectedIndex = null;
this._selectedIndex = 0;
if (TextFilter.operationList.length === 0) { // init for first time
operations$1.forEach(fn => {
TextFilter.operationList.push({ predicate: fn, text: null });
});
}
TextFilter.operationList[0].text = languagePack.filterLabels.TextContains; // contains //
TextFilter.operationList[1].text = languagePack.filterLabels.TextEquals; // equals //
TextFilter.operationList[2].text = languagePack.filterLabels.TextStartsWith; // startsWith //
TextFilter.operationList[3].text = languagePack.filterLabels.TextEndsWith; // endsWith //
TextFilter.operationList[4].text = languagePack.filterLabels.TextEmpty; // empty //
TextFilter.operationList[5].text = languagePack.filterLabels.TextNotEmpty; // notEmpty //
}
get selectedIndex() {
return this._selectedIndex;
}
set selectedIndex(value) {
this._selectedIndex = value;
// init filter parameters
if (value === 0 || value === 1 || value === 2 || value === 3) { // contains equals startsWith endsWith
this.parameters = [{ value: '', text: this.languagePack.filterLabels.Text }];
}
else { // empty notEmpty
this.parameters = null;
}
}
get selectedValue() {
if (this._selectedIndex !== null) {
return TextFilter.operationList[this._selectedIndex];
}
else {
return null;
}
}
getOperations() {
return TextFilter.operationList;
}
toString(dynamicVariable) {
const a = '_a$';
const b = '_b$';
const predicate = this.selectedValue.predicate.replace('a', a).replace('b', b);
const statement = predicate.replace(a, `${a}['${dynamicVariable}']?.toString()?.toLowerCase()`);
// one static parameters equals notEquals greaterThan lessThan //
if (this._selectedIndex === 0 ||
this._selectedIndex === 1 ||
this._selectedIndex === 2 ||
this._selectedIndex === 3) {
const value = '\'' + (this.parameters[0].value !== null ? this.parameters[0].value.toLowerCase() : ' null ') + '\'';
return statement.replace('_b$', value);
}
else { // without static parameters
return statement;
}
}
toPrint() {
return TextFilter.operationList[this._selectedIndex].text + ' ' + this.parameters[0].value + ' ' + (this.type || '') + ' ';
}
toSql() {
return TextFilter.sql[this._selectedIndex].replace('[*]', (this.parameters[0].value || '')) + (this.type || '') + ' ';
}
}
TextFilter.sql = ['LIKE "%[*]%"', '= "[*]"', 'LIKE "%[*]"', 'LIKE "[*]%"', 'IS NULL', 'IS NOT NULL'];
TextFilter.operationList = [];
const equals = 'a === b';
const notEquals = 'a !== b';
const greaterThan = 'a > b';
const lessThan = 'a < b';
const empty = '!a';
const notEmpty = '!!a';
const operations = [equals, notEquals, greaterThan, lessThan, empty, notEmpty];
class NumberFilter extends AbstractFilter {
// private languageText: LanguagePack;
constructor(languagePack) {
super();
this.languagePack = languagePack;
// tslint:disable-next-line:variable-name
this._selectedIndex = null;
if (NumberFilter.operationList.length === 0) {
operations.forEach(fn => {
NumberFilter.operationList.push({ predicate: fn, text: null });
});
}
NumberFilter.operationList[0].text = languagePack.filterLabels.NumberEquals; // equals //
NumberFilter.operationList[1].text = languagePack.filterLabels.NumberNotEquals; // notEquals //
NumberFilter.operationList[2].text = languagePack.filterLabels.NumberGreaterThan; // greaterThan //
NumberFilter.operationList[3].text = languagePack.filterLabels.NumberLessThan; // lessThan //
NumberFilter.operationList[4].text = languagePack.filterLabels.NumberEmpty; // empty //
NumberFilter.operationList[5].text = languagePack.filterLabels.NumberNotEmpty; // notEmpty //
}
get selectedIndex() {
return this._selectedIndex;
}
set selectedIndex(value) {
this._selectedIndex = value;
// init filter parameters
if (value === 0 || value === 1 || value === 2 || value === 3) { // equals notEquals greaterThan lessThan
this.parameters = [{ value: null, text: this.languagePack.filterLabels.Number }];
}
else { // empty notEmpty
this.parameters = null;
}
}
get selectedValue() {
if (this._selectedIndex !== null) {
return NumberFilter.operationList[this._selectedIndex];
}
else {
return null;
}
}
getOperations() {
return NumberFilter.operationList;
}
toString(dynamicVariable) {
const a = '_a$';
const b = '_b$';
const predicate = this.selectedValue.predicate.replace('a', a).replace('b', b);
const statement = predicate.replace(a, `${a}['${dynamicVariable}']`);
// one static variable (equals, notEquals,greaterThan,lessThan)
if (this._selectedIndex === 0 ||
this._selectedIndex === 1 ||
this._selectedIndex === 2 ||
this._selectedIndex === 3) {
const value = this.parameters[0].value ? this.parameters[0].value.toString() : ' null ';
return statement.replace(b, value);
}
else { // none static variable (empty, notEmpty)
return statement;
}
}
toPrint() {
return NumberFilter.operationList[this._selectedIndex].text + ' ' + this.parameters[0].value + ' ' + (this.type || '') + ' ';
}
toSql() {
return NumberFilter.sql[this._selectedIndex] + ' ' + (this.parameters[0].value || '') + ' ' + (this.type || '') + ' ';
}
}
NumberFilter.sql = ['=', '<>', '>', '<', 'IS NULL', 'IS NOT NULL'];
NumberFilter.operationList = [];
class TableIntl {
constructor() {
this.menuLabels = {
saveData: 'Save Data',
newSetting: 'New Setting',
defaultSetting: 'Default Setting',
noSetting: 'No Setting',
fullScreen: 'Full Screen',
columnSetting: 'Column Setting',
saveTableSetting: 'Save Table Setting',
clearFilter: 'Clear Filter',
jsonFile: 'Json File',
csvFile: 'CSV File',
printTable: 'Print Table',
filterMode: 'Filter Mode:',
filterLocalMode: 'Local',
filterServerMode: 'Server',
sortMode: 'Sort Mode:',
sortLocalMode: 'Local',
sortServerMode: 'Server',
printMode: 'Print Mode',
printYesMode: 'Yes',
printNoMode: 'No',
pinMode: 'Pin Mode:',
pinNoneMode: 'None',
pinStartMode: 'Start',
pinEndMode: 'End',
thereIsNoColumn: 'There is no column.'
};
this.paginatorLabels = {
changes: new Subject(),
itemsPerPageLabel: 'Items per page:',
nextPageLabel: 'Next Page:',
previousPageLabel: 'Previous Page:',
firstPageLabel: 'First Page:',
lastPageLabel: 'Last Page:',
getRangeLabel: (page, pageSize, length) => {
if (length === 0 || pageSize === 0) {
return `0 of ${length}`;
}
length = Math.max(length, 0);
const startIndex = page * pageSize;
const endIndex = startIndex < length ?
Math.min(startIndex + pageSize, length) :
startIndex + pageSize;
return `${startIndex + 1} - ${endIndex} of ${length}`;
}
};
this.tableLabels = {
NoData: 'No records found.'
};
this.filterLabels = {
Clear: 'Clear',
Search: 'Search',
And: 'And',
Or: 'Or',
/* Text Compare */
Text: 'Text',
TextContains: 'Contains',
TextEmpty: 'Empty',
TextStartsWith: 'Starts With',
TextEndsWith: 'Ends With',
TextEquals: 'Equals',
TextNotEmpty: 'Not Empty',
/* Number Compare */
Number: 'Number',
NumberEquals: 'Equals',
NumberNotEquals: 'Not Equals',
NumberGreaterThan: 'Greater Than',
NumberLessThan: 'Less Than',
NumberEmpty: 'Empty',
NumberNotEmpty: 'Not Empty',
/* Category List Compare */
CategoryContains: 'Contains',
CategoryNotContains: 'Not Contains',
/* Boolean Compare */
/* Date Compare */
};
}
}
/** @nocollapse */ TableIntl.ɵprov = i0.ɵɵdefineInjectable({ factory: function TableIntl_Factory() { return new TableIntl(); }, token: TableIntl, providedIn: "root" });
/** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
TableIntl.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
const listAnimation = trigger('listAnimation', [
transition('* <=> *', [
query(':enter', [style({ opacity: 0 }), stagger('10ms', animate('400ms ease-out', style({ opacity: 1 })))], { optional: true }),
])
]);
class HeaderFilterComponent {
constructor(languagePack, service, cdr) {
this.languagePack = languagePack;
this.service = service;
this.cdr = cdr;
this.filterChanged = new EventEmitter();
this.filterList = [];
}
get filters() {
if (isNullorUndefined(this.filterList) === true || this.filterList.length === 0) {
this.filterList = [];
this.addNewFilter(this.field.type || 'text');
}
return this.filterList;
}
set filters(values) {
this.filterList = values;
}
get hasValue() {
return this.filterList && this.filterList.filter(f => f.hasValue() === true).length > 0;
}
get showTrigger() {
if (this.menu === undefined) {
return false;
}
else {
return this.menu.menuOpen || this.hasValue;
}
}
ngOnDestroy() {
if (this.eventsSubscription) {
this.eventsSubscription.unsubscribe();
}
}
ngOnInit() {
if (isNullorUndefined(this.filters)) {
this.filters = [];
this.addNewFilter(this.field.type);
}
}
addNewFilter(type = 'text') {
switch (type || 'text') {
case 'text': {
this.filterList.push(new TextFilter(this.languagePack));
break;
}
case 'number': {
this.filterList.push(new NumberFilter(this.languagePack));
break;
}
case 'date': {
// this.compare = new DateCompare(service);
break;
}
case 'boolean': {
// this.compare = new BooleanCompare(service);
break;
}
default: this.filterList.push(new TextFilter(this.languagePack));
}
this.filters[this.filters.length - 1].selectedIndex = 0;
return this.filters[this.filters.length - 1];
}
ngAfterViewInit() {
if (this.menu) {
this.eventsSubscription = this.menu.menuOpened.subscribe(() => this.focusToLastInput());
}
}
focusToLastInput() {
setTimeout(() => {
if (this.filterInputList.length > 0) {
this.filterInputList.last.focus();
}
});
}
filterAction_OnClick(index, action) {
if (action === 0 || action === 1) { // and or
this.filters[index].type = action === 0 ? 'and' : 'or';
if (this.filters.length === index + 1) {
this.addNewFilter(this.field.type);
this.focusToLastInput();
}
}
else if (action === 2 && this.filters.length > 1) { // delete
setTimeout(() => {
this.filters.splice(index, 1);
this.cdr.detectChanges();
this.focusToLastInput();
}); // bug for delete filter item(unwanted reaction close menu)
}
}
clearColumn_OnClick() {
this.filterList = [];
this.filterChanged.emit(this.filterList);
}
applyFilter_OnClick() {
this.filterChanged.emit(this.filterList);
}
}
/** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
HeaderFilterComponent.decorators = [
{ type: Component, args: [{
// tslint:disable-next-line:component-selector
selector: 'header-filter',
template: "<ng-content></ng-content>\r\n\r\n<mat-menu filter-event #filterMenu=\"matMenu\" class=\"menu\">\r\n <ng-template matMenuContent>\r\n\r\n <div filter-event class=\"menu-title\">\r\n {{field?.header}}\r\n </div>\r\n <div [@listAnimation]=\"filters.length\" filter-event *ngFor=\"let filter of filters; let index = index\"\r\n class=\"filter-panel\">\r\n\r\n <mat-form-field>\r\n <mat-select [value]=\"filter.selectedIndex\" [panelClass]=\"'mat-elevation-z10'\"\r\n (selectionChange)=\"filter.selectedIndex = $event.value;\" placeholder='Conditions'\r\n (keyup.enter)=\"applyFilter_OnClick()\">\r\n <mat-option *ngFor=\"let op of filter.getOperations(); let selectedIndex=index\" [value]=\"selectedIndex\">\r\n {{ op.text }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <div *ngFor=\"let ctrl of filter?.parameters\">\r\n <mat-form-field class=\"input-field\">\r\n <mat-label>{{ctrl.text}}</mat-label>\r\n <input matInput #filterInput=\"matInput\" [(ngModel)]=\"ctrl.value\" [placeholder]=\"\"\r\n (keyup.enter)=\"applyFilter_OnClick()\" autocomplete=\"off\" />\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"or-and\">\r\n <span *ngIf=\"filters?.length !== index+1\" class=\"selected-filter-type\">{{ filter?.type === 'and' ?\r\n languagePack.filterLabels.And : languagePack.filterLabels.Or}}</span>\r\n <span class=\"svg\">\r\n <mat-icon (click)=\"filterAction_OnClick(index,0)\">add</mat-icon>\r\n </span>\r\n <span class=\"svg\">\r\n <mat-icon (click)=\"filterAction_OnClick(index,1)\" style=\"transform: rotate(90deg);\">drag_handle</mat-icon>\r\n </span>\r\n <span class=\"svg\">\r\n <mat-icon (click)=\"filterAction_OnClick(index,2)\">clear</mat-icon>\r\n </span>\r\n </div>\r\n\r\n </div>\r\n\r\n <div filter-event class=\"menu-action\">\r\n <button mat-raised-button type=\"button\" (click)=\"clearColumn_OnClick()\">{{ languagePack.filterLabels.Clear\r\n }}</button>\r\n <button mat-raised-button type=\"button\" color=\"primary\" (click)=\"applyFilter_OnClick()\">{{\r\n languagePack.filterLabels.Search}}</button>\r\n </div>\r\n </ng-template>\r\n</mat-menu>\r\n\r\n<span class=\"trigger\" [matMenuTriggerFor]=\"filterMenu\" *ngIf=\"field.filter !== 'none'\">\r\n <mat-icon>filter_list</mat-icon>\r\n</span>\r\n",
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [listAnimation],
styles: ["@media print{.print-preview{background-color:#fff;position:fixed;width:100%;height:auto;z-index:99999999;margin:0;padding:0;top:0;left:0;overflow:visible;display:block}}.disable-backdrop-click .cdk-overlay-backdrop.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{pointer-events:none}:host{display:flex;align-items:center;width:100%;align-self:stretch}.trigger{color:#0000004d;display:flex;opacity:0;transform:translateY(-5px);cursor:pointer;transition-duration:.4s;transition-property:opacity,transform;position:sticky;right:0px;z-index:1;padding-left:0 8px}:host.has-value .trigger{opacity:1;color:#0000008a}:host:hover .trigger,:host.show-trigger .trigger{opacity:1;transform:translateY(-1px)}::ng-deep .mat-menu-content:not(:empty){padding:0!important}.mat-menu-item-highlighted:not([disabled]),.mat-menu-item.cdk-keyboard-focused:not([disabled]),.mat-menu-item.cdk-program-focused:not([disabled]),.mat-menu-item:hover:not([disabled]){background-color:inherit}.input-field{margin-top:-15px}.menu-title{font-weight:bolder;top:-8px;position:sticky;background-color:#fff;z-index:1}.menu-action{position:sticky;bottom:-8px;padding-top:10px;padding-bottom:0;background-color:#fff}.menu-action button{width:calc(50% - 10px);margin:5px;border-radius:10px}.filter-panel{border-radius:5px;background-color:#fdfbfb;border:solid 1px #efefef;transition:all .5s;padding:5px;overflow:hidden;font-size:14px;margin-top:10px;display:flex;flex-direction:column}.filter-panel:nth-child(2){margin-top:0!important}.filter-panel:hover{border:solid 1px #d1d1d1}.filter-panel:hover .svg{opacity:1;transform:translateY(-1px)}.or-and{display:inherit!important;text-align:right;margin:-12px 0;height:35px;cursor:inherit;font-size:12px}.svg{color:#0000004d;display:flex;opacity:0;transform:translateY(-5px);transition-duration:.4s;transition-property:opacity,transform;margin-left:5px;padding:2px;border-radius:5px;color:#4c4c4c;cursor:pointer;display:inline-block!important;height:24px}.svg mat-icon{margin:0;vertical-align:top;border-radius:5px}.svg mat-icon:hover{color:#fff;background-color:#89898a}.svg:hover{background-color:#f8f8f8}.selected-filter-type{float:left;color:#fff;background-color:#898