novo-elements
Version:
1,035 lines (1,024 loc) • 235 kB
JavaScript
import * as i1 from '@angular/cdk/table';
import { CdkHeaderCell, CdkCell, DataSource, CdkHeaderRow, CDK_ROW_TEMPLATE, CdkRow, CdkTableModule } from '@angular/cdk/table';
import * as i0 from '@angular/core';
import { InjectionToken, Input, HostBinding, Inject, ChangeDetectionStrategy, Component, EventEmitter, Injectable, Output, Directive, HostListener, ViewChild, Optional, Pipe, ViewChildren, ContentChildren, ViewEncapsulation, NgModule } from '@angular/core';
import * as i2 from 'novo-elements/elements/toast';
import { Subject, fromEvent, merge, of } from 'rxjs';
import * as i1$1 from 'novo-elements/services';
import { DateUtil, Helpers, BooleanInput, notify } from 'novo-elements/utils';
import * as i5$1 from 'novo-elements/elements/dropdown';
import { NovoDropdownElement, NovoDropdownModule } from 'novo-elements/elements/dropdown';
import { endOfToday, startOfToday } from 'date-fns';
import { trigger, state, style, transition, animate } from '@angular/animations';
import * as i3 from 'novo-elements/elements/icon';
import { NovoIconModule } from 'novo-elements/elements/icon';
import * as i5 from 'novo-elements/elements/date-picker';
import { NovoDatePickerModule } from 'novo-elements/elements/date-picker';
import * as i6 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i7 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import * as i3$1 from 'novo-elements/elements/button';
import { NovoButtonModule } from 'novo-elements/elements/button';
import * as i11 from 'novo-elements/elements/common';
import { NovoTemplate, NovoOptionModule, NovoCommonModule } from 'novo-elements/elements/common';
import * as i12 from 'novo-elements/elements/tooltip';
import { NovoTooltipModule } from 'novo-elements/elements/tooltip';
import * as i13 from 'novo-elements/elements/flex';
import { NovoFlexModule } from 'novo-elements/elements/flex';
import * as i14 from 'novo-elements/elements/field';
import { NovoFieldModule } from 'novo-elements/elements/field';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { startWith, switchMap, map, catchError } from 'rxjs/operators';
import * as i9$1 from 'novo-elements/elements/drag-drop';
import { NovoDragDropModule } from 'novo-elements/elements/drag-drop';
import * as i6$1 from 'novo-elements/elements/loading';
import { NovoLoadingModule } from 'novo-elements/elements/loading';
import * as i11$1 from 'novo-elements/elements/search';
import { NovoSearchBoxModule } from 'novo-elements/elements/search';
import * as i14$1 from '@angular/cdk/scrolling';
import { ScrollingModule } from '@angular/cdk/scrolling';
import * as i7$1 from 'novo-elements/elements/tiles';
import { NovoTilesModule } from 'novo-elements/elements/tiles';
import * as i9 from 'novo-elements/elements/select';
import { NovoSelectModule } from 'novo-elements/elements/select';
import { NovoCheckboxModule } from 'novo-elements/elements/checkbox';
import { NovoFormExtrasModule } from 'novo-elements/elements/form';
/**
* Injection token used to provide the parent component to options.
*/
const NOVO_DATA_TABLE_REF = new InjectionToken('NOVO_DATA_TABLE_REF');
class NovoDataTableCheckboxHeaderCell extends CdkHeaderCell {
get isAtLimit() {
return (this.maxSelected && this.dataTable.state.selectedRows.size + this.dataTable.dataSource.data.length > this.maxSelected && !this.checked);
}
constructor(columnDef, elementRef, renderer, dataTable, ref, toaster) {
super(columnDef, elementRef);
this.dataTable = dataTable;
this.ref = ref;
this.toaster = toaster;
this.role = 'columnheader';
this.maxSelected = undefined;
this.checked = false;
renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-checkbox-column-header-${columnDef.cssClassFriendlyName}`);
renderer.addClass(elementRef.nativeElement, `novo-checkbox-column-${columnDef.cssClassFriendlyName}`);
renderer.addClass(elementRef.nativeElement, 'novo-data-table-checkbox-header-cell');
this.selectionSubscription = this.dataTable.state.selectionSource.subscribe(() => {
this.checked = this.dataTable.allCurrentRowsSelected() || (this.dataTable?.canSelectAll && this.dataTable?.allMatchingSelected);
if (this.dataTable?.canSelectAll) {
this.selectAllChanged();
}
this.ref.markForCheck();
});
this.paginationSubscription = this.dataTable.state.paginationSource.subscribe((event) => {
if (event.isPageSizeChange) {
this.checked = false;
if (this.dataTable?.canSelectAll) {
this.selectAllChanged();
}
this.dataTable.selectRows(false);
this.dataTable.state.checkRetainment('pageSize');
this.dataTable.state.reset(false, true);
}
else {
this.checked = this.dataTable.allCurrentRowsSelected() || (this.dataTable?.canSelectAll && this.dataTable?.allMatchingSelected);
if (this.dataTable?.canSelectAll) {
this.selectAllChanged();
}
}
this.ref.markForCheck();
});
this.resetSubscription = this.dataTable.state.resetSource.subscribe(() => {
this.checked = false;
if (this.dataTable?.canSelectAll) {
this.resetAllMatchingSelected();
}
this.ref.markForCheck();
});
}
ngOnDestroy() {
if (this.selectionSubscription) {
this.selectionSubscription.unsubscribe();
}
if (this.paginationSubscription) {
this.paginationSubscription.unsubscribe();
}
if (this.resetSubscription) {
this.resetSubscription.unsubscribe();
}
}
onClick() {
if (this.isAtLimit) {
this.toaster.alert({
theme: 'danger',
position: 'fixedTop',
message: 'Error, more than 500 items are not able to be selected at one time',
icon: 'caution',
});
}
else {
this.dataTable.selectRows(!this.checked);
}
if (this.dataTable?.canSelectAll) {
if (this.checked) {
this.resetAllMatchingSelected();
}
else {
this.selectAllChanged();
}
}
}
resetAllMatchingSelected() {
this.dataTable.state?.allMatchingSelectedSource?.next(false);
this.dataTable.state?.onSelectionChange();
}
selectAllChanged() {
const allSelectedEvent = {
allSelected: this.checked,
selectedCount: this.dataTable?.state?.selected?.length,
allMatchingSelected: this.dataTable?.allMatchingSelected,
};
this.dataTable.allSelected.emit(allSelectedEvent);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableCheckboxHeaderCell, deps: [{ token: i1.CdkColumnDef }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: NOVO_DATA_TABLE_REF }, { token: i0.ChangeDetectorRef }, { token: i2.NovoToastService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDataTableCheckboxHeaderCell, isStandalone: false, selector: "novo-data-table-checkbox-header-cell", inputs: { maxSelected: "maxSelected" }, host: { properties: { "attr.role": "this.role" } }, usesInheritance: true, ngImport: i0, template: `
<div class="data-table-checkbox" (click)="onClick()">
<input type="checkbox" [checked]="checked" />
<label>
<i [class.bhi-checkbox-empty]="!checked" [class.bhi-checkbox-filled]="checked"></i>
</label>
</div>
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableCheckboxHeaderCell, decorators: [{
type: Component,
args: [{
selector: 'novo-data-table-checkbox-header-cell',
template: `
<div class="data-table-checkbox" (click)="onClick()">
<input type="checkbox" [checked]="checked" />
<label>
<i [class.bhi-checkbox-empty]="!checked" [class.bhi-checkbox-filled]="checked"></i>
</label>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}]
}], ctorParameters: () => [{ type: i1.CdkColumnDef }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
type: Inject,
args: [NOVO_DATA_TABLE_REF]
}] }, { type: i0.ChangeDetectorRef }, { type: i2.NovoToastService }], propDecorators: { role: [{
type: HostBinding,
args: ['attr.role']
}], maxSelected: [{
type: Input
}] } });
class NovoDataTableExpandHeaderCell extends CdkHeaderCell {
constructor(columnDef, elementRef, renderer, dataTable, ref) {
super(columnDef, elementRef);
this.dataTable = dataTable;
this.ref = ref;
this.role = 'columnheader';
this.expanded = false;
renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-expand-column-header-${columnDef.cssClassFriendlyName}`);
renderer.addClass(elementRef.nativeElement, `novo-expand-column-${columnDef.cssClassFriendlyName}`);
renderer.addClass(elementRef.nativeElement, 'novo-data-table-expand-header-cell');
this.expandSubscription = this.dataTable.state.expandSource.subscribe(() => {
this.expanded = this.dataTable.allCurrentRowsExpanded();
this.ref.markForCheck();
});
}
ngOnDestroy() {
if (this.expandSubscription) {
this.expandSubscription.unsubscribe();
}
}
expandAll() {
this.dataTable.expandRows(!this.expanded);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableExpandHeaderCell, deps: [{ token: i1.CdkColumnDef }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: NOVO_DATA_TABLE_REF }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDataTableExpandHeaderCell, isStandalone: false, selector: "novo-data-table-expand-header-cell", host: { properties: { "attr.role": "this.role" } }, usesInheritance: true, ngImport: i0, template: ' <i class="bhi-next data-table-icon" novo-data-table-expander="true" (click)="expandAll()" [class.expanded]="expanded"></i> ', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableExpandHeaderCell, decorators: [{
type: Component,
args: [{
selector: 'novo-data-table-expand-header-cell',
template: ' <i class="bhi-next data-table-icon" novo-data-table-expander="true" (click)="expandAll()" [class.expanded]="expanded"></i> ',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}]
}], ctorParameters: () => [{ type: i1.CdkColumnDef }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
type: Inject,
args: [NOVO_DATA_TABLE_REF]
}] }, { type: i0.ChangeDetectorRef }], propDecorators: { role: [{
type: HostBinding,
args: ['attr.role']
}] } });
class NovoDataTableFilterUtils {
static constructFilter(filter, type, multiSelect) {
let actualFilter = filter;
if (filter) {
if (type && type === 'date') {
if (filter.startDate && filter.endDate) {
actualFilter = {
min: DateUtil.startOfDay(filter.startDate.date),
max: DateUtil.startOfDay(DateUtil.addDays(DateUtil.startOfDay(filter.endDate.date), 1)),
};
}
else {
actualFilter = {
min: filter.min ? DateUtil.addDays(startOfToday(), filter.min) : startOfToday(),
max: filter.max ? DateUtil.addDays(endOfToday(), filter.max) : endOfToday(),
};
}
}
if (multiSelect && Array.isArray(filter)) {
actualFilter = filter.map((filterItem) => {
if (filterItem && filterItem.hasOwnProperty('value')) {
return filterItem.value;
}
return filterItem;
});
}
else if (actualFilter && actualFilter.hasOwnProperty('value')) {
actualFilter = filter.value;
}
}
return actualFilter;
}
}
class DataTableState {
constructor() {
this.selectionSource = new Subject();
this.paginationSource = new Subject();
this.sortFilterSource = new Subject();
this.resetSource = new Subject();
this.expandSource = new Subject();
this.allMatchingSelectedSource = new Subject();
this.dataLoaded = new Subject();
this.dataLoadingSource = new Subject();
this.sort = undefined;
this.filter = undefined;
this.where = undefined;
this.page = 0;
this.pageSize = undefined;
this.globalSearch = undefined;
this.selectedRows = new Map();
this.expandedRows = new Set();
this.isForceRefresh = false;
this.updates = new EventEmitter();
this.retainSelected = false;
this.savedSearchName = undefined;
this.displayedColumns = undefined;
}
get userFiltered() {
return !!(this.filter || this.sort || this.globalSearch || this.outsideFilter || this.where);
}
get userFilteredInternal() {
return !!(this.filter || this.sort || this.globalSearch || this.where);
}
get selected() {
return Array.from(this.selectedRows.values());
}
reset(fireUpdate = true, persistUserFilters) {
this.setState({}, fireUpdate, persistUserFilters);
}
clearSort(fireUpdate = true) {
this.sort = undefined;
this.page = 0;
this.checkRetainment('sort');
this.reset(fireUpdate, true);
this.onSortFilterChange();
if (fireUpdate) {
this.updates.emit({
sort: this.sort,
filter: this.filter,
globalSearch: this.globalSearch,
where: this.where,
});
}
}
clearFilter(fireUpdate = true) {
this.filter = undefined;
this.globalSearch = undefined;
this.page = 0;
this.checkRetainment('filter');
this.reset(fireUpdate, true);
this.onSortFilterChange();
if (fireUpdate) {
this.updates.emit({
sort: this.sort,
filter: this.filter,
globalSearch: this.globalSearch,
where: this.where,
});
}
}
clearQuery(fireUpdate = true) {
this.where = undefined;
this.page = 0;
this.checkRetainment('where');
this.reset(fireUpdate, true);
this.onSortFilterChange();
if (fireUpdate) {
this.updates.emit({
sort: this.sort,
filter: this.filter,
globalSearch: this.globalSearch,
where: this.where,
});
}
}
clearSelected(fireUpdate = true) {
this.allMatchingSelectedSource.next(false);
this.globalSearch = undefined;
this.page = 0;
this.reset(fireUpdate, true);
this.onSelectionChange();
if (fireUpdate) {
this.updates.emit({
sort: this.sort,
filter: this.filter,
globalSearch: this.globalSearch,
where: this.where,
});
}
}
onSelectionChange() {
this.selectionSource.next();
}
onExpandChange(targetId) {
this.expandSource.next(targetId);
}
onPaginationChange(isPageSizeChange, pageSize) {
this.checkRetainment('page');
this.paginationSource.next({ isPageSizeChange, pageSize });
}
onSortFilterChange() {
this.checkRetainment('sort');
this.checkRetainment('filter');
this.checkRetainment('where');
this.sortFilterSource.next({
sort: this.sort,
filter: this.filter,
globalSearch: this.globalSearch,
where: this.where,
savedSearchName: this.savedSearchName,
appliedSearchType: this.appliedSearchType,
});
}
setInitialSortFilter(preferences) {
if (preferences) {
if (preferences.where) {
this.where = preferences.where;
}
if (preferences.sort) {
this.sort = preferences.sort;
}
if (preferences.filter) {
this.filter = this.transformFilters(preferences.filter);
}
if (preferences.globalSearch) {
this.globalSearch = preferences.globalSearch;
}
if (preferences.savedSearchName) {
this.savedSearchName = preferences.savedSearchName;
}
if (preferences.appliedSearchType) {
this.appliedSearchType = preferences.appliedSearchType;
}
}
}
setState(preferences, fireUpdate = true, persistUserFilters = false) {
if (!persistUserFilters) {
this.where = preferences.where;
this.sort = preferences.sort;
this.filter = preferences.filter ? this.transformFilters(preferences.filter) : undefined;
this.globalSearch = preferences.globalSearch;
this.savedSearchName = preferences.savedSearchName;
if (preferences.displayedColumns?.length) {
this.displayedColumns = preferences.displayedColumns;
}
this.appliedSearchType = preferences.appliedSearchType;
}
this.page = 0;
if (!this.retainSelected) {
this.selectedRows.clear();
this.resetSource.next();
}
this.onSortFilterChange();
this.retainSelected = false;
if (fireUpdate) {
this.updates.emit({
sort: this.sort,
filter: this.filter,
globalSearch: this.globalSearch,
where: this.where,
savedSearchName: this.savedSearchName,
displayedColumns: this.displayedColumns,
appliedSearchType: this.appliedSearchType,
});
}
}
checkRetainment(caller, allMatchingSelected = false) {
this.retainSelected = this.selectionOptions?.some((option) => option.label === caller) || this.retainSelected || allMatchingSelected;
}
transformFilters(filters) {
const filterArray = Helpers.convertToArray(filters);
filterArray.forEach((filter) => {
filter.value =
filter.selectedOption && filter.type
? NovoDataTableFilterUtils.constructFilter(filter.selectedOption, filter.type)
: filter.value;
});
return filterArray;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableState, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableState }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableState, decorators: [{
type: Injectable
}] });
var SortDirection;
(function (SortDirection) {
SortDirection["ASC"] = "ascending";
SortDirection["DESC"] = "descending";
SortDirection["NONE"] = "none";
})(SortDirection || (SortDirection = {}));
const activeStyle = { opacity: 1, top: 0 };
const inactiveStyle = { opacity: 0 };
/** Animation that moves the sort indicator. */
const sortAscAnim = trigger('sortAsc', [
// ...
state(SortDirection.ASC, style(activeStyle)),
state(SortDirection.DESC, style(inactiveStyle)),
state(SortDirection.NONE, style(inactiveStyle)),
transition('* => ascending', [animate('1s')]),
transition('ascending => *', [animate('0.5s')]),
]);
const sortDescAnim = trigger('sortDesc', [
// ...
state(SortDirection.ASC, style(inactiveStyle)),
state(SortDirection.DESC, style(activeStyle)),
state(SortDirection.NONE, style(inactiveStyle)),
transition('* => descending', [animate('1s')]),
transition('descending => *', [animate('0.5s')]),
]);
const sortNoneAnim = trigger('sortNone', [
// ...
state(SortDirection.ASC, style(inactiveStyle)),
state(SortDirection.DESC, style(inactiveStyle)),
state(SortDirection.NONE, style(activeStyle)),
transition('* => none', [animate('1s')]),
transition('none => *', [animate('0.5s')]),
]);
class NovoDataTableSortButton {
get value() {
return this._value;
}
set value(value) {
this._value = value;
}
get isActive() {
return this.value !== SortDirection.NONE;
}
constructor(state, ref, labels) {
this.state = state;
this.ref = ref;
this.labels = labels;
this.sortChange = new EventEmitter();
this.SortDirection = SortDirection;
this._value = SortDirection.NONE;
}
changeSort(dir) {
this.value = dir;
this.sortChange.emit(dir);
}
clearSort() {
this.state.clearSort();
this.sortChange.emit(SortDirection.NONE);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableSortButton, deps: [{ token: DataTableState }, { token: i0.ChangeDetectorRef }, { token: i1$1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDataTableSortButton, isStandalone: false, selector: "novo-sort-button", inputs: { value: "value" }, outputs: { sortChange: "sortChange" }, ngImport: i0, template: "<novo-icon\n class=\"novo-sort-asc-icon\"\n [class.sort-active]=\"isActive\"\n [class.sort-hidden]=\"value !== SortDirection.ASC\"\n [@sortAsc]=\"value\"\n (click)=\"changeSort(SortDirection.DESC)\">arrow-up</novo-icon>\n<novo-icon\n class=\"novo-sort-desc-icon\"\n [class.sort-active]=\"isActive\"\n [class.sort-hidden]=\"value !== SortDirection.DESC\"\n [@sortDesc]=\"value\"\n (click)=\"changeSort(SortDirection.NONE)\">arrow-down</novo-icon>\n<novo-icon\n class=\"novo-sortable-icon\"\n [class.sort-active]=\"isActive\"\n [class.sort-hidden]=\"value !== SortDirection.NONE\"\n [@sortNone]=\"value\"\n (click)=\"changeSort(SortDirection.ASC)\">sortable</novo-icon>", styles: [":host{display:inline-flex;position:relative;width:1.6rem;height:1.6rem;cursor:pointer}:host novo-icon{position:absolute;opacity:0;color:var(--text-muted)}:host novo-icon:hover{color:var(--selection)}:host novo-icon.sort-hidden{pointer-events:none}:host .novo-sort-asc-icon{top:10px;color:var(--selection)}:host .novo-sort-desc-icon{top:-10px;color:var(--selection)}\n"], dependencies: [{ kind: "component", type: i3.NovoIconComponent, selector: "novo-icon", inputs: ["raised", "theme", "shape", "color", "size", "smaller", "larger", "alt", "name"] }], animations: [sortAscAnim, sortDescAnim, sortNoneAnim], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableSortButton, decorators: [{
type: Component,
args: [{ selector: 'novo-sort-button', changeDetection: ChangeDetectionStrategy.OnPush, animations: [sortAscAnim, sortDescAnim, sortNoneAnim], standalone: false, template: "<novo-icon\n class=\"novo-sort-asc-icon\"\n [class.sort-active]=\"isActive\"\n [class.sort-hidden]=\"value !== SortDirection.ASC\"\n [@sortAsc]=\"value\"\n (click)=\"changeSort(SortDirection.DESC)\">arrow-up</novo-icon>\n<novo-icon\n class=\"novo-sort-desc-icon\"\n [class.sort-active]=\"isActive\"\n [class.sort-hidden]=\"value !== SortDirection.DESC\"\n [@sortDesc]=\"value\"\n (click)=\"changeSort(SortDirection.NONE)\">arrow-down</novo-icon>\n<novo-icon\n class=\"novo-sortable-icon\"\n [class.sort-active]=\"isActive\"\n [class.sort-hidden]=\"value !== SortDirection.NONE\"\n [@sortNone]=\"value\"\n (click)=\"changeSort(SortDirection.ASC)\">sortable</novo-icon>", styles: [":host{display:inline-flex;position:relative;width:1.6rem;height:1.6rem;cursor:pointer}:host novo-icon{position:absolute;opacity:0;color:var(--text-muted)}:host novo-icon:hover{color:var(--selection)}:host novo-icon.sort-hidden{pointer-events:none}:host .novo-sort-asc-icon{top:10px;color:var(--selection)}:host .novo-sort-desc-icon{top:-10px;color:var(--selection)}\n"] }]
}], ctorParameters: () => [{ type: DataTableState }, { type: i0.ChangeDetectorRef }, { type: i1$1.NovoLabelService }], propDecorators: { sortChange: [{
type: Output
}], value: [{
type: Input
}] } });
class NovoDataTableSortFilter {
constructor(state) {
this.state = state;
}
filter(id, type, value, transform, allowMultipleFilters = false, selectedOption) {
let filter;
if (allowMultipleFilters) {
filter = this.resolveMultiFilter(id, type, value, transform, selectedOption);
}
else {
if (!Helpers.isBlank(value)) {
filter = { id, type, value, transform, ...(selectedOption && { selectedOption }) };
}
else {
filter = undefined;
}
}
this.state.filter = filter;
this.state.checkRetainment('filter');
this.state.reset(false, true);
this.state.updates.next({ filter, sort: this.state.sort });
this.state.onSortFilterChange();
}
sort(id, value, transform) {
const sort = { id, value, transform };
this.state.sort = sort;
this.state.checkRetainment('sort');
this.state.reset(false, true);
this.state.updates.next({ sort, filter: this.state.filter });
this.state.onSortFilterChange();
}
resolveMultiFilter(id, type, value, transform, selectedOption) {
let filter;
filter = Helpers.convertToArray(this.state.filter);
const filterIndex = filter.findIndex((aFilter) => aFilter && aFilter.id === id);
if (filterIndex > -1) {
filter.splice(filterIndex, 1);
}
if (!Helpers.isBlank(value)) {
filter = [...filter, { id, type, value, transform, ...(selectedOption && { selectedOption }) }];
}
if (filter.length < 1) {
filter = undefined;
}
return filter;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableSortFilter, deps: [{ token: DataTableState }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: NovoDataTableSortFilter, isStandalone: false, selector: "[novoDataTableSortFilter]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableSortFilter, decorators: [{
type: Directive,
args: [{
selector: '[novoDataTableSortFilter]',
standalone: false,
}]
}], ctorParameters: () => [{ type: DataTableState }] });
class NovoDataTableCellFilterHeader {
set filter(filter) {
this._filter = filter;
this.hasFilter = !Helpers.isEmpty(filter);
}
get filter() {
return this._filter;
}
constructor(changeDetectorRef, labels) {
this.changeDetectorRef = changeDetectorRef;
this.labels = labels;
this.hasFilter = false;
this.clearFilter = new EventEmitter();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableCellFilterHeader, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.NovoLabelService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDataTableCellFilterHeader, isStandalone: false, selector: "novo-data-table-cell-filter-header", inputs: { label: "label", filter: "filter" }, outputs: { clearFilter: "clearFilter" }, ngImport: i0, template: `
<div class="header">
<novo-label>{{ label || labels.filters }}</novo-label>
<novo-button
theme="dialogue"
color="negative"
size="small"
icon="times"
(click)="clearFilter.emit()"
*ngIf="hasFilter"
data-automation-id="novo-data-table-filter-clear">
{{ labels.clear }}
</novo-button>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3$1.NovoButtonElement, selector: "novo-button,button[theme]", inputs: ["color", "side", "size", "theme", "loading", "icon", "secondIcon", "disabled"] }, { kind: "component", type: i11.NovoLabel, selector: "novo-label,[novo-label]", inputs: ["id"] }, { kind: "directive", type: i11.ThemeColorDirective, selector: "[theme]", inputs: ["theme"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableCellFilterHeader, decorators: [{
type: Component,
args: [{
selector: 'novo-data-table-cell-filter-header',
template: `
<div class="header">
<novo-label>{{ label || labels.filters }}</novo-label>
<novo-button
theme="dialogue"
color="negative"
size="small"
icon="times"
(click)="clearFilter.emit()"
*ngIf="hasFilter"
data-automation-id="novo-data-table-filter-clear">
{{ labels.clear }}
</novo-button>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$1.NovoLabelService }], propDecorators: { label: [{
type: Input
}], filter: [{
type: Input
}], clearFilter: [{
type: Output
}] } });
class NovoDataTableCellHeader {
set column(column) {
this._column = column;
this.label = column.type === 'action' ? '' : column.label;
this.labelIcon = column.labelIcon;
this.config = {
sortable: !!column.sortable,
filterable: !!column.filterable,
resizable: !!column.resizable,
};
this.resizable = this.config.resizable;
const transforms = {};
if (column.filterable && Helpers.isObject(column.filterable)) {
this.config.filterConfig = column.filterable;
if (!this.config.filterConfig.type) {
this.config.filterConfig = { type: 'text' };
}
if (column.filterable.transform) {
transforms.filter = column.filterable.transform;
}
}
else {
this.config.filterConfig = { type: 'text' };
}
if (column.sortable && Helpers.isObject(column.sortable)) {
if (column.sortable.transform) {
transforms.sort = column.sortable.transform;
}
}
if (this.config.filterConfig.type === 'date' && !this.config.filterConfig.options) {
this.config.filterConfig.options = this.getDefaultDateFilterOptions();
}
this.config.transforms = transforms;
}
get column() {
return this._column;
}
constructor(changeDetectorRef, labels, state, renderer, elementRef, _sort, _cdkColumnDef) {
this.changeDetectorRef = changeDetectorRef;
this.labels = labels;
this.state = state;
this.renderer = renderer;
this.elementRef = elementRef;
this._sort = _sort;
this._cdkColumnDef = _cdkColumnDef;
this.allowMultipleFilters = false;
this.toggledFilter = new EventEmitter();
this.icon = 'sortable';
this.filterActive = false;
this.sortActive = false;
this.sortValue = SortDirection.NONE;
this.showCustomRange = false;
this.multiSelect = false;
this.multiSelectedOptions = [];
this.multiSelectedOptionIsHidden = [];
this.optionFilter = '';
this.error = false;
this.subscriptions = [];
this._rerenderSubscription = state.updates.subscribe((change) => this.checkSortFilterState(change));
}
ngOnInit() {
if (this._cdkColumnDef) {
this.id = this._cdkColumnDef.name;
}
this.setupFilterOptions();
this.changeDetectorRef.markForCheck();
}
setupFilterOptions() {
this.checkSortFilterState({ filter: this.state.filter, sort: this.state.sort }, true);
this.multiSelect = this.config.filterConfig && this.config.filterConfig.type ? this.config.filterConfig.type === 'multi-select' : false;
if (this.multiSelect) {
this.multiSelectedOptions = this.filter ? [...this.filter] : [];
}
}
ngOnDestroy() {
this._rerenderSubscription.unsubscribe();
this.subscriptions.forEach((subscription) => {
subscription.unsubscribe();
});
}
checkSortFilterState(sortFilterState, initialConfig = false) {
if (sortFilterState.sort && sortFilterState.sort.id === this.id) {
this.icon = `sort-${sortFilterState.sort.value}`;
this.sortValue = sortFilterState.sort.value === 'asc' ? SortDirection.ASC : SortDirection.DESC;
this.sortActive = true;
}
else {
this.icon = 'sortable';
this.sortValue = SortDirection.NONE;
this.sortActive = false;
}
const tableFilter = Helpers.convertToArray(sortFilterState.filter);
const thisFilter = tableFilter.find((filter) => filter && filter.id === this.id);
if (thisFilter) {
this.filterActive = true;
if (initialConfig && thisFilter.type === 'date' && thisFilter.selectedOption) {
this.activeDateFilter = thisFilter.selectedOption.label || this.labels.customDateRange;
}
this.filter = thisFilter.value;
}
else {
this.filterActive = false;
this.filter = undefined;
this.activeDateFilter = undefined;
this.multiSelectedOptions = [];
}
if (this.defaultSort && this.id === this.defaultSort.id) {
this.icon = `sort-${this.defaultSort.value}`;
this.sortActive = true;
}
this.multiSelect = this.config.filterConfig && this.config.filterConfig.type ? this.config.filterConfig.type === 'multi-select' : false;
if (this.multiSelect) {
this.multiSelectedOptions = this.filter ? [...this.filter] : [];
if (this.config.filterConfig.options) {
if (typeof this.config.filterConfig.options[0] === 'string') {
this.multiSelectedOptionIsHidden = this.config.filterConfig.options.map((option) => ({ option, hidden: false }));
}
else {
this.multiSelectedOptionIsHidden = this.config.filterConfig.options.map((option) => ({
option,
hidden: false,
}));
}
}
}
this.changeDetectorRef.markForCheck();
}
isSelected(option, optionsList) {
if (optionsList) {
const optionValue = option.hasOwnProperty('value') ? option.value : option;
const found = optionsList.find((item) => this.optionPresentCheck(item, optionValue));
return found !== undefined;
}
return false;
}
toggleSelection(option) {
const optionValue = option.hasOwnProperty('value') ? option.value : option;
const optionIndex = this.multiSelectedOptions.findIndex((item) => this.optionPresentCheck(item, optionValue));
this.error = false;
if (optionIndex > -1) {
this.multiSelectedOptions.splice(optionIndex, 1);
if (this.optionFilter && !this.getOptionText(option).toLowerCase().startsWith(this.optionFilter.toLowerCase())) {
this.multiSelectedOptionIsHidden[this.multiSelectedOptionIsHidden.findIndex((record) => record.option === option)].hidden = true;
}
}
else {
this.multiSelectedOptions.push(optionValue);
}
}
optionPresentCheck(item, optionValue) {
if (item.hasOwnProperty('value')) {
return item.value === optionValue;
}
else {
return item === optionValue;
}
}
cancel() {
this.multiSelectedOptions = this.filter ? [...this.filter] : [];
this.dropdown.closePanel();
this.clearOptionFilter();
}
filterMultiSelect() {
if (this.multiSelectedOptions.length === 0 && !this.filter) {
this.multiSelectHasVisibleOptions() && this.dropdown ? (this.error = true) : null;
}
else {
this.clearOptionFilter();
const actualFilter = this.multiSelectedOptions.length > 0 ? [...this.multiSelectedOptions] : undefined;
this.filterData(actualFilter);
this.dropdown.closePanel();
}
}
multiSelectOptionFilter(optionFilter) {
this.multiSelectedOptionIsHidden.forEach((record) => {
if (record.option) {
record.hidden = !(this.getOptionText(record.option).toLowerCase().startsWith(optionFilter.toLowerCase()) ||
this.isSelected(record.option, this.multiSelectedOptions));
}
});
}
multiSelectOptionIsHidden(option) {
return this.multiSelectedOptionIsHidden.find((record) => record.option === option).hidden;
}
multiSelectHasVisibleOptions() {
return this.multiSelectedOptionIsHidden.some((record) => !record.hidden);
}
getOptionText(option) {
if (typeof option !== 'object') {
return option.toString();
}
else {
const opt = option;
return (opt.label.length > 0 ? opt.label : opt.value).toString();
}
}
multiSelectOptionFilterHandleKeydown(event) {
if (this.multiSelect) {
this.error = false;
if (this.dropdown.panelOpen && event.key === "Escape" /* Key.Escape */) {
// escape should clear text box and close
Helpers.swallowEvent(event);
this.clearOptionFilter();
this.dropdown.closePanel();
}
else if (event.key === "Enter" /* Key.Enter */) {
Helpers.swallowEvent(event);
this.filterMultiSelect();
}
else if ((event.keyCode >= 65 && event.keyCode <= 90) ||
(event.keyCode >= 96 && event.keyCode <= 105) ||
(event.keyCode >= 48 && event.keyCode <= 57)) {
this.optionFilterInput.nativeElement.focus();
}
}
}
handleEscapeKeydown(event) {
if (!this.multiSelect) {
this.error = false;
this.dropdown.closePanel();
}
}
clearOptionFilter() {
this.error = false;
if (this.optionFilter.length > 0) {
this.optionFilter = '';
this.multiSelectedOptionIsHidden.forEach((record) => {
record.hidden = false;
});
}
}
startResize(mouseDownEvent) {
mouseDownEvent.preventDefault();
const minimumWidth = 60 + (this.config.filterable ? 30 : 0) + (this.config.sortable ? 30 : 0);
const startingWidth = this.elementRef.nativeElement.getBoundingClientRect().width;
const mouseMoveSubscription = fromEvent(window.document, 'mousemove').subscribe((middleMouseEvent) => {
const differenceWidth = middleMouseEvent.clientX - mouseDownEvent.clientX;
let width = startingWidth + differenceWidth;
if (width < minimumWidth) {
width = minimumWidth;
}
this.setWidth(width);
});
const mouseUpSubscription = fromEvent(window.document, 'mouseup').subscribe(() => {
mouseUpSubscription.unsubscribe();
mouseMoveSubscription.unsubscribe();
this.changeDetectorRef.markForCheck();
});
this.subscriptions.push(mouseMoveSubscription);
this.subscriptions.push(mouseUpSubscription);
}
setWidth(width) {
this._column.width = width;
this.renderer.setStyle(this.elementRef.nativeElement, 'min-width', `${width}px`);
this.renderer.setStyle(this.elementRef.nativeElement, 'max-width', `${width}px`);
this.renderer.setStyle(this.elementRef.nativeElement, 'width', `${width}px`);
this.changeDetectorRef.markForCheck();
this.resized.next(this._column);
}
toggleCustomRange(event, value) {
Helpers.swallowEvent(event);
this.showCustomRange = value;
this.changeDetectorRef.markForCheck();
this.dropdown.openPanel(); // Ensures that the panel correctly updates to the dynamic size of the dropdown
}
clickedFilter(clickEvt) {
if ((typeof this.config.filterConfig === 'object') && this.config.filterConfig.type === 'custom' && !this.filterTemplate) {
this.toggledFilter.next(this.id);
clickEvt.stopImmediatePropagation();
return;
}
this.focusInput();
if (this.multiSelect && this.dropdown) {
this.dropdown._handleKeydown = (event) => {
this.multiSelectOptionFilterHandleKeydown(event);
};
this.changeDetectorRef.markForCheck();
}
}
focusInput() {
if (this.filterInput?.nativeElement) {
setTimeout(() => this.filterInput.nativeElement.focus());
}
}
sort() {
if (this.changeTimeout) {
clearTimeout(this.changeTimeout);
}
this.changeTimeout = setTimeout(() => {
this.direction = this.getNextSortDirection(this.direction);
this._sort.sort(this.id, this.direction, this.config.transforms.sort);
this.changeDetectorRef.markForCheck();
}, 300);
}
filterData(filter) {
let actualFilter = NovoDataTableFilterUtils.constructFilter(filter, this.config.filterConfig.type, this.multiSelect);
const selectedOption = this.config.filterConfig.type === 'date' && filter ? filter : undefined;
this.activeDateFilter = selectedOption ? selectedOption.label : undefined;
if (this.changeTimeout) {
clearTimeout(this.changeTimeout);
}
this.changeTimeout = setTimeout(() => {
if (actualFilter === '') {
actualFilter = undefined;
}
this._sort.filter(this.id, this.config.filterConfig.type, actualFilter, this.config.transforms.filter, this.allowMultipleFilters, selectedOption);
this.changeDetectorRef.markForCheck();
}, 300);
}
clearFilter() {
this.filter = undefined;
this.multiSelectedOptions = [];
this.activeDateFilter = undefined;
this.filterData(undefined);
this.clearOptionFilter();
this.dropdown.closePanel();
}
getNextSortDirection(direction) {
if (!direction) {
return 'asc';
}
if (direction === 'asc') {
return 'desc';
}
return 'asc';
}
getDefaultDateFilterOptions() {
const opts = [
{ label: this.labels.past1Day, min: -1, max: 0 },
{ label: this.labels.past7Days, min: -7, max: 0 },
{ label: this.labels.past30Days, min: -30, max: 0 },
{ label: this.labels.past90Days, min: -90, max: 0 },
{ label: this.labels.past1Year, min: -366, max: 0 },
{ label: this.labels.next1Day, min: 0, max: 1 },
{ label: this.labels.next7Days, min: 0, max: 7 },
{ label: this.labels.next30Days, min: 0, max: 30 },
{ label: this.labels.next90Days, min: 0, max: 90 },
{ label: this.labels.next1Year, min: 0, max: 366 },
];
return opts;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDataTableCellHeader, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.NovoLabelService }, { token: DataTableState }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: NovoDataTableSortFilter, optional: true }, { token: i1.CdkColumnDef, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDataTableCellHeader, isStandalone: false, selector: "[novo-data-table-cell-config]", inputs: { defaultSort: "defaultSort", allowMultipleFilters: "allowMultipleFilters", resized: "resized", filterTemplate: "filterTemplate", column: ["novo-data-table-cell-config", "column"] }, outputs: { toggledFilter: "toggledFilter" }, host: { listeners: { "keydown": "multiSelectOptionFilterHandleKeydown($event)", "keydown.escape": "handleEscapeKeydown($event)" }, properties: { "class.resizable": "this.resizable" } }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "dropdown", first: true, predicate: NovoDropdownElement, descendants: true }, { propertyName: "optionFilterInput", first: true, predicate: ["optionFilterInput"], descendants: true }], ngImport: i0, template: `
<i class="bhi-{{ labelIcon }} label-icon" *ngIf="labelIcon" data-automation-id="novo-data-table-header-icon"></i>
<label data-automation-id="novo-data-table-label">{{ label }}</label>
<div>
<novo-sort-button
*ngIf="config.sortable"
data-automation-id="novo-data-table-sort"
tooltipPosition="left"
[tooltip]="labels.sort"
[attr.data-feature-id]="'novo-data-table-sort-' + this.id"
(sortChange)="sort()"
[value]="sortValue"></novo-sort-button>
<novo-dropdown
*ngIf="config.filterable"
side="left"
parentScrollSelector=".novo-data-table-container"
containerClass="data-table-dropdown"
data-automation-id="novo-data-table-filter"
[multiple]="multiSelect">
<novo-icon
dropdownTrigger
class="filter-button"
[class.filter-active]="filterActive"
[tooltip]="labels.filters"
tooltipPosition="right"
[attr.data-feature-id]="'novo-data-table-filter-' + this.id"
(click)="clickedFilter($event)">filter</novo-icon>
<ng-container [ngSwitch]="config.filterConfig.type">
<ng-container *ngSwitchCase="'date'" (keydown.escape)="handleEscapeKeydown($event)">
<novo-data-table-cell-filter-header [filter]="filter" (clearFilter)="clearFilter()"></novo-data-table-cell-filter-header>
<div class="optgroup-container">
<novo-optgroup>
<ng-container *ngIf="!showCustomRange">
<novo-option
[class.active]="activeDateFilter === option.label"
*ngFor="let option of config.filterConfig.options"
(click)="filterData(option)"
[attr.data-automation-id]="'novo-data-table-filter-' + option.label">
<span>{{ option.label }}</span>
<novo-icon novoSuffix color="positive" *ngIf="activeDateFilter === option.label">check</novo-icon>
</novo-option>
</ng-container>
<novo-option
[class.active]="labels.customDateRange === activeDateFilter"
(click)="toggleCustomRange($event, true)"
*ngIf="config.filterConfig.allowCustomRange && !showCustomRange">
<span>{{ labels.customDateRange }}</span>
<novo-icon novoSuffix color="positive" *ngIf="labels.customDateRange === activeDateFilter">check</novo-icon>
</novo-option>
<novo-option class="calendar-container" *ngIf="showCustomRange" keepOpen>
<novo-stack>
<div class="back-link" (click)="toggleCustomRange($event, false)">
<i class="bhi-previous"></i>
{{ labels