@firestitch/filter 18.2.71 → 18.2.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, Directive, Input, TemplateRef, DestroyRef, Component, ChangeDetectionStrategy, InjectionToken, KeyValueDiffers, ChangeDetectorRef, Injector, ViewChild, EventEmitter, NgZone, ElementRef, Output, ContentChild, HostBinding, NgModule } from '@angular/core';
2
+ import { inject, Injectable, Directive, Input, TemplateRef, DestroyRef, Component, ChangeDetectionStrategy, InjectionToken, KeyValueDiffers, ChangeDetectorRef, Injector, ViewChild, ElementRef, EventEmitter, NgZone, Output, ContentChild, HostBinding, NgModule } from '@angular/core';
3
3
  import { FsMessage } from '@firestitch/message';
4
4
  import { FsPrompt } from '@firestitch/prompt';
5
5
  import { BehaviorSubject, Subject, of, forkJoin, Observable, tap as tap$1, map as map$1, switchMap as switchMap$1, distinctUntilChanged as distinctUntilChanged$1, merge, filter, takeUntil as takeUntil$1, debounceTime as debounceTime$1, fromEvent, combineLatest, interval } from 'rxjs';
@@ -50,7 +50,7 @@ import { FsChipModule } from '@firestitch/chip';
50
50
  import { FsSkeletonModule } from '@firestitch/skeleton';
51
51
  import * as i1$4 from '@firestitch/label';
52
52
  import { FsLabelModule } from '@firestitch/label';
