@bhplugin/ng-datatable
Version:
ng-datatable - fully customizable & easy to use datatable library
877 lines (858 loc) • 118 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, Input, EventEmitter, Component, Output, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, ContentChildren, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i2 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import * as i1$1 from '@angular/platform-browser';
class colDef {
}
class Pager {
}
class SlotDirective {
constructor(templateRef) {
this.templateRef = templateRef;
}
}
SlotDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SlotDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
SlotDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: SlotDirective, selector: "[slot]", inputs: { fieldName: ["slot", "fieldName"], value: ["slotValue", "value"] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SlotDirective, decorators: [{
type: Directive,
args: [{
selector: '[slot]',
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { fieldName: [{
type: Input,
args: ['slot']
}], value: [{
type: Input,
args: ['slotValue']
}] } });
class ColumnFilterComponent {
constructor() {
this.close = new EventEmitter();
this.filterChange = new EventEmitter();
}
ngOnInit() {
document.addEventListener('click', this.closeMethod.bind(this));
}
ngOnDestroy() {
document.removeEventListener('click', this.closeMethod.bind(this));
}
closeMethod() {
this.close.emit();
}
select(condition) {
this.column.condition = condition;
if (condition === '') {
this.column.value = '';
}
this.filterChange.emit(this.column);
}
}
ColumnFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ColumnFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
ColumnFilterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: ColumnFilterComponent, selector: "column-filter", inputs: { column: "column" }, outputs: { close: "close", filterChange: "filterChange" }, ngImport: i0, template: `
<div class="bh-filter-menu bh-absolute bh-z-[1] bh-bg-white bh-shadow-md bh-rounded-md bh-top-full bh-right-0 bh-w-32 bh-mt-1">
<div class="bh-text-[13px] bh-font-normal bh-rounded bh-overflow-hidden" (click)="closeMethod(); $event.stopPropagation()">
<button type="button" [class.active]="column.condition === ''" (click)="select('')">No filter</button>
<ng-container *ngIf="column.type === 'string'">
<button type="button" [class.active]="column.condition === 'contain'" (click)="select('contain')">Contain</button>
<button type="button" [class.active]="column.condition === 'not_contain'" (click)="select('not_contain')">Not contain</button>
<button type="button" [class.active]="column.condition === 'equal'" (click)="select('equal')">Equal</button>
<button type="button" [class.active]="column.condition === 'not_equal'" (click)="select('not_equal')">Not equal</button>
<button type="button" [class.active]="column.condition === 'start_with'" (click)="select('start_with')">Starts with</button>
<button type="button" [class.active]="column.condition === 'end_with'" (click)="select('end_with')">Ends with</button>
</ng-container>
<ng-container *ngIf="column.type === 'number'">
<button type="button" [class.active]="column.condition === 'equal'" (click)="select('equal')">Equal</button>
<button type="button" [class.active]="column.condition === 'not_equal'" (click)="select('not_equal')">Not Equal</button>
<button type="button" [class.active]="column.condition === 'greater_than'" (click)="select('greater_than')">Greater than</button>
<button type="button" [class.active]="column.condition === 'greater_than_equal'" (click)="select('greater_than_equal')">Greater than or equal</button>
<button type="button" [class.active]="column.condition === 'less_than'" (click)="select('less_than')">Less than</button>
<button type="button" [class.active]="column.condition === 'less_than_equal'" (click)="select('less_than_equal')">Less than or equal</button>
</ng-container>
<ng-container *ngIf="column.type === 'date'">
<button type="button" [ngClass]="{ active: column.condition === 'equal' }" (click)="select('equal')">Equal</button>
<button type="button" [ngClass]="{ active: column.condition === 'not_equal' }" (click)="select('not_equal')">Not equal</button>
<button type="button" [ngClass]="{ active: column.condition === 'greater_than' }" (click)="select('greater_than')">Greater than</button>
<button type="button" [ngClass]="{ active: column.condition === 'less_than' }" (click)="select('less_than')">Less than</button>
</ng-container>
<button type="button" [ngClass]="{ active: column.condition === 'is_null' }" (click)="select('is_null')">Is null</button>
<button type="button" [ngClass]="{ active: column.condition === 'is_not_null' }" (click)="select('is_not_null')">Not null</button>
</div>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ColumnFilterComponent, decorators: [{
type: Component,
args: [{
selector: 'column-filter',
template: `
<div class="bh-filter-menu bh-absolute bh-z-[1] bh-bg-white bh-shadow-md bh-rounded-md bh-top-full bh-right-0 bh-w-32 bh-mt-1">
<div class="bh-text-[13px] bh-font-normal bh-rounded bh-overflow-hidden" (click)="closeMethod(); $event.stopPropagation()">
<button type="button" [class.active]="column.condition === ''" (click)="select('')">No filter</button>
<ng-container *ngIf="column.type === 'string'">
<button type="button" [class.active]="column.condition === 'contain'" (click)="select('contain')">Contain</button>
<button type="button" [class.active]="column.condition === 'not_contain'" (click)="select('not_contain')">Not contain</button>
<button type="button" [class.active]="column.condition === 'equal'" (click)="select('equal')">Equal</button>
<button type="button" [class.active]="column.condition === 'not_equal'" (click)="select('not_equal')">Not equal</button>
<button type="button" [class.active]="column.condition === 'start_with'" (click)="select('start_with')">Starts with</button>
<button type="button" [class.active]="column.condition === 'end_with'" (click)="select('end_with')">Ends with</button>
</ng-container>
<ng-container *ngIf="column.type === 'number'">
<button type="button" [class.active]="column.condition === 'equal'" (click)="select('equal')">Equal</button>
<button type="button" [class.active]="column.condition === 'not_equal'" (click)="select('not_equal')">Not Equal</button>
<button type="button" [class.active]="column.condition === 'greater_than'" (click)="select('greater_than')">Greater than</button>
<button type="button" [class.active]="column.condition === 'greater_than_equal'" (click)="select('greater_than_equal')">Greater than or equal</button>
<button type="button" [class.active]="column.condition === 'less_than'" (click)="select('less_than')">Less than</button>
<button type="button" [class.active]="column.condition === 'less_than_equal'" (click)="select('less_than_equal')">Less than or equal</button>
</ng-container>
<ng-container *ngIf="column.type === 'date'">
<button type="button" [ngClass]="{ active: column.condition === 'equal' }" (click)="select('equal')">Equal</button>
<button type="button" [ngClass]="{ active: column.condition === 'not_equal' }" (click)="select('not_equal')">Not equal</button>
<button type="button" [ngClass]="{ active: column.condition === 'greater_than' }" (click)="select('greater_than')">Greater than</button>
<button type="button" [ngClass]="{ active: column.condition === 'less_than' }" (click)="select('less_than')">Less than</button>
</ng-container>
<button type="button" [ngClass]="{ active: column.condition === 'is_null' }" (click)="select('is_null')">Is null</button>
<button type="button" [ngClass]="{ active: column.condition === 'is_not_null' }" (click)="select('is_not_null')">Not null</button>
</div>
</div>
`,
}]
}], ctorParameters: function () { return []; }, propDecorators: { column: [{
type: Input
}], close: [{
type: Output,
args: ['close']
}], filterChange: [{
type: Output,
args: ['filterChange']
}] } });
class IconCheckComponent {
constructor(viewContainerRef) {
this.viewContainerRef = viewContainerRef;
}
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.template);
}
}
IconCheckComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconCheckComponent, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
IconCheckComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: IconCheckComponent, selector: "icon-check", inputs: { class: "class" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true, static: true }], ngImport: i0, template: `
<ng-template #template>
<svg [ngClass]="class" version="1.1" viewBox="0 0 17 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-9 -11)" fill="currentColor" fill-rule="nonzero">
<path
d="m25.576 11.414c0.56558 0.55188 0.56558 1.4439 0 1.9961l-9.404 9.176c-0.28213 0.27529-0.65247 0.41385-1.0228 0.41385-0.37034 0-0.74068-0.13855-1.0228-0.41385l-4.7019-4.588c-0.56584-0.55188-0.56584-1.4442 0-1.9961 0.56558-0.55214 1.4798-0.55214 2.0456 0l3.679 3.5899 8.3812-8.1779c0.56558-0.55214 1.4798-0.55214 2.0456 0z"
/>
</g>
</g>
</svg>
</ng-template>
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconCheckComponent, decorators: [{
type: Component,
args: [{
selector: 'icon-check',
template: `
<ng-template #template>
<svg [ngClass]="class" version="1.1" viewBox="0 0 17 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-9 -11)" fill="currentColor" fill-rule="nonzero">
<path
d="m25.576 11.414c0.56558 0.55188 0.56558 1.4439 0 1.9961l-9.404 9.176c-0.28213 0.27529-0.65247 0.41385-1.0228 0.41385-0.37034 0-0.74068-0.13855-1.0228-0.41385l-4.7019-4.588c-0.56584-0.55188-0.56584-1.4442 0-1.9961 0.56558-0.55214 1.4798-0.55214 2.0456 0l3.679 3.5899 8.3812-8.1779c0.56558-0.55214 1.4798-0.55214 2.0456 0z"
/>
</g>
</g>
</svg>
</ng-template>
`,
}]
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }]; }, propDecorators: { template: [{
type: ViewChild,
args: ['template', { static: true }]
}], class: [{
type: Input
}] } });
class IconDashComponent {
constructor(viewContainerRef) {
this.viewContainerRef = viewContainerRef;
}
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.template);
}
}
IconDashComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconDashComponent, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
IconDashComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: IconDashComponent, selector: "icon-dash", inputs: { class: "class" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true, static: true }], ngImport: i0, template: `
<ng-template #template>
<svg [ngClass]="class" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</ng-template>
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconDashComponent, decorators: [{
type: Component,
args: [{
selector: 'icon-dash',
template: `
<ng-template #template>
<svg [ngClass]="class" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</ng-template>
`,
}]
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }]; }, propDecorators: { template: [{
type: ViewChild,
args: ['template', { static: true }]
}], class: [{
type: Input
}] } });
class IconFilterComponent {
constructor(viewContainerRef) {
this.viewContainerRef = viewContainerRef;
}
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.template);
}
}
IconFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconFilterComponent, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
IconFilterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: IconFilterComponent, selector: "icon-filter", inputs: { class: "class" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true, static: true }], ngImport: i0, template: `
<ng-template #template>
<svg [ngClass]="class" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon>
</svg>
</ng-template>
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconFilterComponent, decorators: [{
type: Component,
args: [{
selector: 'icon-filter',
template: `
<ng-template #template>
<svg [ngClass]="class" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon>
</svg>
</ng-template>
`,
}]
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }]; }, propDecorators: { template: [{
type: ViewChild,
args: ['template', { static: true }]
}], class: [{
type: Input
}] } });
class ColumnHeaderComponent {
constructor(viewContainerRef) {
this.viewContainerRef = viewContainerRef;
this.selectAll = new EventEmitter();
this.sortChange = new EventEmitter();
this.filterChange = new EventEmitter();
this.isOpenFilter = null;
}
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.template);
this.checkboxChange();
}
checkboxChange() {
if (this.selectedAll) {
this.selectedAll.nativeElement.indeterminate = this.checkAll !== 0 ? !this.checkAll : false;
this.selectedAll.nativeElement.checked = this.checkAll;
}
}
toggleFilterMenu(col) {
if (col) {
if (this.isOpenFilter === col.field) {
this.isOpenFilter = null;
}
else {
this.isOpenFilter = col.field;
}
}
else {
this.isOpenFilter = null;
}
}
}
ColumnHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ColumnHeaderComponent, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
ColumnHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: ColumnHeaderComponent, selector: "column-header", inputs: { all: "all", isFooter: "isFooter", checkAll: "checkAll" }, outputs: { selectAll: "selectAll", sortChange: "sortChange", filterChange: "filterChange" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true, static: true }, { propertyName: "selectedAll", first: true, predicate: ["selectedAll"], descendants: true }], ngImport: i0, template: `
<ng-template #template>
<tr>
<th
*ngIf="all.hasCheckbox"
[class]="'bh-w-px'"
[ngClass]="{
'bh-sticky bh-bg-blue-light bh-z-[1]': all.stickyHeader || all.stickyFirstColumn,
'bh-top-0': all.stickyHeader,
'bh-left-0': all.stickyFirstColumn
}"
>
<div class="bh-checkbox">
<input #selectedAll type="checkbox" (click)="selectAll.emit(selectedAll.checked); $event.stopPropagation()" />
<div>
<icon-check class="check"></icon-check>
<icon-dash class="intermediate"></icon-dash>
</div>
</div>
</th>
<ng-container *ngFor="let col of all.columns; let j = index">
<th
*ngIf="!col.hide"
[class]="'bh-select-none bh-z-[1]'"
[ngClass]="[
all.sortable && col.sort ? 'bh-cursor-pointer' : '',
j === 0 && all.stickyFirstColumn ? 'bh-sticky bh-left-0 bh-bg-blue-light' : '',
all.hasCheckbox && j === 0 && all.stickyFirstColumn ? 'bh-left-[52px]' : ''
]"
[style]="{ width: col.width, 'min-width': col.minWidth, 'max-width': col.maxWidth }"
>
<div class="bh-flex bh-items-center" [ngClass]="[col.headerClass ? col.headerClass : '']" (click)="all.sortable && col.sort && sortChange.emit(col.field)">
{{ col.title }}
<span *ngIf="all.sortable && col.sort" class="bh-ml-3 bh-sort bh-flex bh-items-center" [ngClass]="[all.sortColumn, all.sortDirection]">
<svg width="16" height="16" viewBox="0 0 14 14" fill="none">
<polygon
points="3.11,6.25 10.89,6.25 7,1.75"
fill="currentColor"
class="bh-text-black/20"
[ngClass]="[all.sortColumn === col.field && all.sortDirection === 'asc' ? '!bh-text-primary' : '']"
></polygon>
<polygon
points="7,12.25 10.89,7.75 3.11,7.75"
fill="currentColor"
class="bh-text-black/20"
[ngClass]="[all.sortColumn === col.field && all.sortDirection === 'desc' ? '!bh-text-primary' : '']"
></polygon>
</svg>
</span>
</div>
<ng-container *ngIf="all.columnFilter && !isFooter">
<div *ngIf="col.filter" class="bh-filter bh-relative">
<input *ngIf="col.type === 'string'" [(ngModel)]="col.value" type="text" class="bh-form-control" (keyup)="filterChange.emit()" />
<input *ngIf="col.type === 'number'" [(ngModel)]="col.value" type="number" class="bh-form-control" (keyup)="filterChange.emit()" />
<input *ngIf="col.type === 'date'" [(ngModel)]="col.value" type="date" class="bh-form-control" (change)="filterChange.emit()" />
<select *ngIf="col.type === 'bool'" [(ngModel)]="col.value" class="bh-form-control" (change)="filterChange.emit()" (click)="isOpenFilter = null">
<option [ngValue]="undefined">All</option>
<option [ngValue]="true">True</option>
<option [ngValue]="false">False</option>
</select>
<button *ngIf="col.type !== 'bool'" type="button" (click)="toggleFilterMenu(col); $event.stopPropagation()">
<icon-filter class="bh-w-4"></icon-filter>
</button>
<column-filter
[ngClass]="{ 'bh-hidden': isOpenFilter !== col.field }"
[column]="col"
(close)="toggleFilterMenu()"
(filterChange)="filterChange.emit()"
></column-filter>
</div>
</ng-container>
</th>
</ng-container>
</tr>
</ng-template>
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ColumnFilterComponent, selector: "column-filter", inputs: ["column"], outputs: ["close", "filterChange"] }, { kind: "component", type: IconCheckComponent, selector: "icon-check", inputs: ["class"] }, { kind: "component", type: IconDashComponent, selector: "icon-dash", inputs: ["class"] }, { kind: "component", type: IconFilterComponent, selector: "icon-filter", inputs: ["class"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ColumnHeaderComponent, decorators: [{
type: Component,
args: [{
selector: 'column-header',
template: `
<ng-template #template>
<tr>
<th
*ngIf="all.hasCheckbox"
[class]="'bh-w-px'"
[ngClass]="{
'bh-sticky bh-bg-blue-light bh-z-[1]': all.stickyHeader || all.stickyFirstColumn,
'bh-top-0': all.stickyHeader,
'bh-left-0': all.stickyFirstColumn
}"
>
<div class="bh-checkbox">
<input #selectedAll type="checkbox" (click)="selectAll.emit(selectedAll.checked); $event.stopPropagation()" />
<div>
<icon-check class="check"></icon-check>
<icon-dash class="intermediate"></icon-dash>
</div>
</div>
</th>
<ng-container *ngFor="let col of all.columns; let j = index">
<th
*ngIf="!col.hide"
[class]="'bh-select-none bh-z-[1]'"
[ngClass]="[
all.sortable && col.sort ? 'bh-cursor-pointer' : '',
j === 0 && all.stickyFirstColumn ? 'bh-sticky bh-left-0 bh-bg-blue-light' : '',
all.hasCheckbox && j === 0 && all.stickyFirstColumn ? 'bh-left-[52px]' : ''
]"
[style]="{ width: col.width, 'min-width': col.minWidth, 'max-width': col.maxWidth }"
>
<div class="bh-flex bh-items-center" [ngClass]="[col.headerClass ? col.headerClass : '']" (click)="all.sortable && col.sort && sortChange.emit(col.field)">
{{ col.title }}
<span *ngIf="all.sortable && col.sort" class="bh-ml-3 bh-sort bh-flex bh-items-center" [ngClass]="[all.sortColumn, all.sortDirection]">
<svg width="16" height="16" viewBox="0 0 14 14" fill="none">
<polygon
points="3.11,6.25 10.89,6.25 7,1.75"
fill="currentColor"
class="bh-text-black/20"
[ngClass]="[all.sortColumn === col.field && all.sortDirection === 'asc' ? '!bh-text-primary' : '']"
></polygon>
<polygon
points="7,12.25 10.89,7.75 3.11,7.75"
fill="currentColor"
class="bh-text-black/20"
[ngClass]="[all.sortColumn === col.field && all.sortDirection === 'desc' ? '!bh-text-primary' : '']"
></polygon>
</svg>
</span>
</div>
<ng-container *ngIf="all.columnFilter && !isFooter">
<div *ngIf="col.filter" class="bh-filter bh-relative">
<input *ngIf="col.type === 'string'" [(ngModel)]="col.value" type="text" class="bh-form-control" (keyup)="filterChange.emit()" />
<input *ngIf="col.type === 'number'" [(ngModel)]="col.value" type="number" class="bh-form-control" (keyup)="filterChange.emit()" />
<input *ngIf="col.type === 'date'" [(ngModel)]="col.value" type="date" class="bh-form-control" (change)="filterChange.emit()" />
<select *ngIf="col.type === 'bool'" [(ngModel)]="col.value" class="bh-form-control" (change)="filterChange.emit()" (click)="isOpenFilter = null">
<option [ngValue]="undefined">All</option>
<option [ngValue]="true">True</option>
<option [ngValue]="false">False</option>
</select>
<button *ngIf="col.type !== 'bool'" type="button" (click)="toggleFilterMenu(col); $event.stopPropagation()">
<icon-filter class="bh-w-4"></icon-filter>
</button>
<column-filter
[ngClass]="{ 'bh-hidden': isOpenFilter !== col.field }"
[column]="col"
(close)="toggleFilterMenu()"
(filterChange)="filterChange.emit()"
></column-filter>
</div>
</ng-container>
</th>
</ng-container>
</tr>
</ng-template>
`,
}]
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }]; }, propDecorators: { template: [{
type: ViewChild,
args: ['template', { static: true }]
}], selectedAll: [{
type: ViewChild,
args: ['selectedAll']
}], all: [{
type: Input
}], isFooter: [{
type: Input
}], checkAll: [{
type: Input
}], selectAll: [{
type: Output,
args: ['selectAll']
}], sortChange: [{
type: Output,
args: ['sortChange']
}], filterChange: [{
type: Output,
args: ['filterChange']
}] } });
class IconLoaderComponent {
constructor() { }
}
IconLoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
IconLoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: IconLoaderComponent, selector: "icon-loader", ngImport: i0, template: `
<svg width="84" height="84" viewBox="0 0 24 24" class="bh-loader bh-text-primary">
<circle cx="18" cy="12" r="0" fill="currentColor">
<animate
attributeName="r"
begin=".67"
calcMode="spline"
dur="1.5s"
keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8"
repeatCount="indefinite"
values="0;2;0;0"
/>
</circle>
<circle cx="12" cy="12" r="0" fill="currentColor">
<animate
attributeName="r"
begin=".33"
calcMode="spline"
dur="1.5s"
keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8"
repeatCount="indefinite"
values="0;2;0;0"
/>
</circle>
<circle cx="6" cy="12" r="0" fill="currentColor">
<animate
attributeName="r"
begin="0"
calcMode="spline"
dur="1.5s"
keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8"
repeatCount="indefinite"
values="0;2;0;0"
/>
</circle>
</svg>
`, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: IconLoaderComponent, decorators: [{
type: Component,
args: [{
selector: 'icon-loader',
template: `
<svg width="84" height="84" viewBox="0 0 24 24" class="bh-loader bh-text-primary">
<circle cx="18" cy="12" r="0" fill="currentColor">
<animate
attributeName="r"
begin=".67"
calcMode="spline"
dur="1.5s"
keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8"
repeatCount="indefinite"
values="0;2;0;0"
/>
</circle>
<circle cx="12" cy="12" r="0" fill="currentColor">
<animate
attributeName="r"
begin=".33"
calcMode="spline"
dur="1.5s"
keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8"
repeatCount="indefinite"
values="0;2;0;0"
/>
</circle>
<circle cx="6" cy="12" r="0" fill="currentColor">
<animate
attributeName="r"
begin="0"
calcMode="spline"
dur="1.5s"
keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8"
repeatCount="indefinite"
values="0;2;0;0"
/>
</circle>
</svg>
`,
}]
}], ctorParameters: function () { return []; } });
class NgDataTableComponent {
constructor(sanitizer) {
this.sanitizer = sanitizer;
// props
this.loading = false;
this.isServerMode = false;
this.skin = 'bh-table-striped bh-table-hover';
this.totalRows = 0;
this.rows = [];
this.columns = [];
this.hasCheckbox = false;
this.search = '';
this.page = 1;
this.pageSize = 10;
this.pageSizeOptions = [10, 20, 30, 50, 100];
this.showPageSize = true;
this.rowClass = '';
this.cellClass = '';
this.sortable = false;
this.sortColumn = 'id';
this.sortDirection = 'asc';
this.columnFilter = false;
this.pagination = true;
this.showNumbers = true;
this.showNumbersCount = 5;
this.showFirstPage = true;
this.showLastPage = true;
this.firstArrow = '';
this.lastArrow = '';
this.nextArrow = '';
this.previousArrow = '';
this.paginationInfo = 'Showing {0} to {1} of {2} entries';
this.noDataContent = 'No data available';
this.stickyHeader = false;
this.height = '500px';
this.stickyFirstColumn = false;
this.cloneHeaderInFooter = false;
this.selectRowOnClick = false;
this.showFooterRow = false;
// events
this.changeServer = new EventEmitter();
this.sortChange = new EventEmitter();
this.searchChange = new EventEmitter();
this.pageChange = new EventEmitter();
this.pageSizeChange = new EventEmitter();
this.rowSelect = new EventEmitter();
this.filterChange = new EventEmitter();
this.rowClick = new EventEmitter();
this.rowDBClick = new EventEmitter();
// variables
this.filterItems = [];
this.currentPage = this.page;
this.currentPageSize = this.pagination ? this.pageSize : this.rows.length;
this.oldPageSize = this.pageSize;
this.currentSortColumn = this.sortColumn;
this.oldSortColumn = this.sortColumn;
this.currentSortDirection = this.sortDirection;
this.oldSortDirection = this.sortDirection;
this.filterRowCount = this.totalRows;
this.selectedAll = null;
this.currentLoader = this.loading;
this.currentSearch = this.search;
this.uniqueKey = '';
this.pager = new Pager();
//row click
this.timer = null;
this.delay = 230;
this.slotsMap = new Map();
}
ngOnInit() {
this.initDeafultValues();
this.changeRows();
}
ngOnChanges(changes) {
// watch loading
if (changes['loading'] && !changes['loading'].firstChange) {
this.currentLoader = this.loading;
}
// watch rows and columns
if ((changes['rows'] && !changes['rows'].firstChange) || (changes['columns'] && !changes['columns'].firstChange)) {
if (!this.isServerMode) {
this.currentPage = 1;
this.oldColumns = this.noReact(this.columns);
}
this.changeRows();
}
// watch page
if (!this.isServerMode) {
if (changes['page'] && !changes['page'].firstChange) {
this.movePage(this.page);
}
}
// watch pagesize
if (changes['pageSize'] && !changes['pageSize'].firstChange) {
this.currentPageSize = this.pagination ? this.pageSize : this.rows.length;
if (!this.isServerMode) {
this.changePageSize();
}
}
// watch search
if (changes['search'] && !changes['search'].firstChange) {
this.currentSearch = this.search;
this.changeSearch();
}
// watch sort column and direction
if (!this.isServerMode) {
if ((changes['sortColumn'] && !changes['sortColumn'].firstChange) || (changes['sortDirection'] && !changes['sortDirection'].firstChange)) {
this.sortChangeMethod(this.sortColumn, this.sortDirection);
}
}
}
initDeafultValues() {
this.currentLoader = this.loading;
this.currentSearch = this.search;
this.currentSortColumn = this.sortColumn;
this.currentSortDirection = this.sortDirection;
this.filterRowCount = this.totalRows;
this.currentPage = this.page;
if (this.pagination) {
const exists = this.pageSizeOptions.find((d) => d === this.pageSize);
this.currentPageSize = exists ? this.pageSize : 10;
}
else {
this.currentPageSize = this.rows.length;
}
this.oldPageSize = this.pageSize;
this.oldSortColumn = this.sortColumn;
this.oldSortDirection = this.sortDirection;
// set default columns values
for (const item of this.columns || []) {
const type = item.type?.toLowerCase() || 'string';
item.type = type;
item.isUnique = item.isUnique !== undefined ? item.isUnique : false;
item.hide = item.hide !== undefined ? item.hide : false;
item.filter = item.filter !== undefined ? item.filter : true;
item.search = item.search !== undefined ? item.search : true;
item.sort = item.sort !== undefined ? item.sort : true;
item.html = item.html !== undefined ? item.html : false;
item.condition = !type || type === 'string' ? 'contain' : 'equal';
}
this.oldColumns = this.noReact(this.columns);
this.setUniqueKey();
}
get getProps() {
return {
loading: this.currentLoader,
isServerMode: this.isServerMode,
skin: this.skin,
totalRows: this.filterRowCount,
rows: this.rows,
columns: this.columns,
hasCheckbox: this.hasCheckbox,
search: this.currentSearch,
page: this.currentPage,
pageSize: this.currentPageSize,
pageSizeOptions: this.pageSizeOptions,
showPageSize: this.showPageSize,
rowClass: this.rowClass,
cellClass: this.cellClass,
sortable: this.sortable,
sortColumn: this.currentSortColumn,
sortDirection: this.currentSortDirection,
columnFilter: this.columnFilter,
pagination: this.pagination,
showNumbers: this.showNumbers,
showNumbersCount: this.showNumbersCount,
showFirstPage: this.showFirstPage,
showLastPage: this.showLastPage,
firstArrow: this.firstArrow,
lastArrow: this.lastArrow,
nextArrow: this.nextArrow,
previousArrow: this.previousArrow,
paginationInfo: this.paginationInfo,
noDataContent: this.noDataContent,
stickyHeader: this.stickyHeader,
height: this.height,
stickyFirstColumn: this.stickyFirstColumn,
cloneHeaderInFooter: this.cloneHeaderInFooter,
selectRowOnClick: this.selectRowOnClick,
showFooterRow: this.showFooterRow,
};
}
isFunction(value) {
return typeof value === 'function';
}
stringFormat() {
const args = [this.filterRowCount ? this.offset() : 0, this.limit(), this.filterRowCount];
return this.paginationInfo.replace(/{(\d+)}/g, (match, number) => {
return typeof args[number] != 'undefined' ? args[number] : match;
});
}
setUniqueKey() {
const col = this.columns.find((d) => d.isUnique);
this.uniqueKey = col?.field || '';
}
maxPage() {
const totalPages = this.currentPageSize < 1 ? 1 : Math.ceil(this.filterRowCount / this.currentPageSize);
return Math.max(totalPages || 0, 1);
}
offset() {
return (this.currentPage - 1) * this.currentPageSize + 1;
}
limit() {
const limit = this.currentPage * this.currentPageSize;
return this.filterRowCount >= limit ? limit : this.filterRowCount;
}
getPager() {
// total pages
let totalPages = this.maxPage();
// pages
let startPage, endPage;
let isMaxSized = typeof this.showNumbersCount !== 'undefined' && this.showNumbersCount < totalPages;
// recompute if maxSize
if (isMaxSized) {
// Current page is displayed in the middle of the visible ones
startPage = Math.max(this.currentPage - Math.floor(this.showNumbersCount / 2), 1);
endPage = startPage + this.showNumbersCount - 1;
// Adjust if limit is exceeded
if (endPage > totalPages) {
endPage = totalPages;
startPage = endPage - this.showNumbersCount + 1;
}
}
else {
startPage = 1;
endPage = totalPages;
}
const pages = Array.from(Array(endPage + 1 - startPage).keys()).map((i) => startPage + i);
return {
totalRows: this.isServerMode ? this.totalRows : this.filterItems.length,
currentPage: this.currentPage,
pageSize: this.pageSize,
maxPage: totalPages,
startPage: startPage,
endPage: endPage,
stringFormat: this.stringFormat(),
pages: pages,
};
}
setPager() {
this.pager = this.getPager();
}
filterRows() {
let result = [];
let rows = this.rows || [];
if (this.isServerMode) {
this.filterRowCount = this.totalRows || 0;
result = rows;
}
else {
this.columns?.forEach((d) => {
if (d.filter && ((d.value !== undefined && d.value !== null && d.value !== '') || d.condition === 'is_null' || d.condition === 'is_not_null')) {
// string filters
if (d.type === 'string') {
if (d.value && !d.condition) {
d.condition = 'contain';
}
if (d.condition === 'contain') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field)?.toString().toLowerCase().includes(d.value.toLowerCase());
});
}
else if (d.condition === 'not_contain') {
rows = rows.filter((item) => {
return !this.cellValue(item, d.field)?.toString().toLowerCase().includes(d.value.toLowerCase());
});
}
else if (d.condition === 'equal') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field)?.toString().toLowerCase() === d.value.toLowerCase();
});
}
else if (d.condition === 'not_equal') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field)?.toString().toLowerCase() !== d.value.toLowerCase();
});
}
else if (d.condition === 'start_with') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field)?.toString().toLowerCase().indexOf(d.value.toLowerCase()) === 0;
});
}
else if (d.condition === 'end_with') {
rows = rows.filter((item) => {
return (this.cellValue(item, d.field)
?.toString()
.toLowerCase()
.substr(d.value.length * -1) === d.value.toLowerCase());
});
}
}
// number filters
else if (d.type === 'number') {
if (d.value && !d.condition) {
d.condition = 'equal';
}
if (d.condition === 'equal') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field) && parseFloat(this.cellValue(item, d.field)) === parseFloat(d.value);
});
}
else if (d.condition === 'not_equal') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field) && parseFloat(this.cellValue(item, d.field)) !== parseFloat(d.value);
});
}
else if (d.condition === 'greater_than') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field) && parseFloat(this.cellValue(item, d.field)) > parseFloat(d.value);
});
}
else if (d.condition === 'greater_than_equal') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field) && parseFloat(this.cellValue(item, d.field)) >= parseFloat(d.value);
});
}
else if (d.condition === 'less_than') {
rows = rows.filter((item) => {
return this.cellValue(item, d.field) && parseFloat(this.cellValue(it