UNPKG

@firestitch/filter

Version:
1,533 lines (1,528 loc) 105 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, Router, 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, isString, 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]) { this.parseAndSetValue(persisted[this.name]); } } 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.model && Array.isArray(this.model)) { if (Number.isInteger(this.model[0])) { this.model = this.model.map((/** * @param {?} id * @return {?} */ (id) => { return this.values.find((/** * @param {?} value * @return {?} */ (value) => value.value === id)); })); } } } /** * @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); } /** * @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.hasPendingValues) { 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; } } } /** * @param {?} value * @return {?} */ parseAndSetValue(value) { 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); } else if (this.type === ItemType.Select || this.type === ItemType.AutoComplete) { value = +value; } } this.model = value; } /** * @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(), __metadata("design:type", Function) ], FsFilterConfigItem.prototype, "change", 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.queryParam = false; 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, "queryParam", 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 QueryParams { /** * @param {?} _router * @param {?} _route * @param {?} _filterItems */ constructor(_router, _route, _filterItems) { this._router = _router; this._route = _route; this._filterItems = _filterItems; this._queryKeys = []; this._queryParamsToFilter(this._route.snapshot.queryParams, this._filterItems); } /** * Update query with filter values * @param {?} filterParams * @return {?} */ updateQueryParams(filterParams) { // transform selected filter values to query string /** @type {?} */ const newParams = this._filterToQueryParams(filterParams, this._filterItems); // Remove empty keys this._clearKeys(newParams); // Store query keys this._queryKeys = Object.keys(newParams); // Update query this._router.navigate([], { relativeTo: this._route, queryParams: newParams, queryParamsHandling: 'merge' }).then((/** * @return {?} */ () => { })); } /** * Transformation for selected filter values to query string * @private * @param {?} params * @param {?} items * @return {?} */ _filterToQueryParams(params, items) { // selected filter keys /** @type {?} */ const filterKeys = Object.keys(params); return filterKeys.reduce((/** * @param {?} acc * @param {?} filterKey * @return {?} */ (acc, filterKey) => { // looking filter item /** @type {?} */ const filterItem = items.find((/** * @param {?} item * @return {?} */ (item) => item.name === filterKey)); if (filterItem) { if (filterItem.type === ItemType.Range) { acc[filterKey] = [params[filterKey].min, params[filterKey].max].join(','); } else if (filterItem.type === ItemType.Select && filterItem.multiple && filterItem.model.length > 0) { acc[filterKey] = filterItem.model.join(','); } else if (filterItem.type === ItemType.AutoComplete) { acc[filterKey] = [filterItem.model.value, filterItem.model.name].join(','); } else if (filterItem.type === ItemType.AutoCompleteChips) { acc[filterKey] = filterItem.model.map((/** * @param {?} item * @return {?} */ (item) => [item.value, item.name].join(','))).join(';'); } else { acc[filterKey] = params[filterKey]; } } return acc; }), {}); } /** * Parse query and update filter values * @private * @param {?} params * @param {?} items * @return {?} */ _queryParamsToFilter(params, items) { this._queryKeys = Object.keys(params); return this._queryKeys.forEach((/** * @param {?} queryKey * @return {?} */ (queryKey) => { /** @type {?} */ const filterItem = items.find((/** * @param {?} item * @return {?} */ (item) => item.name === queryKey)); if (filterItem) { if (filterItem.type === ItemType.Select && filterItem.multiple) { /** @type {?} */ const values = params[queryKey] .split(','); filterItem.parseAndSetValue(values); } else if (filterItem.type === ItemType.Range) { if (params[queryKey] && isString(params[queryKey])) { /** @type {?} */ const filterParts = params[queryKey].split(','); filterItem.model = { min: filterParts[0], max: filterParts[1] }; } } else if (filterItem.type === ItemType.Chips) { /** @type {?} */ const chipIds = params[queryKey] .split(',') .map((/** * @param {?} value * @return {?} */ (value) => +value)); filterItem.parseAndSetValue(chipIds); } else if (filterItem.type === ItemType.Checkbox) { filterItem.parseAndSetValue(params[queryKey] === 'true'); } else if (filterItem.type === ItemType.AutoComplete) { /** @type {?} */ const filterParts = params[queryKey].split(','); filterItem.model = { name: filterParts[1], value: +filterParts[0] }; } else if (filterItem.type === ItemType.AutoCompleteChips) { /** @type {?} */ const filterParts = params[queryKey].split(';'); filterItem.model = filterParts.reduce((/** * @param {?} acc * @param {?} value * @return {?} */ (acc, value) => { /** @type {?} */ const chipParts = value.split(','); acc.push({ name: chipParts[1], value: +chipParts[0], }); return acc; }), []); } else { filterItem.parseAndSetValue(params[queryKey]); } } })); } /** * @private * @param {?} params * @return {?} */ _clearKeys(params) { this._queryKeys.forEach((/** * @param {?} key * @return {?} */ (key) => { if (!params[key]) { params[key] = null; } })); } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class FilterComponent { /** * @param {?} _store * @param {?} _location * @param {?} _route * @param {?} _router */ constructor(_store, _location, _route, _router) { this._store = _store; this._location = _location; this._route = _route; this._router = _router; 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); if (this.config.queryParam) { this._queryParams = new QueryParams(this._router, this._route, this.config.items); } // 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); })); } if (this.config.init) { this.fireInitCallback(); } // 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) { wind