UNPKG

@firestitch/filter

Version:
1,559 lines (1,555 loc) 96.8 kB
import { FormsModule } from '@angular/forms'; import { MatIconModule, MatInputModule, MatSelectModule, MatChipsModule, MatAutocompleteModule, MatButtonModule, MatCheckboxModule, MAT_LABEL_GLOBAL_OPTIONS } from '@angular/material'; import { FlexLayoutModule } from '@angular/flex-layout'; import { FsDatePickerModule } from '@firestitch/datepicker'; import { FsChipModule } from '@firestitch/chip'; import { FsLabelModule } from '@firestitch/label'; import { Location, CommonModule } from '@angular/common'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { FsStore, FsStoreModule } from '@firestitch/store'; import { __decorate, __metadata } from 'tslib'; import { Alias, Model } from 'tsmodels'; import { isObservable } from 'rxjs/internal/util/isObservable'; import { isDate, isValid, parse, format, isAfter, subMinutes } from 'date-fns'; import { Subject } from 'rxjs'; import { isEmpty, list, filter, remove, FsCommonModule } from '@firestitch/common'; import { isFunction, isObject, toString, clone, cloneDeep } from 'lodash-es'; import { take, takeUntil, debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; import { Component, EventEmitter, Input, ViewChild, ViewEncapsulation, HostListener, ChangeDetectionStrategy, ChangeDetectorRef, IterableDiffers, Output, KeyValueDiffers, Pipe, NgModule } from '@angular/core'; import { toUTC, simpleFormat, format as format$1 } from '@firestitch/date'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @enum {string} */ const ItemType = { Text: 'text', Select: 'select', Range: 'range', Date: 'date', DateTime: 'datetime', DateRange: 'daterange', DateTimeRange: 'datetimerange', AutoComplete: 'autocomplete', AutoCompleteChips: 'autocompletechips', Checkbox: 'checkbox', Chips: 'chips', }; class FsFilterConfigItem extends Model { /** * @param {?=} data * @param {?=} _config * @param {?=} _route * @param {?=} _persists */ constructor(data = {}, _config, _route, _persists) { super(); this._config = _config; this._route = _route; this._persists = _persists; this.initialLoading = false; this.valueChanged = false; this._pendingValues = false; this._fromJSON(data); } /** * @return {?} */ get hasPendingValues() { return this._pendingValues; } /** * @return {?} */ get model() { return this._model; } /** * @param {?} val * @return {?} */ set model(val) { this._model = val; this._tmpModel = val; this.checkIfValueChanged(); } /** * @return {?} */ get tmpModel() { return this._tmpModel; } /** * @param {?} val * @return {?} */ set tmpModel(val) { this._tmpModel = val; } /** * @param {?} data * @return {?} */ _fromJSON(data) { super._fromJSON(data); if (this.name && isObject(this.name)) { this.names = this.name; this.name = Object.keys(this.names).join('-'); } if (this._config.persist) { /** @type {?} */ const persisted = this._persists[this._config.persist.name].data; if (persisted[this.name]) { /** @type {?} */ let value = persisted[this.name]; if (value) { if (this.type === ItemType.DateRange || this.type === ItemType.DateTimeRange) { value.from = value.from ? toUTC(value.from) : null; value.to = value.to ? toUTC(value.to) : null; } else if (this.type === ItemType.Date || this.type === ItemType.DateTime) { if (!isDate(value) || !isValid(value)) { value = parse(value, 'yyyy-MM-dd\'T\'HH:mm:ssxxxxx', new Date()); } } else if (this.type === ItemType.Checkbox && this.checked !== undefined) { value = value == this.checked; } else if (this.type === ItemType.Select && this.multiple) { value = clone(value); } } this.model = value; } } this.applyDataFromQuery(); if (isFunction(data.values) && this.type !== ItemType.AutoComplete && this.type !== ItemType.AutoCompleteChips) { this.values = data.values(); if (isObservable(this.values)) { this._pendingValues = true; } else { /** @type {?} */ const values = Array.isArray(this.values) ? ((/** @type {?} */ (this.values))).slice() : this.values; this.sanitizeItem(values); } } else { /** @type {?} */ const values = Array.isArray(data.values) ? data.values.slice() : data.values; this.sanitizeItem(values); } } /** * @param {?} values * @return {?} */ sanitizeItem(values) { switch (this.type) { case ItemType.Text: break; case ItemType.Select: { this.sanitizeSelectItem(values); } break; case ItemType.Chips: { this.sanitizeChipsItem(values); } break; case ItemType.Range: { this.sanitizeRange(); } break; case ItemType.Checkbox: { this.sanitizeCheckbox(); } break; } if (this.model === undefined) { if (this.type == 'checkbox') { this.model = this.checked == this.defaultValue; } else { this.model = this.defaultValue; } } if (this.model === undefined) { if (this.type == 'checkbox') { this.model = false; } else if (this.type == 'select') { if (this.multiple) { if (!Array.isArray(this.defaultValue)) { this.model = []; } } else { if (this.defaultValue === undefined) { this.model = '__all'; } } } else if (this.type == ItemType.AutoCompleteChips || this.type == ItemType.Chips) { this.model = []; } } } /** * @param {?} values * @return {?} */ sanitizeSelectItem(values) { this.values = values; this.groups = null; // let data = []; // if (this.nested) { // // generate a list of values from objects that have not been nested. // if (!this.multiple) { // data.push({value: '__all', name: 'All', depth: 0}); // } // // Array.prototype.push.apply(data, this.walkSelectNestedValues(filter, null, this.values)); // } else { // // data = this.walkSelectValues(filter, this.values); // } // this.values = data; if (this.isolate) { for (const index in this.values) { if (this.values.hasOwnProperty(index)) { if (!this.values[index]) { continue; } if (this.values[index].value == this.isolate.value) { this.values.splice(index, 1); } } } if (Array.isArray(this.model)) { if (this.model.length == this.values.length) { this.model = null; this.isolate.enabled = false; } else if (this.model[0] == this.isolate.value) { this.isolate.enabled = true; } } } } /** * @param {?} values * @return {?} */ sanitizeChipsItem(values) { this.values = values; this.groups = null; // if (this.isolate) { // for (const index in this.values) { // if (this.values.hasOwnProperty(index)) { // if (!this.values[index]) { // continue; // } // // if (this.values[index].value == this.isolate.value) { // this.values.splice(index, 1); // } // } // } // // if (Array.isArray(this.model)) { // if (this.model.length == this.values.length) { // this.model = null; // this.isolate.enabled = false; // } else if (this.model[0] == this.isolate.value) { // this.isolate.enabled = true; // } // } // } // // for (const value of this.values) { // // if (value.group) { // // if (!this.groups) { // this.groups = {}; // } // // if (!this.groups[value.group]) { // this.groups[value.group] = []; // } // // this.groups[value.group].push(value); // } // } } /** * @return {?} */ sanitizeCheckbox() { this.checked = this.checked ? toString(this.checked) : true; this.unchecked = this.unchecked ? toString(this.unchecked) : false; this.defaultValue = this.defaultValue === undefined ? this.unchecked : toString(this.defaultValue); } /** * @return {?} */ applyDataFromQuery() { if (this.query) { /** @type {?} */ let query = this._route.snapshot.queryParams[this.query]; if (query !== undefined) { query += ''; this.model = query; if (!query.length) { this.model = undefined; } else if (this.type == 'select' && this.multiple) { this.model = this.model.split(','); } else if (this.type == 'daterange' || this.type == 'datetimerange') { /** @type {?} */ const parts = this.model.split(','); this.model = { from: parts[0], to: parts[1] }; } else if (this.type == 'range') { /** @type {?} */ const parts = this.model.split(','); this.model = { min: parts[0], max: parts[1] }; } } } } /** * @param {?} value * @return {?} */ updateValue(value) { switch (this.type) { case ItemType.Select: { if (value === '__all' || value === null) { this.model = value; return; } /** @type {?} */ let valueExists = null; /** @type {?} */ let isolated = null; if (this.multiple) { isolated = this.isolate && Array.isArray(value) && value[0] === this.isolate.value; valueExists = Array.isArray(this.values) ? value.every((/** * @param {?} val * @return {?} */ (val) => { return this.values.find((/** * @param {?} valueItem * @return {?} */ (valueItem) => valueItem.value === val)); })) || isolated : false; } else { valueExists = Array.isArray(this.values) ? this.values.some((/** * @param {?} valueItem * @return {?} */ (valueItem) => valueItem.value === value)) : false; } if (valueExists) { this.model = value; if (this.isolate) { this.isolate.enabled = isolated; } return; } } break; case ItemType.Range: { this.model = isObject(value) ? Object.assign({}, this.model, value) : {}; } break; case ItemType.Chips: { this.model = []; } break; case ItemType.Date: case ItemType.DateTime: { this.model = value; } break; case ItemType.AutoCompleteChips: { if (Array.isArray(value)) { this.model.push(...value); } else if (isObject(value)) { this.model.push(value); } else { this.model = []; } } break; default: { this.model = value; } } } /** * @return {?} */ loadRemoteValues() { if (!this.initialLoading && this._pendingValues) { this.initialLoading = true; this.values .pipe(take(1), takeUntil(this._config.destroy$)) .subscribe((/** * @param {?} values * @return {?} */ (values) => { this._pendingValues = false; this.sanitizeItem(values); this.initialLoading = false; })); } } /** * @return {?} */ clear() { this.valueChanged = false; this.model = undefined; switch (this.type) { case ItemType.AutoComplete: { this.model = null; this.tmpModel = null; this.search = ''; } break; case ItemType.AutoCompleteChips: case ItemType.Chips: { this.model = []; this.tmpModel = []; this.search = ''; } break; case ItemType.Checkbox: { this.model = false; this.tmpModel = false; } break; case ItemType.Select: { if (this.multiple) { this.model = []; this.tmpModel = []; } else { this.model = Array.isArray(this.values) && this.values.some((/** * @param {?} val * @return {?} */ (val) => val.value === '__all')) ? '__all' : null; this.tmpModel = this.model; } if (this.isolate) { this.isolate.enabled = false; } } break; case ItemType.Range: { this.model = {}; this.tmpModel = {}; } break; case ItemType.Text: { this.model = ''; this.tmpModel = ''; } break; case ItemType.Date: case ItemType.DateTime: { this.model = null; this.tmpModel = null; } break; } } /** * @return {?} */ checkIfValueChanged() { switch (this.type) { case ItemType.AutoCompleteChips: { this.valueChanged = this.model && this.model.length; } break; case ItemType.Checkbox: { this.valueChanged = this.model && this.model !== false; } break; case ItemType.Select: { if (this.multiple) { this.valueChanged = this.model && this.model.length; } else { /** @type {?} */ const hasAllOption = Array.isArray(this.values) && this.values.some((/** * @param {?} val * @return {?} */ (val) => val.value === '__all')); if (hasAllOption && this.model && this.model !== '__all') { this.valueChanged = true; } else { this.valueChanged = !!this.model; } } } break; case ItemType.Range: { if (this.model && Object.keys(this.model).length > 0) { this.valueChanged = true; } } break; case ItemType.Text: { this.valueChanged = this.model && this.model !== ''; } break; case ItemType.AutoComplete: case ItemType.Date: case ItemType.DateTime: { this.valueChanged = !!this.model; } break; default: { this.valueChanged = false; } } } /** * @private * @return {?} */ sanitizeRange() { if (!this.placeholder) { this.placeholder = ['Min', 'Max']; } if (!this.model) { this.model = {}; } } } __decorate([ Alias(), __metadata("design:type", String) ], FsFilterConfigItem.prototype, "name", void 0); __decorate([ Alias(), __metadata("design:type", String) ], FsFilterConfigItem.prototype, "type", void 0); __decorate([ Alias(), __metadata("design:type", String) ], FsFilterConfigItem.prototype, "label", void 0); __decorate([ Alias(), __metadata("design:type", String) ], FsFilterConfigItem.prototype, "chipLabel", void 0); __decorate([ Alias(), __metadata("design:type", String) ], FsFilterConfigItem.prototype, "children", void 0); __decorate([ Alias(), __metadata("design:type", Boolean) ], FsFilterConfigItem.prototype, "multiple", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "groups", void 0); __decorate([ Alias(), __metadata("design:type", Boolean) ], FsFilterConfigItem.prototype, "wait", void 0); __decorate([ Alias(), __metadata("design:type", String) ], FsFilterConfigItem.prototype, "query", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "values", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "values$", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "selectedValue", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "isolate", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "names", void 0); __decorate([ Alias(), __metadata("design:type", Boolean) ], FsFilterConfigItem.prototype, "primary", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "search", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "unchecked", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "checked", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "alias", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "placeholder", void 0); __decorate([ Alias('default'), __metadata("design:type", Object) ], FsFilterConfigItem.prototype, "defaultValue", void 0); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const SORT_BY_FIELD = 'system_sort_by'; /** @type {?} */ const SORT_DIRECTION_FIELD = 'system_sort_direction'; class FsFilterConfig extends Model { /** * @param {?=} data */ constructor(data = {}) { super(); this.load = true; this.persist = false; this.inline = false; this.autofocus = false; this.chips = false; this.sortValues = null; this.sort = null; this.sortDirection = null; this.namespace = 'filter'; this.items = []; this.sortByItem = null; this.sortDirectionItem = null; this.searchInput = null; this.singleTextFilter = false; this._filtersNames = []; this._destroy$ = new Subject(); this._fromJSON(data); } /** * @return {?} */ get destroy$() { return this._destroy$.asObservable(); } /** * @param {?} items * @param {?} route * @param {?} persists * @return {?} */ initItems(items, route, persists) { if (items && Array.isArray(items)) { this.items = items.map((/** * @param {?} item * @return {?} */ (item) => { if (item && item.name && this._filtersNames.indexOf(item.name) === -1) { this._filtersNames.push(item.name); return new FsFilterConfigItem(item, this, route, persists); } else { throw Error('Filter init error. Items name must be uniq.'); } })); } this.initSorting(route, persists); this.searchInput = this.items.find((/** * @param {?} item * @return {?} */ (item) => item.type === ItemType.Text)); if (this.items.length === 1 && this.items[0].type === ItemType.Text) { this.singleTextFilter = true; } } /** * @param {?} route * @param {?} persists * @return {?} */ initSorting(route, persists) { if (this.sortValues) { /** @type {?} */ const sortByItem = { name: SORT_BY_FIELD, type: ItemType.Select, label: 'Sort By', values: this.sortValues }; if (this.sort && this.sort.value) { sortByItem['default'] = this.sort.value; } this.sortByItem = new FsFilterConfigItem(sortByItem, this, route, persists); /** @type {?} */ const sortDirectionItem = { name: SORT_DIRECTION_FIELD, type: ItemType.Select, label: 'Sort Direction', values: [ { name: 'Ascending', value: 'asc' }, { name: 'Descending', value: 'desc' } ] }; if (this.sort && this.sort.direction) { sortDirectionItem['default'] = this.sort.direction; } this.sortDirectionItem = new FsFilterConfigItem(sortDirectionItem, this, route, persists); } } /** * @return {?} */ updateModelValues() { this.items.forEach((/** * @param {?} filter * @return {?} */ (filter$$1) => { filter$$1.model = clone(filter$$1.tmpModel); })); if (this.sortByItem) { this.sortByItem.model = clone(this.sortByItem.tmpModel); } if (this.sortDirectionItem) { this.sortDirectionItem.model = clone(this.sortDirectionItem.tmpModel); } } /** * @param {?=} opts * @return {?} */ gets(opts = {}) { /** @type {?} */ const query = {}; for (const filter$$1 of this.items) { /** @type {?} */ let value = clone(filter$$1.model); if (filter$$1.type == ItemType.Select) { if (filter$$1.multiple) { if (filter$$1.isolate) { if (!Array.isArray(filter$$1.model) || !filter$$1.model.length) { value = list(filter$$1.values, 'value'); } } if (filter$$1.model && filter$$1.model.indexOf('__all') > -1) { value = null; } } else { if (filter$$1.isolate) { if (filter$$1.model == '__all') { value = list(filter$$1.values, 'value'); } } else { if (filter$$1.model == '__all') { value = null; } } } } else if (filter$$1.type == ItemType.AutoCompleteChips || filter$$1.type === ItemType.Chips) { if (Array.isArray(filter$$1.model) && filter$$1.model.length && !opts.expand) { value = list(filter$$1.model, 'value'); } } else if (filter$$1.type == ItemType.Checkbox) { value = filter$$1.model ? filter$$1.checked : filter$$1.unchecked; } // @TODO if (isEmpty(value, { zero: true })) { continue; } if (filter$$1.type == ItemType.Date || filter$$1.type == ItemType.DateTime) { if (value && isValid(value) && isDate(value)) { value = simpleFormat(value); } } else if (filter$$1.type == ItemType.DateRange || filter$$1.type == ItemType.DateTimeRange) { /** @type {?} */ const from = value.from; /** @type {?} */ const to = value.to; value = {}; if (from) { value.from = format(from, 'yyyy-MM-dd\THH:mm:ssxxxxx'); } if (to) { value.to = format(to, 'yyyy-MM-dd\THH:mm:ssxxxxx'); } } else if (filter$$1.type == ItemType.AutoComplete) { if (isEmpty(filter$$1.model.value, { zero: true })) { continue; } value = opts.expand ? filter$$1.model : filter$$1.model.value; } if (isObject(filter$$1.names) && opts.names !== false) { for (const key in filter$$1.names) { if (value[filter$$1.names[key]]) { query[key] = value[filter$$1.names[key]]; } } } else { query[filter$$1.name] = value; } } if (opts.flatten) { for (const name in query) { if (Array.isArray(query[name])) { query[name] = query[name].join(','); } } } return query; } /** * @return {?} */ getSort() { /** @type {?} */ let sortBy = this.getSortByValue(); sortBy = sortBy === '__all' ? null : sortBy; /** @type {?} */ let sortDirection = this.getSortDirectionValue(); sortDirection = sortDirection === '__all' ? null : sortDirection; return { value: sortBy, direction: sortDirection, }; } /** * @return {?} */ getSortByValue() { return this.sortByItem ? this.sortByItem.model : null; } /** * @return {?} */ getSortDirectionValue() { return this.sortDirectionItem ? this.sortDirectionItem.model : null; } /** * @param {?} sort * @return {?} */ updateSort(sort) { if (sort.sortBy) { this.sortByItem.model = sort.sortBy; } if (sort.sortDirection) { this.sortDirectionItem.model = sort.sortDirection; } } /** * @return {?} */ getFilledItems() { return this.items.reduce((/** * @param {?} acc * @param {?} filter * @return {?} */ (acc, filter$$1) => { switch (filter$$1.type) { case ItemType.Select: { /** @type {?} */ const multipleIsoldated = filter$$1.multiple && filter$$1.isolate && Array.isArray(filter$$1.model) && !!filter$$1.model.length && filter$$1.model.indexOf('__all') === -1; /** @type {?} */ const multipleHasSelectedValues = filter$$1.multiple && Array.isArray(filter$$1.model) && filter$$1.model.length && filter$$1.model.indexOf('__all') === -1; /** @type {?} */ const selectedValues = !filter$$1.multiple && filter$$1.model && filter$$1.model !== '__all'; if (multipleIsoldated || multipleHasSelectedValues || selectedValues) { acc.push(filter$$1); } } break; case ItemType.AutoCompleteChips: { if (Array.isArray(filter$$1.model) && filter$$1.model.length) { acc.push(filter$$1); } } break; case ItemType.Checkbox: { if (filter$$1.model) { acc.push(filter$$1); } } break; case ItemType.DateRange: case ItemType.DateTimeRange: { if (filter$$1.model.from || filter$$1.model.to) { acc.push(filter$$1); } } break; default: { if (filter$$1.model && filter$$1 !== this.searchInput && (!isEmpty(filter$$1.model, { zero: true }) || !isEmpty(filter$$1.model.value, { zero: true }))) { acc.push(filter$$1); } } } return acc; }), []); } /** * @return {?} */ filtersClear() { for (const filter$$1 of this.items) { filter$$1.clear(); } if (this.sortByItem) { if (this.sort) { this.sortByItem.model = this.sort.value; } else { this.sortByItem.clear(); } } if (this.sortDirectionItem) { if (this.sort) { this.sortDirectionItem.model = this.sort.direction; } else { this.sortDirectionItem.clear(); } } } /** * @return {?} */ loadValuesForPendingItems() { this.items .filter((/** * @param {?} item * @return {?} */ (item) => item.hasPendingValues)) .forEach((/** * @param {?} item * @return {?} */ (item) => item.loadRemoteValues())); } /** * @return {?} */ destroy() { this._destroy$.next(); this._destroy$.complete(); } } __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "load", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "persist", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "inline", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "autofocus", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "chips", void 0); __decorate([ Alias('sorts'), __metadata("design:type", Array) ], FsFilterConfig.prototype, "sortValues", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "sort", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "sortDirection", void 0); __decorate([ Alias(), __metadata("design:type", Object) ], FsFilterConfig.prototype, "namespace", void 0); __decorate([ Alias(), __metadata("design:type", Function) ], FsFilterConfig.prototype, "init", void 0); __decorate([ Alias(), __metadata("design:type", Function) ], FsFilterConfig.prototype, "change", void 0); __decorate([ Alias(), __metadata("design:type", Function) ], FsFilterConfig.prototype, "reload", void 0); __decorate([ Alias(), __metadata("design:type", Function) ], FsFilterConfig.prototype, "sortChange", void 0); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} obj1 * @param {?} obj2 * @return {?} */ function objectsAreEquals(obj1, obj2) { /** @type {?} */ const oldKeys = Object.keys(obj1); /** @type {?} */ const currKeys = Object.keys(obj2); if (oldKeys.length !== currKeys.length) { return false; } for (const key in obj1) { if (obj1.hasOwnProperty(key)) { /** @type {?} */ const oldItem = obj1[key]; /** @type {?} */ const currItem = obj2[key]; /** @type {?} */ const isArrays = Array.isArray(oldItem) && Array.isArray(currItem); /** @type {?} */ const isObjects = isObject(oldItem) && isObject(currItem); if (isArrays && !arraysAreEquals(oldItem, currItem)) { return false; } else if (isObjects && !objectsAreEquals(oldItem, currItem)) { return false; } else if (!isArrays && !isObjects && oldItem !== currItem) { return false; } } } return true; } /** * @param {?} arr1 * @param {?} arr2 * @return {?} */ function arraysAreEquals(arr1, arr2) { if (arr1.length !== arr2.length) { return false; } for (const el of arr1) { if (arr2.indexOf(el) === -1) { return false; } } return true; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class FilterComponent { /** * @param {?} _store * @param {?} route * @param {?} location */ constructor(_store, route, location) { this._store = _store; this.route = route; this.location = location; this.filter = null; this.sortUpdate = null; this.showSortBy = true; this.showFilterInput = true; this.changedFilters = []; this.searchText = ''; this.persists = null; this.activeFiltersCount = 0; this.activeFiltersWithInputCount = 0; this.showFilterMenu = false; this.modelChanged = new EventEmitter(); this._searchTextInput = null; this._firstOpen = true; this._query = {}; this._sort = {}; } /** * @param {?} event * @return {?} */ keyEvent(event) { if (event.code === 'Escape' && this.showFilterMenu) { this.changeVisibility(false); } } /** * @param {?} value * @return {?} */ set searchTextInput(value) { this._searchTextInput = value; } /** * @return {?} */ ngOnInit() { this.config = new FsFilterConfig(this.filter); this.restorePersistValues(); this.config.initItems(this.filter.items, this.route, this.persists); // Set search input value after restore from STORE this.updateSearchText(); // Count active filters after restore this.updateFilledCounter(); this.watchSearchInput(); if (this.sortUpdate) { this.sortUpdate .pipe(takeUntil(this.config.destroy$)) .subscribe((/** * @param {?} data * @return {?} */ (data) => { this.config.updateSort(data); })); } this._query = this.config.gets({ flatten: true }); this._sort = this.config.getSort(); if (this.config.init) { this.config.init(this._query, this.config.getSort()); } // Avoid ngChanges error setTimeout((/** * @return {?} */ () => { if (this._searchTextInput && this.config.autofocus) { this._searchTextInput.nativeElement.focus(); } })); } /** * @return {?} */ ngOnDestroy() { if (this.config) { this.config.destroy(); } } /** * * Do update value of some field * * * To update text value just pass new text value * * public updateSelectValue(val) { * this.filterEl.updateValues({ keyword: val }); * } * * To update select or observable select you could pass suitable value * * public updateSelectValue(val: number) { * this.filterEl.updateValues({ simple_select: val }, { observable_select: val }); * } * * To update checkbox value just pass true/false as value * * public updateCheckox(val: boolean) { * this.filterEl.updateValues({ checkbox: val }); * } * * To update range value just pass object with min&max object or just with one of targets * * Ex.: { min: 10, max 15 }, { min: 5 }, { max 5 } * * public updateRange(val) { * this.filterEl.updateValues({ range: val }); * } * * To update autocomplete just pass object with name/value fields * * Ex.: { name: 'John Doe', value: 1 } * * public updateAutocomplete(val) { * this.filterEl.updateValues({ autocomplete_user_id: val }); * } * * To update autocompletechips just pass: * * 1) object with name/value fields - will be appended to existing set of values * * { name: 'John Doe', value: 1 } * * 2) array of objects - will be appended to existing set of values * * [{ name: 'John Doe', value: 1 }, { name: 'Darya Filipova', value: 2 }] * * 3) null - clear existing set of values * * public updateAutocomplete(val) { * this.filterEl.updateValues({ autocompletechips_user_id: val }); * } * * @param {?} values * @param {?=} changeEvent * @return {?} */ updateValues(values, changeEvent = true) { Object.keys(values).forEach((/** * @param {?} key * @return {?} */ (key) => { /** @type {?} */ const filterItem = this.config.items.find((/** * @param {?} item * @return {?} */ (item) => item.name === key)); if (!filterItem) { return; } filterItem.updateValue(values[key]); if (filterItem === this.config.searchInput) { this.updateSearchText(); } })); this.updateFilledCounter(); if (changeEvent) { this.filterChange(); } } /** * @return {?} */ watchSearchInput() { this.modelChanged .pipe(distinctUntilChanged(), debounceTime(500), takeUntil(this.config.destroy$)) .subscribe((/** * @param {?} value * @return {?} */ (value) => { this.config.searchInput.model = value; this.filterChange(); })); } /** * @param {?} text * @return {?} */ modelChange(text) { this.modelChanged.next(text); } /** * @param {?} event * @return {?} */ backdropClick(event) { this.switchFilterVisibility(event); } /** * @return {?} */ done() { this.changeVisibility(false); } /** * @param {?=} event * @return {?} */ switchFilterVisibility(event = null) { if (event) { event.stopPropagation(); } this.changeVisibility(!this.showFilterMenu); } /** * @param {?} event * @return {?} */ filterInputClick(event) { if (['Enter', 'NumpadEnter', 'Escape'].indexOf(event.code) >= 0) { return this.changeVisibility(false); } this.changeVisibility(true); } /** * @param {?} state * @return {?} */ changeVisibility(state) { this.showFilterMenu = state; if (this._firstOpen) { this.config.loadValuesForPendingItems(); this._firstOpen = false; } if (this.showFilterMenu) { window.document.body.classList.add('fs-filter-open'); } else { window.document.body.classList.remove('fs-filter-open'); this.updateFilledCounter(); } } /** * @param {?=} event * @return {?} */ clear(event = null) { if (event) { event.stopPropagation(); } if (this.config.searchInput) { this.config.searchInput.model = ''; this.modelChange(this.config.searchInput.model); } this.searchText = ''; this.changedFilters = []; this.config.filtersClear(); this.activeFiltersCount = 0; this.activeFiltersWithInputCount = 0; this.filterChange(); this.changeVisibility(false); } /** * Close filter window and do change callback * @return {?} */ search() { this.switchFilterVisibility(); this.filterChange(); } /** * Call change callback and apply new filter values * @return {?} */ change() { this.config.updateModelValues(); /** @type {?} */ const query = this.config.gets({ flatten: true }); /** @type {?} */ const sort = this.config.getSort(); /** @type {?} */ const queryChanged = !objectsAreEquals(this._query, query); if (queryChanged) { this._query = query; this.storePersistValues(); this.updateFilledCounter(); if (this.config.change) { this.config.change(cloneDeep(query), sort); } } /** @type {?} */ const sortingChanged = ((!sort || !this._sort) && sort !== this._sort) || (sort && this._sort && !objectsAreEquals(this._sort, sort)); if (sortingChanged) { this._sort = sort; if (this.config.sortChange) { this.config.sortChange(cloneDeep(query), sort); } } } /** * Do update count of filled filters * @return {?} */ updateFilledCounter() { this.changedFilters = this.config.getFilledItems(); this.changedFilters .filter((/** * @param {?} item * @return {?} */ (item) => item.hasPendingValues)) .forEach((/** * @param {?} item * @return {?} */ (item) => item.loadRemoteValues())); this.activeFiltersWithInputCount = (this.searchText !== '') ? this.changedFilters.length + 1 : this.changedFilters.length; } /** * Store updated filter data into localstorage * @param {?=} changedItem * @return {?} */ filterChange(changedItem = null) { if (changedItem) { if (changedItem === this.config.searchInput) { this.searchText = changedItem.model; } changedItem.checkIfValueChanged(); } this.storePersistValues(); this.change(); } /** * Just reload with same values * @param {?} event * @return {?} */ reload(event) { event.stopPropagation(); /** @type {?} */ const query = this.config.gets({ flatten: true }); if (this.config.reload) { this.config.reload(cloneDeep(query), this.config.getSort()); } } /** * Restoring values from local storage * @return {?} */ restorePersistValues() { this.persists = this._store.get(this.config.namespace + '-persist', {}); if (this.persists === undefined) { this.persists = {}; } if (this.config.persist) { if (typeof this.config.persist.persist !== 'object') { this.config.persist = { name: this.config.persist }; } if (!this.config.persist.name) { this.config.persist.name = this.location.path(); } if (!this.persists[this.config.persist.name] || !this.persists[this.config.persist.name].data) { this.persists[this.config.persist.name] = { data: {}, date: new Date() }; } if (this.config.persist.timeout) { /** @type {?} */ const date = new Date(this.persists[this.config.persist.name].date); if (isAfter(subMinutes(date, this.config.persist.timeout), new Date())) { this.persists[this.config.persist.name] = { data: {}, date: new Date() }; } } } } /** * Store values to local storage * @return {?} */ storePersistValues() { if (this.config.persist) { this.persists[this.config.persist.name] = { data: this.config.gets({ expand: true, names: false }), date: new Date() }; this._store.set(this.config.namespace + '-persist', this.persists, {}); } } /** * Reset filter * @param {?} item * @return {?} */ resetFilter(item) { /** @type {?} */ const index = this.changedFilters.indexOf(item); if (index > -1) { this.changedFilters.splice(index, 1); item.clear(); } this.change(); } /** * @private * @return {?} */ updateSearchText() { if (this.config.searchInput && this.config.searchInput.model) { this.searchText = this.config.searchInput.model; } } } FilterComponent.decorators = [ { type: Component, args: [{ selector: 'fs-filter', template: "<div class=\"fs-filter\"\n *ngIf=\"config?.items?.length\"\n [ngClass]=\"{\n 'filters-open': showFilterMenu,\n 'no-input': !showFilterInput\n }\">\n <div fxLayou=\"row\" fxLayoutAlign=\"start center\" class=\"menu-filter\" fxFlex=\"1 1 0\">\n <div class=\"menu-filter-input\" fxFlex=\"grow\">\n <div class=\"main-filter-bar\" fxLayout=\"row\" fxLayoutAlign=\"start center\">\n <form autocomplete=\"off\" role=\"presentation\">\n <mat-form-field\n class=\"filter-input-field\"\n (click)=\"changeVisibility(true)\"\n floatLabel=\"never\">\n <span matPrefix><mat-icon matPrefix>search</mat-icon></span>\n\n <input matInput\n [(ngModel)]=\"searchText\"\n name=\"filter-input\"\n #searchTextInput\n (keydown)=\"filterInputClick($event)\"\n class=\"filter-input\"\n (ngModelChange)=\"modelChange(searchText)\">\n\n <mat-placeholder>Search</mat-placeholder>\n <a matSuffix\