@testgorilla/tgo-ui 9.1.1 → 9.3.0

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.
@@ -9,7 +9,7 @@ type DividerSize = 'small' | 'large';
9
9
  declare class DividerComponent {
10
10
  private readonly defaultAppTheme;
11
11
  /**
12
- * Divider size
12
+ * Divider size. Defaults to 'small'.
13
13
  *
14
14
  * @type {DividerSize}
15
15
  * @memberof DividerComponent
@@ -1,6 +1,6 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnChanges, EventEmitter, ElementRef, QueryList, ChangeDetectorRef, SimpleChanges } from '@angular/core';
3
- import { FocusableOption, FocusKeyManager } from '@angular/cdk/a11y';
2
+ import { OnChanges, EventEmitter, ElementRef, ChangeDetectorRef, DestroyRef, SimpleChanges } from '@angular/core';
3
+ import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
4
4
  import * as i6 from '@testgorilla/tgo-ui/components/button';
5
5
  import { ButtonIconPosition } from '@testgorilla/tgo-ui/components/button';
6
6
  import * as i5 from '@testgorilla/tgo-ui/components/core';
@@ -29,6 +29,13 @@ interface OptionChangeEvent {
29
29
  option: FilterButtonOption;
30
30
  }
31
31
 
32
+ type FilterButtonRow = {
33
+ kind: 'header';
34
+ title: string;
35
+ } | {
36
+ kind: 'option';
37
+ option: FilterButtonOption;
38
+ };
32
39
  declare class FilterButtonComponent implements OnChanges {
33
40
  private readonly defaultAppTheme;
34
41
  private readonly cdr;
@@ -131,11 +138,15 @@ declare class FilterButtonComponent implements OnChanges {
131
138
  */
132
139
  applicationTheme: ApplicationTheme;
133
140
  overlayContainer: ElementRef<HTMLElement>;
134
- filterOptions: QueryList<FocusableOption>;
141
+ viewport?: CdkVirtualScrollViewport;
142
+ /** Must match the `.item` height in the SCSS — the fixed-size viewport relies on it. */
143
+ readonly ITEM_SIZE = 48;
144
+ readonly MAX_LIST_HEIGHT = 290;
135
145
  isOpen: _angular_core.WritableSignal<boolean>;
136
146
  displayedLabel: _angular_core.WritableSignal<string>;
137
147
  tooltip: _angular_core.WritableSignal<string>;
138
148
  search: _angular_core.WritableSignal<string>;
149
+ activeIndex: _angular_core.WritableSignal<number>;
139
150
  showTooltip: {
140
151
  [key: string]: boolean;
141
152
  };
@@ -146,20 +157,30 @@ declare class FilterButtonComponent implements OnChanges {
146
157
  labelNumber?: number;
147
158
  icon?: IconName;
148
159
  }[]>;
160
+ flatOptions: _angular_core.Signal<FilterButtonRow[]>;
161
+ optionCount: _angular_core.Signal<number>;
162
+ activeOption: _angular_core.Signal<FilterButtonOption | undefined>;
163
+ activePosition: _angular_core.Signal<number>;
164
+ listHeight: _angular_core.Signal<number>;
165
+ protected trackByRow: (_index: number, row: FilterButtonRow) => string;
149
166
  readonly MIN_OPTIONS_LENGTH_FOR_SEARCH = 8;
150
- protected keyManager: FocusKeyManager<FocusableOption>;
151
167
  protected translationContext: string;
152
- constructor(defaultAppTheme: ApplicationTheme, cdr: ChangeDetectorRef);
168
+ private readonly searchInput$;
169
+ constructor(defaultAppTheme: ApplicationTheme, cdr: ChangeDetectorRef, destroyRef: DestroyRef);
153
170
  ngOnChanges(changes: SimpleChanges): void;
154
171
  clearValue(): void;
155
172
  outsideClick(): void;
156
173
  toggle(): void;
157
174
  isSelected(value?: FilterValue): boolean;
158
175
  optionChanged(selected: boolean, option: FilterButtonOption): void;
176
+ onSearchChange(value: string): void;
159
177
  keyDownHandler(ev: KeyboardEvent): void;
160
178
  onLabelEllipsisChange(isEllipsis: boolean, value: FilterValue): void;
179
+ private firstOptionIndex;
180
+ private moveActive;
181
+ private toggleActiveOption;
161
182
  private updateLabel;
162
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FilterButtonComponent, [{ optional: true; }, null]>;
183
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FilterButtonComponent, [{ optional: true; }, null, null]>;
163
184
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<FilterButtonComponent, "ui-filter-button", never, { "label": { "alias": "label"; "required": false; }; "message": { "alias": "message"; "required": false; }; "value": { "alias": "value"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "allowClear": { "alias": "allowClear"; "required": false; }; "singleSelection": { "alias": "singleSelection"; "required": false; }; "iconName": { "alias": "iconName"; "required": false; }; "iconPosition": { "alias": "iconPosition"; "required": false; }; "noOptionsMessage": { "alias": "noOptionsMessage"; "required": false; }; "applicationTheme": { "alias": "applicationTheme"; "required": false; }; }, { "changed": "changed"; "optionChange": "optionChange"; }, never, never, true, never>;
164
185
  }
165
186
 
@@ -40,7 +40,10 @@ declare class IconComponent implements OnInit, OnChanges {
40
40
  private readonly domSanitzer;
41
41
  private readonly iconsService;
42
42
  /**
43
- * Icon size
43
+ * Icon size. Accepts ONLY '16' | '24' | '40' | '80' (pixel sizes, as strings).
44
+ * There is no runtime validation or clamping — any other value renders the
45
+ * glyph at an uncontrolled (typically huge) size. Pick the nearest supported
46
+ * value instead of passing a custom one.
44
47
  *
45
48
  * @type {IconSize}
46
49
  * @memberof IconComponent
@@ -7,12 +7,12 @@ class DividerComponent {
7
7
  constructor(defaultAppTheme) {
8
8
  this.defaultAppTheme = defaultAppTheme;
9
9
  /**
10
- * Divider size
10
+ * Divider size. Defaults to 'small'.
11
11
  *
12
12
  * @type {DividerSize}
13
13
  * @memberof DividerComponent
14
14
  */
15
- this.size = 'large';
15
+ this.size = 'small';
16
16
  /**
17
17
  * Determines whether the checkbox color.
18
18
  * Default: Test Gorilla primary color.
@@ -1 +1 @@
1
- {"version":3,"file":"testgorilla-tgo-ui-components-divider.mjs","sources":["../../../components/divider/divider.component.ts","../../../components/divider/divider.component.html","../../../components/divider/divider.component.module.ts","../../../components/divider/testgorilla-tgo-ui-components-divider.ts"],"sourcesContent":["import { NgClass } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, HostBinding, Inject, Input, Optional } from '@angular/core';\nimport { MatDivider } from '@angular/material/divider';\n\nimport { ApplicationTheme } from '@testgorilla/tgo-ui/components/core';\n\nimport { DividerSize } from './divider.model';\n\n@Component({\n selector: 'ui-divider',\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './divider.component.html',\n styleUrls: ['./divider.component.scss'],\n imports: [MatDivider, NgClass],\n})\nexport class DividerComponent {\n /**\n * Divider size\n *\n * @type {DividerSize}\n * @memberof DividerComponent\n */\n @Input() size: DividerSize = 'large';\n\n /**\n * Determines whether the checkbox color.\n * Default: Test Gorilla primary color.\n *\n * @type {string}\n * @memberof DividerComponent\n */\n @HostBinding('style.--companyColor')\n @Input()\n companyColor: string | null = '#46A997';\n\n /**\n *\n * Defines the application theme\n *\n * @type {ApplicationTheme}\n * @memberof DividerComponent\n */\n @Input() applicationTheme: ApplicationTheme = 'light';\n\n /**\n * Text content in the center of the divider\n *\n * @type {string}\n * @memberof DividerComponent\n */\n @Input() label = '';\n\n constructor(\n @Optional() @Inject('CANOPYUI_DEFAULT_APPLICATION_THEME') private readonly defaultAppTheme: ApplicationTheme\n ) {\n if (defaultAppTheme) {\n this.applicationTheme = defaultAppTheme;\n }\n }\n}\n","<div class=\"ui-divider-wrapper\">\n @if (!label) {\n <mat-divider aria-hidden=\"true\" [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider>\n } @else {\n <div class=\"container\">\n <div class=\"line\" aria-hidden=\"true\"><mat-divider [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider></div>\n <div [class]=\"'label' + ' ' + size\">{{ label }}</div>\n <div class=\"line\" aria-hidden=\"true\"><mat-divider [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider></div>\n </div>\n }\n</div>\n","import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { MatDividerModule } from '@angular/material/divider';\n\nimport { DividerComponent } from './divider.component';\n\n@NgModule({\n exports: [DividerComponent],\n imports: [CommonModule, MatDividerModule, DividerComponent],\n})\nexport class DividerComponentModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testgorilla-tgo-ui-components-divider.mjs","sources":["../../../components/divider/divider.component.ts","../../../components/divider/divider.component.html","../../../components/divider/divider.component.module.ts","../../../components/divider/testgorilla-tgo-ui-components-divider.ts"],"sourcesContent":["import { NgClass } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, HostBinding, Inject, Input, Optional } from '@angular/core';\nimport { MatDivider } from '@angular/material/divider';\n\nimport { ApplicationTheme } from '@testgorilla/tgo-ui/components/core';\n\nimport { DividerSize } from './divider.model';\n\n@Component({\n selector: 'ui-divider',\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './divider.component.html',\n styleUrls: ['./divider.component.scss'],\n imports: [MatDivider, NgClass],\n})\nexport class DividerComponent {\n /**\n * Divider size. Defaults to 'small'.\n *\n * @type {DividerSize}\n * @memberof DividerComponent\n */\n @Input() size: DividerSize = 'small';\n\n /**\n * Determines whether the checkbox color.\n * Default: Test Gorilla primary color.\n *\n * @type {string}\n * @memberof DividerComponent\n */\n @HostBinding('style.--companyColor')\n @Input()\n companyColor: string | null = '#46A997';\n\n /**\n *\n * Defines the application theme\n *\n * @type {ApplicationTheme}\n * @memberof DividerComponent\n */\n @Input() applicationTheme: ApplicationTheme = 'light';\n\n /**\n * Text content in the center of the divider\n *\n * @type {string}\n * @memberof DividerComponent\n */\n @Input() label = '';\n\n constructor(\n @Optional() @Inject('CANOPYUI_DEFAULT_APPLICATION_THEME') private readonly defaultAppTheme: ApplicationTheme\n ) {\n if (defaultAppTheme) {\n this.applicationTheme = defaultAppTheme;\n }\n }\n}\n","<div class=\"ui-divider-wrapper\">\n @if (!label) {\n <mat-divider aria-hidden=\"true\" [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider>\n } @else {\n <div class=\"container\">\n <div class=\"line\" aria-hidden=\"true\"><mat-divider [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider></div>\n <div [class]=\"'label' + ' ' + size\">{{ label }}</div>\n <div class=\"line\" aria-hidden=\"true\"><mat-divider [class]=\"applicationTheme\" [ngClass]=\"size\"></mat-divider></div>\n </div>\n }\n</div>\n","import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { MatDividerModule } from '@angular/material/divider';\n\nimport { DividerComponent } from './divider.component';\n\n@NgModule({\n exports: [DividerComponent],\n imports: [CommonModule, MatDividerModule, DividerComponent],\n})\nexport class DividerComponentModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,14 @@
1
- import { FocusKeyManager } from '@angular/cdk/a11y';
2
1
  import { CdkOverlayOrigin, CdkConnectedOverlay, OverlayModule } from '@angular/cdk/overlay';
2
+ import { CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll, CdkVirtualForOf } from '@angular/cdk/scrolling';
3
3
  import { AsyncPipe, CommonModule } from '@angular/common';
4
4
  import * as i0 from '@angular/core';
5
- import { input, EventEmitter, signal, computed, ViewChildren, ViewChild, HostBinding, Input, Output, Optional, Inject, ChangeDetectionStrategy, ViewEncapsulation, Component, NgModule } from '@angular/core';
5
+ import { input, EventEmitter, signal, computed, ViewChild, HostBinding, Input, Output, Optional, Inject, ChangeDetectionStrategy, ViewEncapsulation, Component, NgModule } from '@angular/core';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
7
  import * as i1 from '@angular/forms';
7
8
  import { FormsModule } from '@angular/forms';
8
9
  import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
10
+ import { Subject } from 'rxjs';
11
+ import { debounceTime } from 'rxjs/operators';
9
12
  import { ButtonComponent, ButtonComponentModule } from '@testgorilla/tgo-ui/components/button';
10
13
  import { CheckboxComponent, CheckboxComponentModule } from '@testgorilla/tgo-ui/components/checkbox';
11
14
  import { IKeyboardEvent, EllipseTextDirective, UiTranslatePipe } from '@testgorilla/tgo-ui/components/core';
@@ -14,7 +17,7 @@ import { IconComponent, IconComponentModule } from '@testgorilla/tgo-ui/componen
14
17
  import { RadioButtonComponent, RadioButtonComponentModule } from '@testgorilla/tgo-ui/components/radio-button';
15
18
 
16
19
  class FilterButtonComponent {
17
- constructor(defaultAppTheme, cdr) {
20
+ constructor(defaultAppTheme, cdr, destroyRef) {
18
21
  this.defaultAppTheme = defaultAppTheme;
19
22
  this.cdr = cdr;
20
23
  this.class = 'ui-filter-button';
@@ -107,10 +110,14 @@ class FilterButtonComponent {
107
110
  * @memberof FilterButtonComponent
108
111
  */
109
112
  this.applicationTheme = 'light';
113
+ /** Must match the `.item` height in the SCSS — the fixed-size viewport relies on it. */
114
+ this.ITEM_SIZE = 48;
115
+ this.MAX_LIST_HEIGHT = 290;
110
116
  this.isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
111
117
  this.displayedLabel = signal('', ...(ngDevMode ? [{ debugName: "displayedLabel" }] : []));
112
118
  this.tooltip = signal('', ...(ngDevMode ? [{ debugName: "tooltip" }] : []));
113
119
  this.search = signal('', ...(ngDevMode ? [{ debugName: "search" }] : []));
120
+ this.activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
114
121
  this.showTooltip = {};
115
122
  this.displayedOptions = computed(() => {
116
123
  let lastSectionTitle;
@@ -122,11 +129,49 @@ class FilterButtonComponent {
122
129
  })
123
130
  .filter(option => option.label.toLowerCase().includes(this.search().toLowerCase()));
124
131
  }, ...(ngDevMode ? [{ debugName: "displayedOptions" }] : []));
132
+ // Section titles become their own rows so the fixed-size viewport can position every row with one `itemSize`.
133
+ this.flatOptions = computed(() => {
134
+ const rows = [];
135
+ let lastTitle;
136
+ for (const option of this.displayedOptions()) {
137
+ if (option.sectionTitle && option.sectionTitle !== lastTitle) {
138
+ lastTitle = option.sectionTitle;
139
+ rows.push({ kind: 'header', title: option.sectionTitle });
140
+ }
141
+ rows.push({ kind: 'option', option });
142
+ }
143
+ return rows;
144
+ }, ...(ngDevMode ? [{ debugName: "flatOptions" }] : []));
145
+ this.optionCount = computed(() => this.displayedOptions().length, ...(ngDevMode ? [{ debugName: "optionCount" }] : []));
146
+ this.activeOption = computed(() => {
147
+ const row = this.flatOptions()[this.activeIndex()];
148
+ return row?.kind === 'option' ? row.option : undefined;
149
+ }, ...(ngDevMode ? [{ debugName: "activeOption" }] : []));
150
+ // 1-based position of the active option (headers excluded) for the aria-live announcement.
151
+ this.activePosition = computed(() => {
152
+ const rows = this.flatOptions();
153
+ const activeIndex = this.activeIndex();
154
+ let position = 0;
155
+ for (let i = 0; i <= activeIndex && i < rows.length; i++) {
156
+ if (rows[i].kind === 'option') {
157
+ position++;
158
+ }
159
+ }
160
+ return position;
161
+ }, ...(ngDevMode ? [{ debugName: "activePosition" }] : []));
162
+ this.listHeight = computed(() => Math.min(Math.max(this.flatOptions().length * this.ITEM_SIZE, this.ITEM_SIZE), this.MAX_LIST_HEIGHT), ...(ngDevMode ? [{ debugName: "listHeight" }] : []));
163
+ this.trackByRow = (_index, row) => row.kind === 'header' ? `header:${row.title}` : `option:${row.option.value}`;
125
164
  this.MIN_OPTIONS_LENGTH_FOR_SEARCH = 8;
126
165
  this.translationContext = 'FILTER_BUTTON.';
166
+ this.searchInput$ = new Subject();
127
167
  if (this.defaultAppTheme) {
128
168
  this.applicationTheme = this.defaultAppTheme;
129
169
  }
170
+ this.searchInput$.pipe(debounceTime(150), takeUntilDestroyed(destroyRef)).subscribe(value => {
171
+ this.search.set(value);
172
+ this.activeIndex.set(this.firstOptionIndex());
173
+ this.cdr.markForCheck();
174
+ });
130
175
  }
131
176
  ngOnChanges(changes) {
132
177
  if (changes['value']) {
@@ -146,8 +191,7 @@ class FilterButtonComponent {
146
191
  toggle() {
147
192
  this.isOpen.set(!this.isOpen());
148
193
  if (this.isOpen()) {
149
- this.keyManager = new FocusKeyManager(this.filterOptions).withWrap();
150
- this.keyManager.setActiveItem(0);
194
+ this.activeIndex.set(this.firstOptionIndex());
151
195
  }
152
196
  this.cdr.markForCheck();
153
197
  }
@@ -171,14 +215,21 @@ class FilterButtonComponent {
171
215
  this.optionChange.emit({ selected, option });
172
216
  this.updateLabel();
173
217
  }
218
+ onSearchChange(value) {
219
+ this.searchInput$.next(value);
220
+ }
174
221
  keyDownHandler(ev) {
175
222
  if (ev.key === IKeyboardEvent.ARROW_DOWN) {
176
223
  ev.preventDefault();
177
- this.keyManager.setNextItemActive();
224
+ this.moveActive(1);
178
225
  }
179
226
  else if (ev.key === IKeyboardEvent.ARROW_UP) {
180
227
  ev.preventDefault();
181
- this.keyManager.setPreviousItemActive();
228
+ this.moveActive(-1);
229
+ }
230
+ else if (ev.key === IKeyboardEvent.ENTER || ev.key === IKeyboardEvent.SPACE) {
231
+ ev.preventDefault();
232
+ this.toggleActiveOption();
182
233
  }
183
234
  else if (ev.key === IKeyboardEvent.ESCAPE) {
184
235
  this.isOpen.set(false);
@@ -187,6 +238,34 @@ class FilterButtonComponent {
187
238
  onLabelEllipsisChange(isEllipsis, value) {
188
239
  this.showTooltip[value] = isEllipsis;
189
240
  }
241
+ firstOptionIndex() {
242
+ const index = this.flatOptions().findIndex(row => row.kind === 'option');
243
+ return index === -1 ? 0 : index;
244
+ }
245
+ moveActive(delta) {
246
+ const rows = this.flatOptions();
247
+ const optionIndexes = [];
248
+ rows.forEach((row, index) => {
249
+ if (row.kind === 'option') {
250
+ optionIndexes.push(index);
251
+ }
252
+ });
253
+ if (!optionIndexes.length) {
254
+ return;
255
+ }
256
+ const currentPos = optionIndexes.indexOf(this.activeIndex());
257
+ const basePos = currentPos === -1 ? 0 : currentPos;
258
+ const nextPos = (basePos + delta + optionIndexes.length) % optionIndexes.length;
259
+ const nextIndex = optionIndexes[nextPos];
260
+ this.activeIndex.set(nextIndex);
261
+ this.viewport?.scrollToIndex(nextIndex, 'smooth');
262
+ }
263
+ toggleActiveOption() {
264
+ const option = this.activeOption();
265
+ if (option) {
266
+ this.optionChanged(!this.isSelected(option.value), option);
267
+ }
268
+ }
190
269
  updateLabel() {
191
270
  this.value = this.value || [];
192
271
  let displayedLabel = '';
@@ -205,8 +284,8 @@ class FilterButtonComponent {
205
284
  this.tooltip.set(tooltip);
206
285
  this.cdr.markForCheck();
207
286
  }
208
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FilterButtonComponent, deps: [{ token: 'CANOPYUI_DEFAULT_APPLICATION_THEME', optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
209
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: FilterButtonComponent, isStandalone: true, selector: "ui-filter-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: false, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, allowClear: { classPropertyName: "allowClear", publicName: "allowClear", isSignal: false, isRequired: false, transformFunction: null }, singleSelection: { classPropertyName: "singleSelection", publicName: "singleSelection", isSignal: false, isRequired: false, transformFunction: null }, iconName: { classPropertyName: "iconName", publicName: "iconName", isSignal: false, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: false, isRequired: false, transformFunction: null }, noOptionsMessage: { classPropertyName: "noOptionsMessage", publicName: "noOptionsMessage", isSignal: false, isRequired: false, transformFunction: null }, applicationTheme: { classPropertyName: "applicationTheme", publicName: "applicationTheme", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { changed: "changed", optionChange: "optionChange" }, host: { properties: { "class": "this.class", "attr.theme": "this.applicationTheme" } }, viewQueries: [{ propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true }, { propertyName: "filterOptions", predicate: ["filterOption"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container>\n <ui-button\n [matTooltip]=\"tooltip()\"\n [matTooltipClass]=\"'tooltip-multi-line ' + applicationTheme\"\n [matTooltipPosition]=\"'above'\"\n class=\"ui-filter-button-trigger\"\n [class.button-opened]=\"isOpen()\"\n [variant]=\"value.length || isOpen() ? 'secondary' : 'ghost'\"\n [label]=\"displayedLabel() || label\"\n [disabled]=\"disabled || (options().length === 0 && !noOptionsMessage)\"\n (click)=\"toggle()\"\n type=\"button\"\n size=\"small\"\n [role]=\"'combobox'\"\n [tooltip]=\"value.length ? ' ' : ''\"\n [attr.aria-expanded]=\"isOpen()\"\n [iconName]=\"iconName || (isOpen() ? 'Arrow-chevron-up-filled' : 'Arrow-chevron-down-filled')\"\n [iconPosition]=\"iconPosition\"\n [applicationTheme]=\"applicationTheme\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n ></ui-button>\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n (overlayOutsideClick)=\"outsideClick()\"\n >\n @if (displayedOptions().length) {\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ displayedOptions()[keyManager.activeItemIndex ?? 0].label }}\n {{\n translationContext + 'ACTIVE_ITEM'\n | uiTranslate: { active: (keyManager.activeItemIndex ?? 0) + 1, total: filterOptions.length }\n | async\n }}\n </div>\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ displayedOptions()[keyManager.activeItemIndex ?? 0].label }}\n {{\n (value.includes(displayedOptions()[keyManager.activeItemIndex ?? 0].value)\n ? translationContext + 'SELECTED'\n : translationContext + 'DESELECTED'\n )\n | uiTranslate\n | async\n }}\n </div>\n }\n <div class=\"ui-filter-button\" #overlayContainer>\n @if (options().length > MIN_OPTIONS_LENGTH_FOR_SEARCH) {\n <div class=\"ui-filter-button-header\">\n <ui-field\n type=\"search\"\n [showBottomContent]=\"false\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-field>\n </div>\n }\n <div class=\"ui-filter-button-list\" #list tabindex=\"0\" (keydown)=\"keyDownHandler($event)\">\n @if (!displayedOptions().length && options().length) {\n <div class=\"no-results-found\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </div>\n } @else {\n @for (section of displayedOptions(); track section; let i = $index) {\n <div>\n @if (section.sectionTitle && section.sectionTitle !== displayedOptions()[i - 1]?.sectionTitle) {\n <div class=\"section-title\">\n {{ section.sectionTitle }}\n </div>\n }\n <div\n class=\"item\"\n [class.item-selected]=\"isSelected(section.value)\"\n tabindex=\"0\"\n (click)=\"optionChanged(!isSelected(section.value), section)\"\n (keydown.enter)=\"optionChanged(!isSelected(section.value), section)\"\n >\n @if (singleSelection) {\n <ui-radio-button\n (click)=\"$event.stopPropagation()\"\n [tabIndex]=\"i === 0 ? 0 : -1\"\n [selected]=\"isSelected(section.value)\"\n (changeRadio)=\"optionChanged($event.optionChecked, section)\"\n [applicationTheme]=\"applicationTheme\"\n #filterOption\n >\n </ui-radio-button>\n }\n @if (!singleSelection) {\n <ui-checkbox\n (click)=\"$event.stopPropagation()\"\n [class.with-label-number]=\"section.labelNumber !== undefined\"\n [tabIndex]=\"i === 0 ? 0 : -1\"\n [checked]=\"isSelected(section.value)\"\n (changed)=\"optionChanged($event, section)\"\n [applicationTheme]=\"applicationTheme\"\n #filterOption\n ></ui-checkbox>\n }\n @if (section.icon) {\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"section.icon\"></ui-icon>\n }\n <span\n #tooltip=\"matTooltip\"\n class=\"item-label\"\n uiEllipseText\n (onChangeTextState)=\"onLabelEllipsisChange($event, section.value)\"\n [matTooltip]=\"showTooltip[section.value] ? section.label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >\n {{ section.label }}\n </span>\n @if (section.labelNumber !== undefined) {\n <span class=\"item-number\">({{ section.labelNumber }})</span>\n }\n </div>\n </div>\n }\n }\n </div>\n @if (!options().length && !!noOptionsMessage) {\n <div class=\"ui-filter-button-no-options\" [innerHTML]=\"noOptionsMessage\"></div>\n } @else {\n @if (allowClear || message) {\n <div class=\"ui-filter-button-footer\">\n @if (allowClear) {\n <ui-button\n variant=\"text\"\n size=\"small\"\n [label]=\"('COMMON.RESET' | uiTranslate | async)!\"\n (click)=\"clearValue()\"\n [applicationTheme]=\"applicationTheme\"\n (keydown.escape)=\"toggle()\"\n ></ui-button>\n }\n <div class=\"ui-filter-button-footer-message\">\n {{ message }}\n </div>\n </div>\n }\n }\n </div>\n </ng-template>\n</ng-container>\n", styles: [".bg-teal-60b{background:#1c443c}.bg-teal-30b{background:#31766a}.bg-teal-default{background:#46a997}.bg-teal-30w{background:#7ec3b6}.bg-teal-60w{background:#b5ddd5}.bg-teal-secondary{background:#cbd6cb}.bg-teal-90w{background:#ecf6f5}.bg-petrol-60b{background:#102930}.bg-petrol-30b{background:#1b4754}.bg-petrol-default{background:#276678}.bg-petrol-30w{background:#6894a0}.bg-petrol-60w{background:#a9c2c9}.bg-petrol-secondary{background:#c8d7de}.bg-petrol-90w{background:#e9f0f1}.bg-error-60b{background:#513131}.bg-error-30b{background:#8e5655}.bg-error-60w{background:#e3c3c6}.bg-error-secondary{background:#f0dad9}.bg-error-default{background:#cb7b7a}.bg-warning-secondary{background:#f0d6bb}.bg-warning-default{background:#cca45f}.bg-black{background:#000}.bg-dark{background:#888}.bg-medium{background:#e0e0e0}.bg-grey{background:#ededed}.bg-light{background:#f6f6f6}.bg-white{background:#fff}.bg-box-shadow{background:#00000014}.bg-navigation-subtitle{background:#528593}.bgc-teal-60b{background-color:#1c443c}.bgc-teal-30b{background-color:#31766a}.bgc-teal-default{background-color:#46a997}.bgc-teal-30w{background-color:#7ec3b6}.bgc-teal-60w{background-color:#b5ddd5}.bgc-teal-secondary{background-color:#cbd6cb}.bgc-teal-90w{background-color:#ecf6f5}.bgc-petrol-60b{background-color:#102930}.bgc-petrol-30b{background-color:#1b4754}.bgc-petrol-default{background-color:#276678}.bgc-petrol-30w{background-color:#6894a0}.bgc-petrol-60w{background-color:#a9c2c9}.bgc-petrol-secondary{background-color:#c8d7de}.bgc-petrol-90w{background-color:#e9f0f1}.bgc-error-60b{background-color:#513131}.bgc-error-30b{background-color:#8e5655}.bgc-error-60w{background-color:#e3c3c6}.bgc-error-secondary{background-color:#f0dad9}.bgc-error-default{background-color:#cb7b7a}.bgc-warning-secondary{background-color:#f0d6bb}.bgc-warning-default{background-color:#cca45f}.bgc-black{background-color:#000}.bgc-dark{background-color:#888}.bgc-medium{background-color:#e0e0e0}.bgc-grey{background-color:#ededed}.bgc-light{background-color:#f6f6f6}.bgc-white{background-color:#fff}.bgc-box-shadow{background-color:#00000014}.bgc-navigation-subtitle{background-color:#528593}ui-filter-button{display:inline-block}ui-filter-button ui-button .button-wrapper button{padding:7px 24px!important}ui-filter-button ui-button .button-wrapper button .icon ui-icon mat-icon.size-16 svg{width:24px;height:24px}ui-filter-button ui-button .button-wrapper button .label{line-clamp:1;-webkit-line-clamp:1!important}ui-filter-button ui-button.button-opened .button-wrapper button{background-color:#666!important;border-color:#666}ui-filter-button ui-button button.secondary{border:1px solid #242424}ui-filter-button ui-button button.secondary:hover{border-color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button{margin-top:4px;box-shadow:0 4px 16px #24242414;border-radius:10px;background-color:#fff;padding:0 0 8px;width:240px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-header{padding:8px 16px 0}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list{max-height:290px;overflow-y:auto;overflow-x:hidden}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .section-title,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .no-results-found{padding:8px 16px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item{padding:0 16px 0 5px;display:flex;align-items:center;gap:5px;height:48px;cursor:pointer}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item:hover,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item.item-selected{background-color:#f4f4f4}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox{max-width:100%}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox.with-label-number{max-width:calc(100% - 25px)}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-number{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-label{-webkit-user-select:none;user-select:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer{display:flex;justify-content:space-between;align-items:center;padding:0 16px 0 5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer ui-button{margin-left:-5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer-message{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-no-options{padding:24px}.tooltip-multi-line .mdc-tooltip__surface{text-align:left;white-space:pre-line}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "ui-button", inputs: ["size", "variant", "label", "iconPosition", "justIcon", "iconName", "disabled", "loading", "loadingWithLabel", "fullWidth", "url", "urlTarget", "value", "tooltip", "isPremium", "type", "companyColor", "buttonBadgeConfig", "applicationTheme", "disabledScaleOnClick", "ariaLabel", "ariaExpanded", "ariaControls", "ariaPressed", "ariaRequired", "ariaLabelledby", "ariaDescribedby", "preventDefault", "hasBackground", "tooltipPosition", "role", "iconFilled"], outputs: ["buttonClickEvent", "buttonHoverEvent"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: FieldComponent, selector: "ui-field", inputs: ["fullWidth", "fullHeight", "label", "labelHtml", "labelIcon", "fieldName", "placeholder", "id", "value", "badgeVariant", "errors", "disabled", "required", "readOnly", "hintMessage", "type", "updateOnBlur", "allowOnlyDigits", "isAutocompleteOff", "allowNegative", "showBottomContent", "applicationTheme", "ariaLabel", "loading", "isValid", "maxCharacters", "trimOnBlur", "trimOnSubmit", "maxRows", "hasTextAreaCounter", "hideBuiltInErrors", "hideLabelInErrors", "max", "min", "textareaHeight", "borderless", "autosizableTextarea", "isAIVariant", "ariaLabelledby", "ariaDescribedby", "hasError"], outputs: ["validateEvent", "fieldBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: RadioButtonComponent, selector: "ui-radio-button", inputs: ["disabled", "selected", "allowUnselect", "companyColor", "name", "label", "multiple", "mediaCard", "value", "applicationTheme", "ariaLabel", "ariaRequired", "hasError", "ariaLabelledby", "ariaDescribedby", "truncateText", "alignment", "tabIndex", "hideTooltipOnEllipsis"], outputs: ["changeRadio"] }, { kind: "component", type: CheckboxComponent, selector: "ui-checkbox", inputs: ["disabled", "checked", "indeterminate", "companyColor", "name", "label", "multiple", "applicationTheme", "ariaLabel", "ariaRequired", "hasError", "hideBuiltInErrors", "hideLabelInErrors", "ariaLabelledby", "ariaDescribedby", "truncateText", "alignment", "tabIndex"], outputs: ["changed"] }, { kind: "component", type: IconComponent, selector: "ui-icon", inputs: ["size", "cssClass", "name", "color", "filled", "toggleIconStyle", "applicationTheme", "useFullIconName"] }, { kind: "directive", type: EllipseTextDirective, selector: "[uiEllipseText]", inputs: ["isMultiline", "maxDiffPixels", "refresh"], outputs: ["onChangeTextState"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: UiTranslatePipe, name: "uiTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
287
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FilterButtonComponent, deps: [{ token: 'CANOPYUI_DEFAULT_APPLICATION_THEME', optional: true }, { token: i0.ChangeDetectorRef }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component }); }
288
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: FilterButtonComponent, isStandalone: true, selector: "ui-filter-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: false, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, allowClear: { classPropertyName: "allowClear", publicName: "allowClear", isSignal: false, isRequired: false, transformFunction: null }, singleSelection: { classPropertyName: "singleSelection", publicName: "singleSelection", isSignal: false, isRequired: false, transformFunction: null }, iconName: { classPropertyName: "iconName", publicName: "iconName", isSignal: false, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: false, isRequired: false, transformFunction: null }, noOptionsMessage: { classPropertyName: "noOptionsMessage", publicName: "noOptionsMessage", isSignal: false, isRequired: false, transformFunction: null }, applicationTheme: { classPropertyName: "applicationTheme", publicName: "applicationTheme", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { changed: "changed", optionChange: "optionChange" }, host: { properties: { "class": "this.class", "attr.theme": "this.applicationTheme" } }, viewQueries: [{ propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true }, { propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container>\n <ui-button\n [matTooltip]=\"tooltip()\"\n [matTooltipClass]=\"'tooltip-multi-line ' + applicationTheme\"\n [matTooltipPosition]=\"'above'\"\n class=\"ui-filter-button-trigger\"\n [class.button-opened]=\"isOpen()\"\n [variant]=\"value.length || isOpen() ? 'secondary' : 'ghost'\"\n [label]=\"displayedLabel() || label\"\n [disabled]=\"disabled || (options().length === 0 && !noOptionsMessage)\"\n (click)=\"toggle()\"\n type=\"button\"\n size=\"small\"\n [role]=\"'combobox'\"\n [tooltip]=\"value.length ? ' ' : ''\"\n [attr.aria-expanded]=\"isOpen()\"\n [iconName]=\"iconName || (isOpen() ? 'Arrow-chevron-up-filled' : 'Arrow-chevron-down-filled')\"\n [iconPosition]=\"iconPosition\"\n [applicationTheme]=\"applicationTheme\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n ></ui-button>\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n (overlayOutsideClick)=\"outsideClick()\"\n >\n @if (activeOption(); as active) {\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ active.label }}\n {{\n translationContext + 'ACTIVE_ITEM' | uiTranslate: { active: activePosition(), total: optionCount() } | async\n }}\n </div>\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ active.label }}\n {{\n (value.includes(active.value) ? translationContext + 'SELECTED' : translationContext + 'DESELECTED')\n | uiTranslate\n | async\n }}\n </div>\n }\n <div class=\"ui-filter-button\" #overlayContainer>\n @if (options().length > MIN_OPTIONS_LENGTH_FOR_SEARCH) {\n <div class=\"ui-filter-button-header\">\n <ui-field\n type=\"search\"\n [showBottomContent]=\"false\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"onSearchChange($event)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-field>\n </div>\n }\n <div\n class=\"ui-filter-button-list\"\n #list\n tabindex=\"0\"\n role=\"listbox\"\n [attr.aria-activedescendant]=\"isOpen() && activeOption() ? 'ui-fb-option-' + activeIndex() : null\"\n (keydown)=\"keyDownHandler($event)\"\n >\n @if (!displayedOptions().length && options().length) {\n <div class=\"no-results-found\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </div>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"ui-filter-button-viewport\"\n [itemSize]=\"ITEM_SIZE\"\n [style.height.px]=\"listHeight()\"\n >\n <ng-container *cdkVirtualFor=\"let row of flatOptions(); let i = index; trackBy: trackByRow\">\n @if (row.kind === 'header') {\n <div class=\"section-title\">\n {{ row.title }}\n </div>\n } @else {\n <div\n class=\"item\"\n role=\"option\"\n tabindex=\"-1\"\n [id]=\"'ui-fb-option-' + i\"\n [class.item-selected]=\"isSelected(row.option.value)\"\n [class.item-active]=\"activeIndex() === i\"\n [attr.aria-selected]=\"isSelected(row.option.value)\"\n (click)=\"activeIndex.set(i); optionChanged(!isSelected(row.option.value), row.option)\"\n (keydown.enter)=\"optionChanged(!isSelected(row.option.value), row.option)\"\n >\n @if (singleSelection) {\n <ui-radio-button\n (click)=\"$event.stopPropagation()\"\n [tabIndex]=\"-1\"\n [selected]=\"isSelected(row.option.value)\"\n (changeRadio)=\"optionChanged($event.optionChecked, row.option)\"\n [applicationTheme]=\"applicationTheme\"\n >\n </ui-radio-button>\n }\n @if (!singleSelection) {\n <ui-checkbox\n (click)=\"$event.stopPropagation()\"\n [class.with-label-number]=\"row.option.labelNumber !== undefined\"\n [tabIndex]=\"-1\"\n [checked]=\"isSelected(row.option.value)\"\n (changed)=\"optionChanged($event, row.option)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-checkbox>\n }\n @if (row.option.icon) {\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"row.option.icon\"></ui-icon>\n }\n <span\n #tooltip=\"matTooltip\"\n class=\"item-label\"\n uiEllipseText\n [refresh]=\"row.option.value\"\n (onChangeTextState)=\"onLabelEllipsisChange($event, row.option.value)\"\n [matTooltip]=\"showTooltip[row.option.value] ? row.option.label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >\n {{ row.option.label }}\n </span>\n @if (row.option.labelNumber !== undefined) {\n <span class=\"item-number\">({{ row.option.labelNumber }})</span>\n }\n </div>\n }\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </div>\n @if (!options().length && !!noOptionsMessage) {\n <div class=\"ui-filter-button-no-options\" [innerHTML]=\"noOptionsMessage\"></div>\n } @else {\n @if (allowClear || message) {\n <div class=\"ui-filter-button-footer\">\n @if (allowClear) {\n <ui-button\n variant=\"text\"\n size=\"small\"\n [label]=\"('COMMON.RESET' | uiTranslate | async)!\"\n (click)=\"clearValue()\"\n [applicationTheme]=\"applicationTheme\"\n (keydown.escape)=\"toggle()\"\n ></ui-button>\n }\n <div class=\"ui-filter-button-footer-message\">\n {{ message }}\n </div>\n </div>\n }\n }\n </div>\n </ng-template>\n</ng-container>\n", styles: [".bg-teal-60b{background:#1c443c}.bg-teal-30b{background:#31766a}.bg-teal-default{background:#46a997}.bg-teal-30w{background:#7ec3b6}.bg-teal-60w{background:#b5ddd5}.bg-teal-secondary{background:#cbd6cb}.bg-teal-90w{background:#ecf6f5}.bg-petrol-60b{background:#102930}.bg-petrol-30b{background:#1b4754}.bg-petrol-default{background:#276678}.bg-petrol-30w{background:#6894a0}.bg-petrol-60w{background:#a9c2c9}.bg-petrol-secondary{background:#c8d7de}.bg-petrol-90w{background:#e9f0f1}.bg-error-60b{background:#513131}.bg-error-30b{background:#8e5655}.bg-error-60w{background:#e3c3c6}.bg-error-secondary{background:#f0dad9}.bg-error-default{background:#cb7b7a}.bg-warning-secondary{background:#f0d6bb}.bg-warning-default{background:#cca45f}.bg-black{background:#000}.bg-dark{background:#888}.bg-medium{background:#e0e0e0}.bg-grey{background:#ededed}.bg-light{background:#f6f6f6}.bg-white{background:#fff}.bg-box-shadow{background:#00000014}.bg-navigation-subtitle{background:#528593}.bgc-teal-60b{background-color:#1c443c}.bgc-teal-30b{background-color:#31766a}.bgc-teal-default{background-color:#46a997}.bgc-teal-30w{background-color:#7ec3b6}.bgc-teal-60w{background-color:#b5ddd5}.bgc-teal-secondary{background-color:#cbd6cb}.bgc-teal-90w{background-color:#ecf6f5}.bgc-petrol-60b{background-color:#102930}.bgc-petrol-30b{background-color:#1b4754}.bgc-petrol-default{background-color:#276678}.bgc-petrol-30w{background-color:#6894a0}.bgc-petrol-60w{background-color:#a9c2c9}.bgc-petrol-secondary{background-color:#c8d7de}.bgc-petrol-90w{background-color:#e9f0f1}.bgc-error-60b{background-color:#513131}.bgc-error-30b{background-color:#8e5655}.bgc-error-60w{background-color:#e3c3c6}.bgc-error-secondary{background-color:#f0dad9}.bgc-error-default{background-color:#cb7b7a}.bgc-warning-secondary{background-color:#f0d6bb}.bgc-warning-default{background-color:#cca45f}.bgc-black{background-color:#000}.bgc-dark{background-color:#888}.bgc-medium{background-color:#e0e0e0}.bgc-grey{background-color:#ededed}.bgc-light{background-color:#f6f6f6}.bgc-white{background-color:#fff}.bgc-box-shadow{background-color:#00000014}.bgc-navigation-subtitle{background-color:#528593}ui-filter-button{display:inline-block}ui-filter-button ui-button .button-wrapper button{padding:7px 24px!important}ui-filter-button ui-button .button-wrapper button .icon ui-icon mat-icon.size-16 svg{width:24px;height:24px}ui-filter-button ui-button .button-wrapper button .label{line-clamp:1;-webkit-line-clamp:1!important}ui-filter-button ui-button.button-opened .button-wrapper button{background-color:#666!important;border-color:#666}ui-filter-button ui-button button.secondary{border:1px solid #242424}ui-filter-button ui-button button.secondary:hover{border-color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button{margin-top:4px;box-shadow:0 4px 16px #24242414;border-radius:10px;background-color:#fff;padding:0 0 8px;width:240px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-header{padding:8px 16px 0}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .section-title{display:flex;align-items:center;height:48px;padding:0 16px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .no-results-found{padding:8px 16px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item{padding:0 16px 0 5px;display:flex;align-items:center;gap:5px;height:48px;cursor:pointer}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item:hover,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item.item-active,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item.item-selected{background-color:#f4f4f4}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox{max-width:100%}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox.with-label-number{max-width:calc(100% - 25px)}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-number{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-label{-webkit-user-select:none;user-select:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-viewport{width:100%;max-height:290px;overflow-x:hidden}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-viewport .cdk-virtual-scroll-content-wrapper{max-width:100%}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer{display:flex;justify-content:space-between;align-items:center;padding:0 16px 0 5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer ui-button{margin-left:-5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer-message{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-no-options{padding:24px}.tooltip-multi-line .mdc-tooltip__surface{text-align:left;white-space:pre-line}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "ui-button", inputs: ["size", "variant", "label", "iconPosition", "justIcon", "iconName", "disabled", "loading", "loadingWithLabel", "fullWidth", "url", "urlTarget", "value", "tooltip", "isPremium", "type", "companyColor", "buttonBadgeConfig", "applicationTheme", "disabledScaleOnClick", "ariaLabel", "ariaExpanded", "ariaControls", "ariaPressed", "ariaRequired", "ariaLabelledby", "ariaDescribedby", "preventDefault", "hasBackground", "tooltipPosition", "role", "iconFilled"], outputs: ["buttonClickEvent", "buttonHoverEvent"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: FieldComponent, selector: "ui-field", inputs: ["fullWidth", "fullHeight", "label", "labelHtml", "labelIcon", "fieldName", "placeholder", "id", "value", "badgeVariant", "errors", "disabled", "required", "readOnly", "hintMessage", "type", "updateOnBlur", "allowOnlyDigits", "isAutocompleteOff", "allowNegative", "showBottomContent", "applicationTheme", "ariaLabel", "loading", "isValid", "maxCharacters", "trimOnBlur", "trimOnSubmit", "maxRows", "hasTextAreaCounter", "hideBuiltInErrors", "hideLabelInErrors", "max", "min", "textareaHeight", "borderless", "autosizableTextarea", "isAIVariant", "ariaLabelledby", "ariaDescribedby", "hasError"], outputs: ["validateEvent", "fieldBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: RadioButtonComponent, selector: "ui-radio-button", inputs: ["disabled", "selected", "allowUnselect", "companyColor", "name", "label", "multiple", "mediaCard", "value", "applicationTheme", "ariaLabel", "ariaRequired", "hasError", "ariaLabelledby", "ariaDescribedby", "truncateText", "alignment", "tabIndex", "hideTooltipOnEllipsis"], outputs: ["changeRadio"] }, { kind: "component", type: CheckboxComponent, selector: "ui-checkbox", inputs: ["disabled", "checked", "indeterminate", "companyColor", "name", "label", "multiple", "applicationTheme", "ariaLabel", "ariaRequired", "hasError", "hideBuiltInErrors", "hideLabelInErrors", "ariaLabelledby", "ariaDescribedby", "truncateText", "alignment", "tabIndex"], outputs: ["changed"] }, { kind: "component", type: IconComponent, selector: "ui-icon", inputs: ["size", "cssClass", "name", "color", "filled", "toggleIconStyle", "applicationTheme", "useFullIconName"] }, { kind: "directive", type: EllipseTextDirective, selector: "[uiEllipseText]", inputs: ["isMultiline", "maxDiffPixels", "refresh"], outputs: ["onChangeTextState"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: UiTranslatePipe, name: "uiTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
210
289
  }
211
290
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FilterButtonComponent, decorators: [{
212
291
  type: Component,
@@ -215,6 +294,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
215
294
  CdkOverlayOrigin,
216
295
  MatTooltip,
217
296
  CdkConnectedOverlay,
297
+ CdkVirtualScrollViewport,
298
+ CdkFixedSizeVirtualScroll,
299
+ CdkVirtualForOf,
218
300
  FieldComponent,
219
301
  FormsModule,
220
302
  RadioButtonComponent,
@@ -223,13 +305,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
223
305
  EllipseTextDirective,
224
306
  AsyncPipe,
225
307
  UiTranslatePipe,
226
- ], template: "<ng-container>\n <ui-button\n [matTooltip]=\"tooltip()\"\n [matTooltipClass]=\"'tooltip-multi-line ' + applicationTheme\"\n [matTooltipPosition]=\"'above'\"\n class=\"ui-filter-button-trigger\"\n [class.button-opened]=\"isOpen()\"\n [variant]=\"value.length || isOpen() ? 'secondary' : 'ghost'\"\n [label]=\"displayedLabel() || label\"\n [disabled]=\"disabled || (options().length === 0 && !noOptionsMessage)\"\n (click)=\"toggle()\"\n type=\"button\"\n size=\"small\"\n [role]=\"'combobox'\"\n [tooltip]=\"value.length ? ' ' : ''\"\n [attr.aria-expanded]=\"isOpen()\"\n [iconName]=\"iconName || (isOpen() ? 'Arrow-chevron-up-filled' : 'Arrow-chevron-down-filled')\"\n [iconPosition]=\"iconPosition\"\n [applicationTheme]=\"applicationTheme\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n ></ui-button>\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n (overlayOutsideClick)=\"outsideClick()\"\n >\n @if (displayedOptions().length) {\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ displayedOptions()[keyManager.activeItemIndex ?? 0].label }}\n {{\n translationContext + 'ACTIVE_ITEM'\n | uiTranslate: { active: (keyManager.activeItemIndex ?? 0) + 1, total: filterOptions.length }\n | async\n }}\n </div>\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ displayedOptions()[keyManager.activeItemIndex ?? 0].label }}\n {{\n (value.includes(displayedOptions()[keyManager.activeItemIndex ?? 0].value)\n ? translationContext + 'SELECTED'\n : translationContext + 'DESELECTED'\n )\n | uiTranslate\n | async\n }}\n </div>\n }\n <div class=\"ui-filter-button\" #overlayContainer>\n @if (options().length > MIN_OPTIONS_LENGTH_FOR_SEARCH) {\n <div class=\"ui-filter-button-header\">\n <ui-field\n type=\"search\"\n [showBottomContent]=\"false\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-field>\n </div>\n }\n <div class=\"ui-filter-button-list\" #list tabindex=\"0\" (keydown)=\"keyDownHandler($event)\">\n @if (!displayedOptions().length && options().length) {\n <div class=\"no-results-found\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </div>\n } @else {\n @for (section of displayedOptions(); track section; let i = $index) {\n <div>\n @if (section.sectionTitle && section.sectionTitle !== displayedOptions()[i - 1]?.sectionTitle) {\n <div class=\"section-title\">\n {{ section.sectionTitle }}\n </div>\n }\n <div\n class=\"item\"\n [class.item-selected]=\"isSelected(section.value)\"\n tabindex=\"0\"\n (click)=\"optionChanged(!isSelected(section.value), section)\"\n (keydown.enter)=\"optionChanged(!isSelected(section.value), section)\"\n >\n @if (singleSelection) {\n <ui-radio-button\n (click)=\"$event.stopPropagation()\"\n [tabIndex]=\"i === 0 ? 0 : -1\"\n [selected]=\"isSelected(section.value)\"\n (changeRadio)=\"optionChanged($event.optionChecked, section)\"\n [applicationTheme]=\"applicationTheme\"\n #filterOption\n >\n </ui-radio-button>\n }\n @if (!singleSelection) {\n <ui-checkbox\n (click)=\"$event.stopPropagation()\"\n [class.with-label-number]=\"section.labelNumber !== undefined\"\n [tabIndex]=\"i === 0 ? 0 : -1\"\n [checked]=\"isSelected(section.value)\"\n (changed)=\"optionChanged($event, section)\"\n [applicationTheme]=\"applicationTheme\"\n #filterOption\n ></ui-checkbox>\n }\n @if (section.icon) {\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"section.icon\"></ui-icon>\n }\n <span\n #tooltip=\"matTooltip\"\n class=\"item-label\"\n uiEllipseText\n (onChangeTextState)=\"onLabelEllipsisChange($event, section.value)\"\n [matTooltip]=\"showTooltip[section.value] ? section.label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >\n {{ section.label }}\n </span>\n @if (section.labelNumber !== undefined) {\n <span class=\"item-number\">({{ section.labelNumber }})</span>\n }\n </div>\n </div>\n }\n }\n </div>\n @if (!options().length && !!noOptionsMessage) {\n <div class=\"ui-filter-button-no-options\" [innerHTML]=\"noOptionsMessage\"></div>\n } @else {\n @if (allowClear || message) {\n <div class=\"ui-filter-button-footer\">\n @if (allowClear) {\n <ui-button\n variant=\"text\"\n size=\"small\"\n [label]=\"('COMMON.RESET' | uiTranslate | async)!\"\n (click)=\"clearValue()\"\n [applicationTheme]=\"applicationTheme\"\n (keydown.escape)=\"toggle()\"\n ></ui-button>\n }\n <div class=\"ui-filter-button-footer-message\">\n {{ message }}\n </div>\n </div>\n }\n }\n </div>\n </ng-template>\n</ng-container>\n", styles: [".bg-teal-60b{background:#1c443c}.bg-teal-30b{background:#31766a}.bg-teal-default{background:#46a997}.bg-teal-30w{background:#7ec3b6}.bg-teal-60w{background:#b5ddd5}.bg-teal-secondary{background:#cbd6cb}.bg-teal-90w{background:#ecf6f5}.bg-petrol-60b{background:#102930}.bg-petrol-30b{background:#1b4754}.bg-petrol-default{background:#276678}.bg-petrol-30w{background:#6894a0}.bg-petrol-60w{background:#a9c2c9}.bg-petrol-secondary{background:#c8d7de}.bg-petrol-90w{background:#e9f0f1}.bg-error-60b{background:#513131}.bg-error-30b{background:#8e5655}.bg-error-60w{background:#e3c3c6}.bg-error-secondary{background:#f0dad9}.bg-error-default{background:#cb7b7a}.bg-warning-secondary{background:#f0d6bb}.bg-warning-default{background:#cca45f}.bg-black{background:#000}.bg-dark{background:#888}.bg-medium{background:#e0e0e0}.bg-grey{background:#ededed}.bg-light{background:#f6f6f6}.bg-white{background:#fff}.bg-box-shadow{background:#00000014}.bg-navigation-subtitle{background:#528593}.bgc-teal-60b{background-color:#1c443c}.bgc-teal-30b{background-color:#31766a}.bgc-teal-default{background-color:#46a997}.bgc-teal-30w{background-color:#7ec3b6}.bgc-teal-60w{background-color:#b5ddd5}.bgc-teal-secondary{background-color:#cbd6cb}.bgc-teal-90w{background-color:#ecf6f5}.bgc-petrol-60b{background-color:#102930}.bgc-petrol-30b{background-color:#1b4754}.bgc-petrol-default{background-color:#276678}.bgc-petrol-30w{background-color:#6894a0}.bgc-petrol-60w{background-color:#a9c2c9}.bgc-petrol-secondary{background-color:#c8d7de}.bgc-petrol-90w{background-color:#e9f0f1}.bgc-error-60b{background-color:#513131}.bgc-error-30b{background-color:#8e5655}.bgc-error-60w{background-color:#e3c3c6}.bgc-error-secondary{background-color:#f0dad9}.bgc-error-default{background-color:#cb7b7a}.bgc-warning-secondary{background-color:#f0d6bb}.bgc-warning-default{background-color:#cca45f}.bgc-black{background-color:#000}.bgc-dark{background-color:#888}.bgc-medium{background-color:#e0e0e0}.bgc-grey{background-color:#ededed}.bgc-light{background-color:#f6f6f6}.bgc-white{background-color:#fff}.bgc-box-shadow{background-color:#00000014}.bgc-navigation-subtitle{background-color:#528593}ui-filter-button{display:inline-block}ui-filter-button ui-button .button-wrapper button{padding:7px 24px!important}ui-filter-button ui-button .button-wrapper button .icon ui-icon mat-icon.size-16 svg{width:24px;height:24px}ui-filter-button ui-button .button-wrapper button .label{line-clamp:1;-webkit-line-clamp:1!important}ui-filter-button ui-button.button-opened .button-wrapper button{background-color:#666!important;border-color:#666}ui-filter-button ui-button button.secondary{border:1px solid #242424}ui-filter-button ui-button button.secondary:hover{border-color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button{margin-top:4px;box-shadow:0 4px 16px #24242414;border-radius:10px;background-color:#fff;padding:0 0 8px;width:240px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-header{padding:8px 16px 0}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list{max-height:290px;overflow-y:auto;overflow-x:hidden}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .section-title,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .no-results-found{padding:8px 16px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item{padding:0 16px 0 5px;display:flex;align-items:center;gap:5px;height:48px;cursor:pointer}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item:hover,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item.item-selected{background-color:#f4f4f4}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox{max-width:100%}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox.with-label-number{max-width:calc(100% - 25px)}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-number{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-label{-webkit-user-select:none;user-select:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer{display:flex;justify-content:space-between;align-items:center;padding:0 16px 0 5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer ui-button{margin-left:-5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer-message{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-no-options{padding:24px}.tooltip-multi-line .mdc-tooltip__surface{text-align:left;white-space:pre-line}\n"] }]
308
+ ], template: "<ng-container>\n <ui-button\n [matTooltip]=\"tooltip()\"\n [matTooltipClass]=\"'tooltip-multi-line ' + applicationTheme\"\n [matTooltipPosition]=\"'above'\"\n class=\"ui-filter-button-trigger\"\n [class.button-opened]=\"isOpen()\"\n [variant]=\"value.length || isOpen() ? 'secondary' : 'ghost'\"\n [label]=\"displayedLabel() || label\"\n [disabled]=\"disabled || (options().length === 0 && !noOptionsMessage)\"\n (click)=\"toggle()\"\n type=\"button\"\n size=\"small\"\n [role]=\"'combobox'\"\n [tooltip]=\"value.length ? ' ' : ''\"\n [attr.aria-expanded]=\"isOpen()\"\n [iconName]=\"iconName || (isOpen() ? 'Arrow-chevron-up-filled' : 'Arrow-chevron-down-filled')\"\n [iconPosition]=\"iconPosition\"\n [applicationTheme]=\"applicationTheme\"\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n ></ui-button>\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n (overlayOutsideClick)=\"outsideClick()\"\n >\n @if (activeOption(); as active) {\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ active.label }}\n {{\n translationContext + 'ACTIVE_ITEM' | uiTranslate: { active: activePosition(), total: optionCount() } | async\n }}\n </div>\n <div style=\"position: absolute; left: -9999px\" [attr.aria-live]=\"'polite'\">\n {{ active.label }}\n {{\n (value.includes(active.value) ? translationContext + 'SELECTED' : translationContext + 'DESELECTED')\n | uiTranslate\n | async\n }}\n </div>\n }\n <div class=\"ui-filter-button\" #overlayContainer>\n @if (options().length > MIN_OPTIONS_LENGTH_FOR_SEARCH) {\n <div class=\"ui-filter-button-header\">\n <ui-field\n type=\"search\"\n [showBottomContent]=\"false\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"onSearchChange($event)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-field>\n </div>\n }\n <div\n class=\"ui-filter-button-list\"\n #list\n tabindex=\"0\"\n role=\"listbox\"\n [attr.aria-activedescendant]=\"isOpen() && activeOption() ? 'ui-fb-option-' + activeIndex() : null\"\n (keydown)=\"keyDownHandler($event)\"\n >\n @if (!displayedOptions().length && options().length) {\n <div class=\"no-results-found\">\n <span>{{ translationContext + 'NO_RESULTS_FOUND' | uiTranslate | async }}</span>\n </div>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"ui-filter-button-viewport\"\n [itemSize]=\"ITEM_SIZE\"\n [style.height.px]=\"listHeight()\"\n >\n <ng-container *cdkVirtualFor=\"let row of flatOptions(); let i = index; trackBy: trackByRow\">\n @if (row.kind === 'header') {\n <div class=\"section-title\">\n {{ row.title }}\n </div>\n } @else {\n <div\n class=\"item\"\n role=\"option\"\n tabindex=\"-1\"\n [id]=\"'ui-fb-option-' + i\"\n [class.item-selected]=\"isSelected(row.option.value)\"\n [class.item-active]=\"activeIndex() === i\"\n [attr.aria-selected]=\"isSelected(row.option.value)\"\n (click)=\"activeIndex.set(i); optionChanged(!isSelected(row.option.value), row.option)\"\n (keydown.enter)=\"optionChanged(!isSelected(row.option.value), row.option)\"\n >\n @if (singleSelection) {\n <ui-radio-button\n (click)=\"$event.stopPropagation()\"\n [tabIndex]=\"-1\"\n [selected]=\"isSelected(row.option.value)\"\n (changeRadio)=\"optionChanged($event.optionChecked, row.option)\"\n [applicationTheme]=\"applicationTheme\"\n >\n </ui-radio-button>\n }\n @if (!singleSelection) {\n <ui-checkbox\n (click)=\"$event.stopPropagation()\"\n [class.with-label-number]=\"row.option.labelNumber !== undefined\"\n [tabIndex]=\"-1\"\n [checked]=\"isSelected(row.option.value)\"\n (changed)=\"optionChanged($event, row.option)\"\n [applicationTheme]=\"applicationTheme\"\n ></ui-checkbox>\n }\n @if (row.option.icon) {\n <ui-icon [applicationTheme]=\"applicationTheme\" [name]=\"row.option.icon\"></ui-icon>\n }\n <span\n #tooltip=\"matTooltip\"\n class=\"item-label\"\n uiEllipseText\n [refresh]=\"row.option.value\"\n (onChangeTextState)=\"onLabelEllipsisChange($event, row.option.value)\"\n [matTooltip]=\"showTooltip[row.option.value] ? row.option.label : ''\"\n [matTooltipClass]=\"applicationTheme\"\n >\n {{ row.option.label }}\n </span>\n @if (row.option.labelNumber !== undefined) {\n <span class=\"item-number\">({{ row.option.labelNumber }})</span>\n }\n </div>\n }\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </div>\n @if (!options().length && !!noOptionsMessage) {\n <div class=\"ui-filter-button-no-options\" [innerHTML]=\"noOptionsMessage\"></div>\n } @else {\n @if (allowClear || message) {\n <div class=\"ui-filter-button-footer\">\n @if (allowClear) {\n <ui-button\n variant=\"text\"\n size=\"small\"\n [label]=\"('COMMON.RESET' | uiTranslate | async)!\"\n (click)=\"clearValue()\"\n [applicationTheme]=\"applicationTheme\"\n (keydown.escape)=\"toggle()\"\n ></ui-button>\n }\n <div class=\"ui-filter-button-footer-message\">\n {{ message }}\n </div>\n </div>\n }\n }\n </div>\n </ng-template>\n</ng-container>\n", styles: [".bg-teal-60b{background:#1c443c}.bg-teal-30b{background:#31766a}.bg-teal-default{background:#46a997}.bg-teal-30w{background:#7ec3b6}.bg-teal-60w{background:#b5ddd5}.bg-teal-secondary{background:#cbd6cb}.bg-teal-90w{background:#ecf6f5}.bg-petrol-60b{background:#102930}.bg-petrol-30b{background:#1b4754}.bg-petrol-default{background:#276678}.bg-petrol-30w{background:#6894a0}.bg-petrol-60w{background:#a9c2c9}.bg-petrol-secondary{background:#c8d7de}.bg-petrol-90w{background:#e9f0f1}.bg-error-60b{background:#513131}.bg-error-30b{background:#8e5655}.bg-error-60w{background:#e3c3c6}.bg-error-secondary{background:#f0dad9}.bg-error-default{background:#cb7b7a}.bg-warning-secondary{background:#f0d6bb}.bg-warning-default{background:#cca45f}.bg-black{background:#000}.bg-dark{background:#888}.bg-medium{background:#e0e0e0}.bg-grey{background:#ededed}.bg-light{background:#f6f6f6}.bg-white{background:#fff}.bg-box-shadow{background:#00000014}.bg-navigation-subtitle{background:#528593}.bgc-teal-60b{background-color:#1c443c}.bgc-teal-30b{background-color:#31766a}.bgc-teal-default{background-color:#46a997}.bgc-teal-30w{background-color:#7ec3b6}.bgc-teal-60w{background-color:#b5ddd5}.bgc-teal-secondary{background-color:#cbd6cb}.bgc-teal-90w{background-color:#ecf6f5}.bgc-petrol-60b{background-color:#102930}.bgc-petrol-30b{background-color:#1b4754}.bgc-petrol-default{background-color:#276678}.bgc-petrol-30w{background-color:#6894a0}.bgc-petrol-60w{background-color:#a9c2c9}.bgc-petrol-secondary{background-color:#c8d7de}.bgc-petrol-90w{background-color:#e9f0f1}.bgc-error-60b{background-color:#513131}.bgc-error-30b{background-color:#8e5655}.bgc-error-60w{background-color:#e3c3c6}.bgc-error-secondary{background-color:#f0dad9}.bgc-error-default{background-color:#cb7b7a}.bgc-warning-secondary{background-color:#f0d6bb}.bgc-warning-default{background-color:#cca45f}.bgc-black{background-color:#000}.bgc-dark{background-color:#888}.bgc-medium{background-color:#e0e0e0}.bgc-grey{background-color:#ededed}.bgc-light{background-color:#f6f6f6}.bgc-white{background-color:#fff}.bgc-box-shadow{background-color:#00000014}.bgc-navigation-subtitle{background-color:#528593}ui-filter-button{display:inline-block}ui-filter-button ui-button .button-wrapper button{padding:7px 24px!important}ui-filter-button ui-button .button-wrapper button .icon ui-icon mat-icon.size-16 svg{width:24px;height:24px}ui-filter-button ui-button .button-wrapper button .label{line-clamp:1;-webkit-line-clamp:1!important}ui-filter-button ui-button.button-opened .button-wrapper button{background-color:#666!important;border-color:#666}ui-filter-button ui-button button.secondary{border:1px solid #242424}ui-filter-button ui-button button.secondary:hover{border-color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button{margin-top:4px;box-shadow:0 4px 16px #24242414;border-radius:10px;background-color:#fff;padding:0 0 8px;width:240px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-header{padding:8px 16px 0}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .section-title{display:flex;align-items:center;height:48px;padding:0 16px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .no-results-found{padding:8px 16px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item{padding:0 16px 0 5px;display:flex;align-items:center;gap:5px;height:48px;cursor:pointer}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item:hover,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item.item-active,.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item.item-selected{background-color:#f4f4f4}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox{max-width:100%}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item ui-checkbox.with-label-number{max-width:calc(100% - 25px)}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-number{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-list .item-label{-webkit-user-select:none;user-select:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-viewport{width:100%;max-height:290px;overflow-x:hidden}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-viewport .cdk-virtual-scroll-content-wrapper{max-width:100%}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer{display:flex;justify-content:space-between;align-items:center;padding:0 16px 0 5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer ui-button{margin-left:-5px}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-footer-message{color:#666}.cdk-overlay-container .cdk-overlay-pane .ui-filter-button-no-options{padding:24px}.tooltip-multi-line .mdc-tooltip__surface{text-align:left;white-space:pre-line}\n"] }]
227
309
  }], ctorParameters: () => [{ type: undefined, decorators: [{
228
310
  type: Optional
229
311
  }, {
230
312
  type: Inject,
231
313
  args: ['CANOPYUI_DEFAULT_APPLICATION_THEME']
232
- }] }, { type: i0.ChangeDetectorRef }], propDecorators: { class: [{
314
+ }] }, { type: i0.ChangeDetectorRef }, { type: i0.DestroyRef }], propDecorators: { class: [{
233
315
  type: HostBinding
234
316
  }], label: [{
235
317
  type: Input
@@ -261,9 +343,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
261
343
  }], overlayContainer: [{
262
344
  type: ViewChild,
263
345
  args: ['overlayContainer']
264
- }], filterOptions: [{
265
- type: ViewChildren,
266
- args: ['filterOption']
346
+ }], viewport: [{
347
+ type: ViewChild,
348
+ args: [CdkVirtualScrollViewport]
267
349
  }] } });
268
350
 
269
351
  class FilterButtonComponentModule {