@firestitch/filter 18.2.71 → 18.2.73

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,6 +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';
3
- import { FsMessage } from '@firestitch/message';
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';
4
3
  import { FsPrompt } from '@firestitch/prompt';
5
4
  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';
6
5
  import { distinctUntilChanged, switchMap, tap, skip, takeUntil, map, debounceTime, finalize, filter as filter$1 } from 'rxjs/operators';
@@ -47,14 +46,14 @@ import { Overlay, OverlayConfig } from '@angular/cdk/overlay';
47
46
  import { ComponentPortal } from '@angular/cdk/portal';
48
47
  import * as i1$5 from '@firestitch/chip';
49
48
  import { FsChipModule } from '@firestitch/chip';
49
+ import { FsMessage } from '@firestitch/message';
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 {
57
- _message = inject(FsMessage);
58
57
  _filterController;
59
58
  _savedFilters$ = new BehaviorSubject([]);
60
59
  _activeFilter$ = new BehaviorSubject(null);
@@ -792,6 +791,7 @@ class FsFilterConfig {
792
791
  namespace; // for persistance
793
792
  heading;
794
793
  subheading;
794
+ maxEnabled;
795
795
  constructor(data = {}) {
796
796
  this._init(data);
797
797
  }
@@ -802,6 +802,7 @@ class FsFilterConfig {
802
802
  savedFilters: data.savedFilters,
803
803
  autofocus: data.autofocus ?? false,
804
804
  chips: data.chips ?? false,
805
+ maxEnabled: data.maxEnabled ?? 0,
805
806
  sort: data.sort,
806
807
  queryParam: data.queryParam ?? false,
807
808
  init: data.init,
@@ -1002,6 +1003,7 @@ class BaseItem {
1002
1003
  persistanceDisabled;
1003
1004
  queryParamsDisabled;
1004
1005
  primary;
1006
+ secondary;
1005
1007
  changeCallback;
1006
1008
  initCallback;
1007
1009
  _type;
@@ -1009,12 +1011,25 @@ class BaseItem {
1009
1011
  _hidden$ = new BehaviorSubject(false);
1010
1012
  _value$ = new BehaviorSubject({ value: undefined, emitChange: true });
1011
1013
  _values$ = new BehaviorSubject(null);
1014
+ _secondaryVisible$ = new BehaviorSubject(false);
1012
1015
  _destroy$ = new Subject();
1013
1016
  constructor(itemConfig, _filter) {
1014
1017
  this._filter = _filter;
1015
1018
  this._type = itemConfig.type;
1016
1019
  this._initConfig(itemConfig);
1017
1020
  }
1021
+ secondaryShow() {
1022
+ this._secondaryVisible$.next(true);
1023
+ }
1024
+ secondaryHide() {
1025
+ this._secondaryVisible$.next(false);
1026
+ }
1027
+ get secondaryVisible$() {
1028
+ return this._secondaryVisible$.asObservable();
1029
+ }
1030
+ get secondaryVisible() {
1031
+ return this._secondaryVisible$.getValue();
1032
+ }
1018
1033
  get filter() {
1019
1034
  return this._filter;
1020
1035
  }
@@ -1079,6 +1094,10 @@ class BaseItem {
1079
1094
  get hasValue() {
1080
1095
  return this.value !== null && this.value !== undefined;
1081
1096
  }
1097
+ get notValue$() {
1098
+ return this.value$
1099
+ .pipe(map(() => !this.hasValue));
1100
+ }
1082
1101
  get hasValue$() {
1083
1102
  return this.value$
1084
1103
  .pipe(map(() => this.hasValue));
@@ -1122,9 +1141,15 @@ class BaseItem {
1122
1141
  return this.query;
1123
1142
  }
1124
1143
  hide() {
1144
+ if (!this.primary) {
1145
+ this.secondaryHide();
1146
+ }
1125
1147
  this._hidden$.next(true);
1126
1148
  }
1127
1149
  show() {
1150
+ if (!this.primary) {
1151
+ this.secondaryShow();
1152
+ }
1128
1153
  this._hidden$.next(false);
1129
1154
  }
1130
1155
  get query() {
@@ -1190,12 +1215,13 @@ class BaseItem {
1190
1215
  }
1191
1216
  }
1192
1217
  _initConfig(item) {
1193
- const hidden = item.hide ?? !(item.show ?? true);
1194
1218
  this.name = item.name;
1195
1219
  this.label = item.label;
1196
- this.primary = item.primary ?? false;
1220
+ this.primary = this.isTypeKeyword || (item.primary ?? false);
1221
+ this.secondary = item.secondary ?? false;
1197
1222
  this.chipLabel = item.chipLabel;
1198
- this._hidden$.next(hidden);
1223
+ this._hidden$.next(item.hide ?? !(item.show ?? true));
1224
+ this._secondaryVisible$.next(item.secondary ?? false);
1199
1225
  this.clearable = item.clear ?? true;
1200
1226
  this.persistanceDisabled = item.disablePersist ?? false;
1201
1227
  this.queryParamsDisabled = item.disableQueryParams ?? false;
@@ -2260,7 +2286,6 @@ class FilterController {
2260
2286
  _ready$ = new BehaviorSubject(false);
2261
2287
  _items = new Map();
2262
2288
  _config;
2263
- _add$ = new Subject();
2264
2289
  _init$ = new Subject();
2265
2290
  _change$ = new Subject();
2266
2291
  _destroy$ = new Subject();
@@ -2281,9 +2306,6 @@ class FilterController {
2281
2306
  get init$() {
2282
2307
  return this._init$.asObservable();
2283
2308
  }
2284
- get add$() {
2285
- return this._add$.asObservable();
2286
- }
2287
2309
  get change$() {
2288
2310
  return this._change$
2289
2311
  .pipe(debounceTime(30));
@@ -2404,13 +2426,38 @@ class FilterController {
2404
2426
  .map((item) => {
2405
2427
  return item.init(values[item.name]);
2406
2428
  }))
2407
- .pipe(tap(() => this.items.forEach((item) => {
2429
+ .pipe(tap(() => {
2430
+ this._initEnabledItems();
2431
+ }), tap(() => this.items
2432
+ .forEach((item) => {
2408
2433
  item.initCallback(item, this.filter);
2409
2434
  })));
2410
2435
  }
2436
+ _initEnabledItems() {
2437
+ let enabled = 0;
2438
+ this.items
2439
+ .forEach((item) => {
2440
+ if (!item.primary) {
2441
+ if (item.hasValue) {
2442
+ item.secondaryShow();
2443
+ }
2444
+ }
2445
+ });
2446
+ this.items
2447
+ .forEach((item) => {
2448
+ if (!item.primary) {
2449
+ if (item.secondaryVisible) {
2450
+ enabled++;
2451
+ }
2452
+ else if (enabled < this._config.maxEnabled) {
2453
+ item.secondaryShow();
2454
+ enabled++;
2455
+ }
2456
+ }
2457
+ });
2458
+ }
2411
2459
  _addItems(items) {
2412
- this._add$.next();
2413
- this._items = new Map(items
2460
+ const itemMap = items
2414
2461
  .filter((item) => {
2415
2462
  if (this._items.has(item.name)) {
2416
2463
  throw Error('Filter init error. Items name must be unique.');
@@ -2422,9 +2469,9 @@ class FilterController {
2422
2469
  if (filterItem instanceof KeywordItem) {
2423
2470
  this._keywordController.keywordItem = filterItem;
2424
2471
  }
2425
- this._items.set(item.name, filterItem);
2426
2472
  return [item.name, filterItem];
2427
- }));
2473
+ });
2474
+ this._items = new Map(itemMap);
2428
2475
  }
2429
2476
  _initChanges() {
2430
2477
  merge(...this.items
@@ -2948,7 +2995,7 @@ class SelectComponent {
2948
2995
  }
2949
2996
  }
2950
2997
  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 });
2998
+ 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
2999
  }
2953
3000
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, decorators: [{
2954
3001
  type: Component,
@@ -2962,10 +3009,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2962
3009
  FsFormModule,
2963
3010
  MatOption,
2964
3011
  MatOptgroup,
2965
- MatHint,
2966
3012
  MatCheckbox,
2967
3013
  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"] }]
3014
+ ], 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
3015
  }], propDecorators: { autofocus: [{
2970
3016
  type: Input
2971
3017
  }], floatLabel: [{
@@ -3239,6 +3285,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
3239
3285
 
3240
3286
  class FsFilterChipsComponent {
3241
3287
  ItemType = ItemType;
3288
+ secondaryItems = [];
3242
3289
  _filterController = inject(FilterController);
3243
3290
  _dialog = inject(MatDialog);
3244
3291
  _message = inject(FsMessage);
@@ -3246,45 +3293,55 @@ class FsFilterChipsComponent {
3246
3293
  _injector = inject(Injector);
3247
3294
  _overlay = inject(Overlay);
3248
3295
  _overlayRef;
3296
+ _destroyRef = inject(DestroyRef);
3297
+ _elementRef = inject(ElementRef);
3298
+ _hasSecondaryValue$ = new BehaviorSubject(false);
3249
3299
  get items() {
3250
3300
  return this._filterController.items
3251
3301
  .filter((item) => !item.isTypeKeyword);
3252
3302
  }
3253
- get nonPrimaryItems() {
3303
+ addFilter(event) {
3304
+ const item = event.value;
3305
+ item.secondaryShow();
3306
+ setTimeout(() => {
3307
+ this.openChip(item);
3308
+ });
3309
+ }
3310
+ get disabledItems() {
3254
3311
  return this.items
3255
- .filter((item) => !item.primary)
3256
- .flat();
3312
+ .filter((item) => !item.secondaryVisible && !item.hasValue && !item.primary);
3313
+ }
3314
+ get hasSecondaryValue$() {
3315
+ return this._hasSecondaryValue$.asObservable();
3257
3316
  }
3258
3317
  get savedFilterController() {
3259
3318
  return this._savedFilterController;
3260
3319
  }
3320
+ get hasSecondaryValue() {
3321
+ return this._filterController.items
3322
+ .some((item) => item.hasValue && item.visible && !item.primary);
3323
+ }
3261
3324
  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
- });
3325
+ this.secondaryItems = this.items
3326
+ .filter((item) => !item.primary);
3327
+ this._initHasSecondaryValue();
3328
+ this._initChipClick();
3275
3329
  }
3276
3330
  clear() {
3277
- this.items
3331
+ this._filterController.items
3278
3332
  .filter((item) => item.clearable)
3279
3333
  .forEach((item) => {
3334
+ if (!item.secondary) {
3335
+ item.secondaryHide();
3336
+ }
3280
3337
  item.clear(false);
3281
3338
  });
3282
3339
  this._filterController.change();
3283
3340
  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 [borderColor]=\"'#ddd'\"\n [attr.data-filter-item]=\"item.name\"\n [outlined]=\"true\"\n (click)=\"openChip(item, chip.name)\">\n @if (chip.value) {\n {{ chip.label }}:\n <a>\n {{ chip.value }}\n </a>\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 @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 [borderColor]=\"'#ddd'\"\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-button-base\"\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 <mat-select\n class=\"saved-filters-select mat-mdc-button-base\"\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 @if (hasSecondaryValue$ | async) {\n <a\n class=\"clear\"\n mat-button\n (click)=\"clear()\">\n Clear filters\n </a>\n }\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;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 5px}.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 [borderColor]=\"'#ddd'\"\n [attr.data-filter-item]=\"item.name\"\n [outlined]=\"true\"\n (click)=\"openChip(item, chip.name)\">\n @if (chip.value) {\n {{ chip.label }}:\n <a>\n {{ chip.value }}\n </a>\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 @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 [borderColor]=\"'#ddd'\"\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-button-base\"\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 <mat-select\n class=\"saved-filters-select mat-mdc-button-base\"\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 @if (hasSecondaryValue$ | async) {\n <a\n class=\"clear\"\n mat-button\n (click)=\"clear()\">\n Clear filters\n </a>\n }\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;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 5px}.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 {