53
- import { MatFormField, MatLabel, MatPrefix, MatSuffix, MatHint } from '@angular/material/form-field';
53
+ import { MatFormField, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';
54
54
  import { MatCheckbox } from '@angular/material/checkbox';
55
55
 
56
56
  class SavedFilterController {
@@ -792,6 +792,7 @@ class FsFilterConfig {
792
792
  namespace; // for persistance
793
793
  heading;
794
794
  subheading;
795
+ maxEnabled;
795
796
  constructor(data = {}) {
796
797
  this._init(data);
797
798
  }
@@ -802,6 +803,7 @@ class FsFilterConfig {
802
803
  savedFilters: data.savedFilters,
803
804
  autofocus: data.autofocus ?? false,
804
805
  chips: data.chips ?? false,
806
+ maxEnabled: data.maxEnabled ?? 0,
805
807
  sort: data.sort,
806
808
  queryParam: data.queryParam ?? false,
807
809
  init: data.init,
@@ -1002,6 +1004,7 @@ class BaseItem {
1002
1004
  persistanceDisabled;
1003
1005
  queryParamsDisabled;
1004
1006
  primary;
1007
+ secondary;
1005
1008
  changeCallback;
1006
1009
  initCallback;
1007
1010
  _type;
@@ -1009,12 +1012,25 @@ class BaseItem {
1009
1012
  _hidden$ = new BehaviorSubject(false);
1010
1013
  _value$ = new BehaviorSubject({ value: undefined, emitChange: true });
1011
1014
  _values$ = new BehaviorSubject(null);
1015
+ _secondaryVisible$ = new BehaviorSubject(false);
1012
1016
  _destroy$ = new Subject();
1013
1017
  constructor(itemConfig, _filter) {
1014
1018
  this._filter = _filter;
1015
1019
  this._type = itemConfig.type;
1016
1020
  this._initConfig(itemConfig);
1017
1021
  }
1022
+ secondaryShow() {
1023
+ this._secondaryVisible$.next(true);
1024
+ }
1025
+ secondaryHide() {
1026
+ this._secondaryVisible$.next(false);
1027
+ }
1028
+ get secondaryVisible$() {
1029
+ return this._secondaryVisible$.asObservable();
1030
+ }
1031
+ get secondaryVisible() {
1032
+ return this._secondaryVisible$.getValue();
1033
+ }
1018
1034
  get filter() {
1019
1035
  return this._filter;
1020
1036
  }
@@ -1079,6 +1095,10 @@ class BaseItem {
1079
1095
  get hasValue() {
1080
1096
  return this.value !== null && this.value !== undefined;
1081
1097
  }
1098
+ get notValue$() {
1099
+ return this.value$
1100
+ .pipe(map(() => !this.hasValue));
1101
+ }
1082
1102
  get hasValue$() {
1083
1103
  return this.value$
1084
1104
  .pipe(map(() => this.hasValue));
@@ -1122,9 +1142,15 @@ class BaseItem {
1122
1142
  return this.query;
1123
1143
  }
1124
1144
  hide() {
1145
+ if (!this.primary) {
1146
+ this.secondaryHide();
1147
+ }
1125
1148
  this._hidden$.next(true);
1126
1149
  }
1127
1150
  show() {
1151
+ if (!this.primary) {
1152
+ this.secondaryShow();
1153
+ }
1128
1154
  this._hidden$.next(false);
1129
1155
  }
1130
1156
  get query() {
@@ -1190,12 +1216,13 @@ class BaseItem {
1190
1216
  }
1191
1217
  }
1192
1218
  _initConfig(item) {
1193
- const hidden = item.hide ?? !(item.show ?? true);
1194
1219
  this.name = item.name;
1195
1220
  this.label = item.label;
1196
- this.primary = item.primary ?? false;
1221
+ this.primary = this.isTypeKeyword || (item.primary ?? false);
1222
+ this.secondary = item.secondary ?? false;
1197
1223
  this.chipLabel = item.chipLabel;
1198
- this._hidden$.next(hidden);
1224
+ this._hidden$.next(item.hide ?? !(item.show ?? true));
1225
+ this._secondaryVisible$.next(item.secondary ?? false);
1199
1226
  this.clearable = item.clear ?? true;
1200
1227
  this.persistanceDisabled = item.disablePersist ?? false;
1201
1228
  this.queryParamsDisabled = item.disableQueryParams ?? false;
@@ -2260,7 +2287,6 @@ class FilterController {
2260
2287
  _ready$ = new BehaviorSubject(false);
2261
2288
  _items = new Map();
2262
2289
  _config;
2263
- _add$ = new Subject();
2264
2290
  _init$ = new Subject();
2265
2291
  _change$ = new Subject();
2266
2292
  _destroy$ = new Subject();
@@ -2281,9 +2307,6 @@ class FilterController {
2281
2307
  get init$() {
2282
2308
  return this._init$.asObservable();
2283
2309
  }
2284
- get add$() {
2285
- return this._add$.asObservable();
2286
- }
2287
2310
  get change$() {
2288
2311
  return this._change$
2289
2312
  .pipe(debounceTime(30));
@@ -2404,13 +2427,38 @@ class FilterController {
2404
2427
  .map((item) => {
2405
2428
  return item.init(values[item.name]);
2406
2429
  }))
2407
- .pipe(tap(() => this.items.forEach((item) => {
2430
+ .pipe(tap(() => {
2431
+ this._initEnabledItems();
2432
+ }), tap(() => this.items
2433
+ .forEach((item) => {
2408
2434
  item.initCallback(item, this.filter);
2409
2435
  })));
2410
2436
  }
2437
+ _initEnabledItems() {
2438
+ let enabled = 0;
2439
+ this.items
2440
+ .forEach((item) => {
2441
+ if (!item.primary) {
2442
+ if (item.hasValue) {
2443
+ item.secondaryShow();
2444
+ }
2445
+ }
2446
+ });
2447
+ this.items
2448
+ .forEach((item) => {
2449
+ if (!item.primary) {
2450
+ if (item.secondaryVisible) {
2451
+ enabled++;
2452
+ }
2453
+ else if (enabled < this._config.maxEnabled) {
2454
+ item.secondaryShow();
2455
+ enabled++;
2456
+ }
2457
+ }
2458
+ });
2459
+ }
2411
2460
  _addItems(items) {
2412
- this._add$.next();
2413
- this._items = new Map(items
2461
+ const itemMap = items
2414
2462
  .filter((item) => {
2415
2463
  if (this._items.has(item.name)) {
2416
2464
  throw Error('Filter init error. Items name must be unique.');
@@ -2422,9 +2470,9 @@ class FilterController {
2422
2470
  if (filterItem instanceof KeywordItem) {
2423
2471
  this._keywordController.keywordItem = filterItem;
2424
2472
  }
2425
- this._items.set(item.name, filterItem);
2426
2473
  return [item.name, filterItem];
2427
- }));
2474
+ });
2475
+ this._items = new Map(itemMap);
2428
2476
  }
2429
2477
  _initChanges() {
2430
2478
  merge(...this.items
@@ -2948,7 +2996,7 @@ class SelectComponent {
2948
2996
  }
2949
2997
  }
2950
2998
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2951
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: SelectComponent, isStandalone: true, selector: "filter-item-select", inputs: { autofocus: "autofocus", floatLabel: "floatLabel", item: "item" }, viewQueries: [{ propertyName: "select", first: true, predicate: MatSelect, descendants: true, static: true }], ngImport: i0, template: "<mat-form-field\n [ngClass]=\"{ isolate: item.isolate }\"\n [floatLabel]=\"floatLabel\"\n class=\"form-field-padless\">\n <mat-label>\n {{ item.label }}\n </mat-label>\n <mat-select\n [fsFilterFocusTrigger]=\"autofocus\"\n [(ngModel)]=\"value\"\n (ngModelChange)=\"changed()\"\n [multiple]=\"item.multiple\">\n @if (item.children) {\n @for (option of item.values$ | async; track option) {\n @if (option[item.children]) {\n <mat-optgroup [label]=\"option.name\">\n @for (childOption of option[item.children]; track childOption.value) {\n <mat-option [value]=\"childOption.value\">\n {{ childOption.name }}\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n } @else {\n @for (option of item.values$ | async; track option) {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n </mat-select>\n <mat-hint>\n @if (item.isolate) {\n <mat-checkbox\n (change)=\"isolateChange($event)\"\n [(ngModel)]=\"item.isolated\">\n <span class=\"checkbox-label\">\n {{ item.isolateLabel }}\n </span>\n </mat-checkbox>\n }\n </mat-hint>\n</mat-form-field>", styles: [":host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper{padding-left:0;padding-right:0;padding-top:8px}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field{position:relative}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-checkbox{position:absolute}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-label{margin-left:32px;font-size:smaller;line-height:normal}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: FocusToItemDirective, selector: "[fsFilterFocusTrigger]", inputs: ["fsFilterFocusTrigger"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i3.FsFormNoFsValidatorsDirective, selector: "[ngModel]:not([required]):not([fsFormRequired]):not([fsFormCompare]):not([fsFormDateRange]):not([fsFormEmail]):not([fsFormEmails]):not([fsFormFunction]):not([fsFormGreater]):not([fsFormGreaterEqual]):not([fsFormInteger]):not([fsFormLesser]):not([fsFormMax]):not([fsFormMaxLength]):not([fsFormMin]):not([fsFormMinLength]):not([fsFormNumeric]):not([fsFormPattern]):not([fsFormPhone]):not([fsFormUrl]):not([validate])" }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2999
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: SelectComponent, isStandalone: true, selector: "filter-item-select", inputs: { autofocus: "autofocus", floatLabel: "floatLabel", item: "item" }, viewQueries: [{ propertyName: "select", first: true, predicate: MatSelect, descendants: true, static: true }], ngImport: i0, template: "<mat-form-field\n [ngClass]=\"{ isolate: item.isolate }\"\n [floatLabel]=\"floatLabel\"\n class=\"form-field-padless\">\n <mat-label>\n {{ item.label }}\n </mat-label>\n <mat-select\n [fsFilterFocusTrigger]=\"autofocus && !item.isolate\"\n [(ngModel)]=\"value\"\n (ngModelChange)=\"changed()\"\n [multiple]=\"item.multiple\">\n @if (item.children) {\n @for (option of item.values$ | async; track option) {\n @if (option[item.children]) {\n <mat-optgroup [label]=\"option.name\">\n @for (childOption of option[item.children]; track childOption.value) {\n <mat-option [value]=\"childOption.value\">\n {{ childOption.name }}\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n } @else {\n @for (option of item.values$ | async; track option) {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n </mat-select>\n</mat-form-field>\n@if (item.isolate) {\n <div class=\"isolate-checkbox\">\n <mat-checkbox\n (change)=\"isolateChange($event)\"\n [(ngModel)]=\"item.isolated\">\n <span class=\"checkbox-label\">\n {{ item.isolateLabel }}\n </span>\n </mat-checkbox>\n </div>\n}", styles: [":host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper{padding-left:0;padding-right:0;padding-top:8px}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field{position:relative}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-checkbox{position:absolute}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-label{margin-left:32px;font-size:smaller;line-height:normal}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: FocusToItemDirective, selector: "[fsFilterFocusTrigger]", inputs: ["fsFilterFocusTrigger"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i3.FsFormNoFsValidatorsDirective, selector: "[ngModel]:not([required]):not([fsFormRequired]):not([fsFormCompare]):not([fsFormDateRange]):not([fsFormEmail]):not([fsFormEmails]):not([fsFormFunction]):not([fsFormGreater]):not([fsFormGreaterEqual]):not([fsFormInteger]):not([fsFormLesser]):not([fsFormMax]):not([fsFormMaxLength]):not([fsFormMin]):not([fsFormMinLength]):not([fsFormNumeric]):not([fsFormPattern]):not([fsFormPhone]):not([fsFormUrl]):not([validate])" }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2952
3000
  }
2953
3001
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, decorators: [{
2954
3002
  type: Component,
@@ -2962,10 +3010,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2962
3010
  FsFormModule,
2963
3011
  MatOption,
2964
3012
  MatOptgroup,
2965
- MatHint,
2966
3013
  MatCheckbox,
2967
3014
  AsyncPipe,
2968
- ], template: "<mat-form-field\n [ngClass]=\"{ isolate: item.isolate }\"\n [floatLabel]=\"floatLabel\"\n class=\"form-field-padless\">\n <mat-label>\n {{ item.label }}\n </mat-label>\n <mat-select\n [fsFilterFocusTrigger]=\"autofocus\"\n [(ngModel)]=\"value\"\n (ngModelChange)=\"changed()\"\n [multiple]=\"item.multiple\">\n @if (item.children) {\n @for (option of item.values$ | async; track option) {\n @if (option[item.children]) {\n <mat-optgroup [label]=\"option.name\">\n @for (childOption of option[item.children]; track childOption.value) {\n <mat-option [value]=\"childOption.value\">\n {{ childOption.name }}\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n } @else {\n @for (option of item.values$ | async; track option) {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n </mat-select>\n <mat-hint>\n @if (item.isolate) {\n <mat-checkbox\n (change)=\"isolateChange($event)\"\n [(ngModel)]=\"item.isolated\">\n <span class=\"checkbox-label\">\n {{ item.isolateLabel }}\n </span>\n </mat-checkbox>\n }\n </mat-hint>\n</mat-form-field>", styles: [":host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper{padding-left:0;padding-right:0;padding-top:8px}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field{position:relative}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-checkbox{position:absolute}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-label{margin-left:32px;font-size:smaller;line-height:normal}\n"] }]
3015
+ ], template: "<mat-form-field\n [ngClass]=\"{ isolate: item.isolate }\"\n [floatLabel]=\"floatLabel\"\n class=\"form-field-padless\">\n <mat-label>\n {{ item.label }}\n </mat-label>\n <mat-select\n [fsFilterFocusTrigger]=\"autofocus && !item.isolate\"\n [(ngModel)]=\"value\"\n (ngModelChange)=\"changed()\"\n [multiple]=\"item.multiple\">\n @if (item.children) {\n @for (option of item.values$ | async; track option) {\n @if (option[item.children]) {\n <mat-optgroup [label]=\"option.name\">\n @for (childOption of option[item.children]; track childOption.value) {\n <mat-option [value]=\"childOption.value\">\n {{ childOption.name }}\n </mat-option>\n }\n </mat-optgroup>\n } @else {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n } @else {\n @for (option of item.values$ | async; track option) {\n <mat-option [value]=\"option.value\">\n {{ option.name }}\n </mat-option>\n }\n }\n </mat-select>\n</mat-form-field>\n@if (item.isolate) {\n <div class=\"isolate-checkbox\">\n <mat-checkbox\n (change)=\"isolateChange($event)\"\n [(ngModel)]=\"item.isolated\">\n <span class=\"checkbox-label\">\n {{ item.isolateLabel }}\n </span>\n </mat-checkbox>\n </div>\n}", styles: [":host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper{padding-left:0;padding-right:0;padding-top:8px}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field{position:relative}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-checkbox{position:absolute}:host ::ng-deep .isolate .mat-mdc-form-field-hint-wrapper mat-hint .mat-internal-form-field .mdc-label{margin-left:32px;font-size:smaller;line-height:normal}\n"] }]
2969
3016
  }], propDecorators: { autofocus: [{
2970
3017
  type: Input
2971
3018
  }], floatLabel: [{
@@ -3239,6 +3286,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
3239
3286
 
3240
3287
  class FsFilterChipsComponent {
3241
3288
  ItemType = ItemType;
3289
+ secondaryItems = [];
3242
3290
  _filterController = inject(FilterController);
3243
3291
  _dialog = inject(MatDialog);
3244
3292
  _message = inject(FsMessage);
@@ -3246,45 +3294,54 @@ class FsFilterChipsComponent {
3246
3294
  _injector = inject(Injector);
3247
3295
  _overlay = inject(Overlay);
3248
3296
  _overlayRef;
3297
+ _destroyRef = inject(DestroyRef);
3298
+ _elementRef = inject(ElementRef);
3299
+ _hasSecondaryValue$ = new BehaviorSubject(false);
3249
3300
  get items() {
3250
3301
  return this._filterController.items
3251
3302
  .filter((item) => !item.isTypeKeyword);
3252
3303
  }
3253
- get nonPrimaryItems() {
3304
+ addFilter(event) {
3305
+ const item = event.value;
3306
+ item.secondaryShow();
3307
+ setTimeout(() => {
3308
+ this.openChip(item);
3309
+ });
3310
+ }
3311
+ get disabledItems() {
3254
3312
  return this.items
3255
- .filter((item) => !item.primary)
3256
- .flat();
3313
+ .filter((item) => !item.secondaryVisible && !item.hasValue && !item.primary);
3314
+ }
3315
+ get hasSecondaryValue$() {
3316
+ return this._hasSecondaryValue$.asObservable();
3257
3317
  }
3258
3318
  get savedFilterController() {
3259
3319
  return this._savedFilterController;
3260
3320
  }
3321
+ get hasSecondaryValue() {
3322
+ return this._filterController.items
3323
+ .some((item) => item.hasValue && item.visible && !item.primary);
3324
+ }
3261
3325
  ngOnInit() {
3262
- fromEvent(document, 'click')
3263
- .subscribe((event) => {
3264
- const elements = document.elementsFromPoint(event.clientX, event.clientY);
3265
- const item1 = elements.some((element) => {
3266
- return !!this.getNestedElement(element, 'cdk-overlay-pane');
3267
- });
3268
- const item2 = elements.some((element) => {
3269
- return !!this.getNestedElement(element, 'filter-chip');
3270
- });
3271
- if (!item1 && !item2) {
3272
- this._destroyOverlay();
3273
- }
3274
- });
3326
+ this.secondaryItems = this.items
3327
+ .filter((item) => !item.primary);
3328
+ this._initHasSecondaryValue();
3329
+ this._initChipClick();
3275
3330
  }
3276
3331
  clear() {
3277
3332
  this.items
3278
3333
  .filter((item) => item.clearable)
3279
3334
  .forEach((item) => {
3335
+ if (!item.secondary) {
3336
+ item.secondaryHide();
3337
+ }
3280
3338
  item.clear(false);
3281
3339
  });
3282
3340
  this._filterController.change();
3283
- this._savedFilterController.setActiveFilter(null);
3284
3341
  }
3285
- click(item, name, el) {
3342
+ openChip(item, name = null) {
3286
3343
  this._destroyOverlay();
3287
- el = this.getNestedElement(el, 'filter-chip');
3344
+ const el = this._elementRef.nativeElement.querySelector(`[data-filter-item="${item.name}"]`);
3288
3345
  const positions = [
3289
3346
  {
3290
3347
  originX: 'start',
@@ -3365,7 +3422,10 @@ class FsFilterChipsComponent {
3365
3422
  this._overlayRef.dispose();
3366
3423
  }
3367
3424
  }
3368
- remove(item, chip) {
3425
+ removeChip(item, chip) {
3426
+ if (!item.secondary) {
3427
+ item.secondaryHide();
3428
+ }
3369
3429
  if (chip.name) {
3370
3430
  item.clearByName(chip.name);
3371
3431
  }
@@ -3388,8 +3448,31 @@ class FsFilterChipsComponent {
3388
3448
  parent: this._injector,
3389
3449
  });
3390
3450
  }
3451
+ _initChipClick() {
3452
+ fromEvent(document, 'click')
3453
+ .subscribe((event) => {
3454
+ const elements = document.elementsFromPoint(event.clientX, event.clientY);
3455
+ const item1 = elements.some((element) => {
3456
+ return !!this.getNestedElement(element, 'cdk-overlay-pane');
3457
+ });
3458
+ const item2 = elements.some((element) => {
3459
+ return !!this.getNestedElement(element, 'filter-chip');
3460
+ });
3461
+ if (!item1 && !item2) {
3462
+ this._destroyOverlay();
3463
+ }
3464
+ });
3465
+ }
3466
+ _initHasSecondaryValue() {
3467
+ this._hasSecondaryValue$.next(this.hasSecondaryValue);
3468
+ this._filterController.change$
3469
+ .pipe(tap$1(() => {
3470
+ this._hasSecondaryValue$.next(this.hasSecondaryValue);
3471
+ }), takeUntilDestroyed(this._destroyRef))
3472
+ .subscribe();
3473
+ }
3391
3474
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FsFilterChipsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3392
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FsFilterChipsComponent, isStandalone: true, selector: "fs-filter-chips", ngImport: i0, template: "@if (items.length !== 0) {\n @for (item of nonPrimaryItems; track item.name) {\n @if (item.visible$ | async) {\n @if ((item.chips$ | async).length) {\n @for (chip of item.chips$ | async; track chip.label) {\n <fs-chip\n class=\"filter-chip selected\"\n [size]=\"'medium'\"\n (click)=\"click(item, chip.name, $event.target)\">\n @if (chip.value) {\n {{ chip.label + ': ' + chip.value }}\n } @else {\n {{ chip.label }}\n }\n <ng-template\n fsChipSuffix\n [icon]=\"'cancel_circle_outline'\"\n (click)=\"remove(item, chip)\">\n </ng-template>\n </fs-chip>\n }\n } @else {\n <fs-chip\n class=\"filter-chip\"\n [size]=\"'medium'\"\n (click)=\"click(item, null, $event.target)\"\n [outlined]=\"true\">\n {{ item.mergedLabel }}\n <ng-template\n fsChipSuffix\n [icon]=\"'add_circle_outline'\"\n (click)=\"click(item, null, $event.event.target)\">\n </ng-template>\n </fs-chip>\n }\n }\n }\n <a\n class=\"clear\"\n mat-stroked-button\n (click)=\"clear()\">\n Clear filters\n </a>\n <mat-select\n class=\"saved-filters-select mat-mdc-outlined-button\"\n [buttonType]=\"'basic'\"\n fsSelectButton\n [placeholder]=\"(savedFilterController.activeFilter$ | async) ? savedFilterController.singularLabel + ': ' + (savedFilterController.activeFilter$ | async).name : savedFilterController.pluralLabel\"\n [deselectOnChange]=\"true\">\n @if (savedFilterController.activeFilter$ | async) {\n <mat-option (click)=\"saveActiveFilter()\">\n Update filters\n </mat-option>\n <mat-option (click)=\"saveAs()\">\n Save as new\n </mat-option>\n } @else {\n <mat-option (click)=\"createSavedFilter()\">\n Create new\n </mat-option>\n }\n <mat-option (click)=\"manageSavedFilters()\">\n View all\n </mat-option>\n </mat-select>\n}", styles: [":host{display:flex;flex-wrap:wrap;align-items:center;gap:5px;max-width:100%;margin-top:4px}.saved-filters-select,.clear{display:flex;color:inherit;height:30px}.saved-filters-select.clear,.clear.clear{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-value,.clear ::ng-deep .mat-mdc-select-value{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-arrow-wrapper,.clear ::ng-deep .mat-mdc-select-arrow-wrapper{padding-right:10px}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: FsChipModule }, { kind: "component", type: i1$5.FsChipComponent, selector: "fs-chip", inputs: ["selectable", "removable", "value", "maxWidth", "width", "backgroundColor", "borderColor", "color", "shape", "outlined", "outlineDash", "icon", "image", "selected", "padding", "contrastColor", "size"], outputs: ["selectedToggled", "removed", "click"] }, { kind: "directive", type: i1$5.FsChipSuffixDirective, selector: "[fsChipSuffix]", inputs: ["icon", "link", "linkTarget", "color", "data", "tooltip"], outputs: ["click"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption$1, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: FsSelectButtonModule }, { kind: "directive", type: i3$1.FsSelectButtonDirective, selector: "[fsSelectButton]", inputs: ["color", "minWidth", "maxWidth", "width", "buttonType", "deselectOnChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3475
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FsFilterChipsComponent, isStandalone: true, selector: "fs-filter-chips", ngImport: i0, template: "@if (items.length !== 0) {\n @for (item of secondaryItems; track item.name) {\n @if ((item.visible$ | async) && (item.hasValue$ | async)) {\n @for (chip of item.chips$ | async; track chip.label) {\n <fs-chip\n class=\"filter-chip selected\"\n [size]=\"'medium'\"\n [attr.data-filter-item]=\"item.name\"\n (click)=\"openChip(item, chip.name)\">\n @if (chip.value) {\n {{ chip.label + ': ' + chip.value }}\n } @else {\n {{ chip.label }}\n }\n <ng-template\n fsChipSuffix\n [icon]=\"'cancel_circle_outline'\"\n (click)=\"removeChip(item, chip)\">\n </ng-template>\n </fs-chip>\n }\n }\n }\n @for (item of secondaryItems; track item.name) {\n @if ((item.visible$ | async) && (item.notValue$ | async) && (item.secondaryVisible$ | async)) {\n <fs-chip\n class=\"filter-chip\"\n [attr.data-filter-item]=\"item.name\"\n [size]=\"'medium'\"\n (click)=\"openChip(item)\"\n [outlined]=\"true\">\n {{ item.mergedLabel }}\n <ng-template\n fsChipSuffix\n [icon]=\"'add_circle_outline'\"\n (click)=\"openChip(item)\">\n </ng-template>\n </fs-chip>\n }\n }\n <mat-select\n class=\"more-filters-select mat-mdc-outlined-button\"\n [buttonType]=\"'basic'\"\n fsSelectButton\n [placeholder]=\"'More filters'\"\n (selectionChange)=\"addFilter($event)\"\n [deselectOnChange]=\"true\">\n @for (item of disabledItems; track item.name) {\n <mat-option [value]=\"item\">\n {{ item.mergedLabel }}\n </mat-option>\n }\n </mat-select>\n @if (hasSecondaryValue$ | async) {\n <a\n class=\"clear\"\n mat-stroked-button\n (click)=\"clear()\">\n Clear filters\n </a>\n }\n <mat-select\n class=\"saved-filters-select mat-mdc-outlined-button\"\n [buttonType]=\"'basic'\"\n fsSelectButton\n [placeholder]=\"(savedFilterController.activeFilter$ | async) ? savedFilterController.singularLabel + ': ' + (savedFilterController.activeFilter$ | async).name : savedFilterController.pluralLabel\"\n [deselectOnChange]=\"true\">\n @if (savedFilterController.activeFilter$ | async) {\n <mat-option (click)=\"saveActiveFilter()\">\n Update filters\n </mat-option>\n <mat-option (click)=\"saveAs()\">\n Save as new\n </mat-option>\n } @else {\n <mat-option (click)=\"createSavedFilter()\">\n Create new\n </mat-option>\n }\n <mat-option (click)=\"manageSavedFilters()\">\n View all\n </mat-option>\n </mat-select>\n}", styles: [":host{display:flex;flex-wrap:wrap;align-items:center;gap:5px;max-width:100%;margin-top:4px}.saved-filters-select,.more-filters-select,.clear{display:flex;color:inherit;height:30px}.saved-filters-select.clear,.more-filters-select.clear,.clear.clear{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-value,.more-filters-select ::ng-deep .mat-mdc-select-value,.clear ::ng-deep .mat-mdc-select-value{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-arrow-wrapper,.more-filters-select ::ng-deep .mat-mdc-select-arrow-wrapper,.clear ::ng-deep .mat-mdc-select-arrow-wrapper{padding-right:10px}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "ngmodule", type: FsChipModule }, { kind: "component", type: i1$5.FsChipComponent, selector: "fs-chip", inputs: ["selectable", "removable", "value", "maxWidth", "width", "backgroundColor", "borderColor", "color", "shape", "outlined", "outlineDash", "icon", "image", "selected", "padding", "contrastColor", "size"], outputs: ["selectedToggled", "removed", "click"] }, { kind: "directive", type: i1$5.FsChipSuffixDirective, selector: "[fsChipSuffix]", inputs: ["icon", "link", "linkTarget", "color", "data", "tooltip"], outputs: ["click"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption$1, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: FsSelectButtonModule }, { kind: "directive", type: i3$1.FsSelectButtonDirective, selector: "[fsSelectButton]", inputs: ["color", "minWidth", "maxWidth", "width", "buttonType", "deselectOnChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3393
3476
  }
3394
3477
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FsFilterChipsComponent, decorators: [{
3395
3478
  type: Component,
@@ -3401,7 +3484,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
3401
3484
  FsSelectButtonModule,
3402
3485
  MatButtonModule,
3403
3486
  FsButtonDirective,
3404
- ], template: "@if (items.length !== 0) {\n @for (item of nonPrimaryItems; track item.name) {\n @if (item.visible$ | async) {\n @if ((item.chips$ | async).length) {\n @for (chip of item.chips$ | async; track chip.label) {\n <fs-chip\n class=\"filter-chip selected\"\n [size]=\"'medium'\"\n (click)=\"click(item, chip.name, $event.target)\">\n @if (chip.value) {\n {{ chip.label + ': ' + chip.value }}\n } @else {\n {{ chip.label }}\n }\n <ng-template\n fsChipSuffix\n [icon]=\"'cancel_circle_outline'\"\n (click)=\"remove(item, chip)\">\n </ng-template>\n </fs-chip>\n }\n } @else {\n <fs-chip\n class=\"filter-chip\"\n [size]=\"'medium'\"\n (click)=\"click(item, null, $event.target)\"\n [outlined]=\"true\">\n {{ item.mergedLabel }}\n <ng-template\n fsChipSuffix\n [icon]=\"'add_circle_outline'\"\n (click)=\"click(item, null, $event.event.target)\">\n </ng-template>\n </fs-chip>\n }\n }\n }\n <a\n class=\"clear\"\n mat-stroked-button\n (click)=\"clear()\">\n Clear filters\n </a>\n <mat-select\n class=\"saved-filters-select mat-mdc-outlined-button\"\n [buttonType]=\"'basic'\"\n fsSelectButton\n [placeholder]=\"(savedFilterController.activeFilter$ | async) ? savedFilterController.singularLabel + ': ' + (savedFilterController.activeFilter$ | async).name : savedFilterController.pluralLabel\"\n [deselectOnChange]=\"true\">\n @if (savedFilterController.activeFilter$ | async) {\n <mat-option (click)=\"saveActiveFilter()\">\n Update filters\n </mat-option>\n <mat-option (click)=\"saveAs()\">\n Save as new\n </mat-option>\n } @else {\n <mat-option (click)=\"createSavedFilter()\">\n Create new\n </mat-option>\n }\n <mat-option (click)=\"manageSavedFilters()\">\n View all\n </mat-option>\n </mat-select>\n}", styles: [":host{display:flex;flex-wrap:wrap;align-items:center;gap:5px;max-width:100%;margin-top:4px}.saved-filters-select,.clear{display:flex;color:inherit;height:30px}.saved-filters-select.clear,.clear.clear{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-value,.clear ::ng-deep .mat-mdc-select-value{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-arrow-wrapper,.clear ::ng-deep .mat-mdc-select-arrow-wrapper{padding-right:10px}\n"] }]
3487
+ ], template: "@if (items.length !== 0) {\n @for (item of secondaryItems; track item.name) {\n @if ((item.visible$ | async) && (item.hasValue$ | async)) {\n @for (chip of item.chips$ | async; track chip.label) {\n <fs-chip\n class=\"filter-chip selected\"\n [size]=\"'medium'\"\n [attr.data-filter-item]=\"item.name\"\n (click)=\"openChip(item, chip.name)\">\n @if (chip.value) {\n {{ chip.label + ': ' + chip.value }}\n } @else {\n {{ chip.label }}\n }\n <ng-template\n fsChipSuffix\n [icon]=\"'cancel_circle_outline'\"\n (click)=\"removeChip(item, chip)\">\n </ng-template>\n </fs-chip>\n }\n }\n }\n @for (item of secondaryItems; track item.name) {\n @if ((item.visible$ | async) && (item.notValue$ | async) && (item.secondaryVisible$ | async)) {\n <fs-chip\n class=\"filter-chip\"\n [attr.data-filter-item]=\"item.name\"\n [size]=\"'medium'\"\n (click)=\"openChip(item)\"\n [outlined]=\"true\">\n {{ item.mergedLabel }}\n <ng-template\n fsChipSuffix\n [icon]=\"'add_circle_outline'\"\n (click)=\"openChip(item)\">\n </ng-template>\n </fs-chip>\n }\n }\n <mat-select\n class=\"more-filters-select mat-mdc-outlined-button\"\n [buttonType]=\"'basic'\"\n fsSelectButton\n [placeholder]=\"'More filters'\"\n (selectionChange)=\"addFilter($event)\"\n [deselectOnChange]=\"true\">\n @for (item of disabledItems; track item.name) {\n <mat-option [value]=\"item\">\n {{ item.mergedLabel }}\n </mat-option>\n }\n </mat-select>\n @if (hasSecondaryValue$ | async) {\n <a\n class=\"clear\"\n mat-stroked-button\n (click)=\"clear()\">\n Clear filters\n </a>\n }\n <mat-select\n class=\"saved-filters-select mat-mdc-outlined-button\"\n [buttonType]=\"'basic'\"\n fsSelectButton\n [placeholder]=\"(savedFilterController.activeFilter$ | async) ? savedFilterController.singularLabel + ': ' + (savedFilterController.activeFilter$ | async).name : savedFilterController.pluralLabel\"\n [deselectOnChange]=\"true\">\n @if (savedFilterController.activeFilter$ | async) {\n <mat-option (click)=\"saveActiveFilter()\">\n Update filters\n </mat-option>\n <mat-option (click)=\"saveAs()\">\n Save as new\n </mat-option>\n } @else {\n <mat-option (click)=\"createSavedFilter()\">\n Create new\n </mat-option>\n }\n <mat-option (click)=\"manageSavedFilters()\">\n View all\n </mat-option>\n </mat-select>\n}", styles: [":host{display:flex;flex-wrap:wrap;align-items:center;gap:5px;max-width:100%;margin-top:4px}.saved-filters-select,.more-filters-select,.clear{display:flex;color:inherit;height:30px}.saved-filters-select.clear,.more-filters-select.clear,.clear.clear{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-value,.more-filters-select ::ng-deep .mat-mdc-select-value,.clear ::ng-deep .mat-mdc-select-value{padding:0 10px}.saved-filters-select ::ng-deep .mat-mdc-select-arrow-wrapper,.more-filters-select ::ng-deep .mat-mdc-select-arrow-wrapper,.clear ::ng-deep .mat-mdc-select-arrow-wrapper{padding-right:10px}\n"] }]
3405
3488
  }] });
3406
3489
 
3407
3490
  class KeywordInputComponent {