UNPKG

@herdwatch/ngx-mat-select-search

Version:

Angular component providing an input field for searching / filtering MatSelect options of the Angular Material library.

594 lines 91.1 kB
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Inject, Input, Optional, Output, ViewChild } from '@angular/core'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { MatOption } from '@angular/material/core'; import { MatFormField } from '@angular/material/form-field'; import { MatSelect } from '@angular/material/select'; import { BehaviorSubject, combineLatest, of, Subject } from 'rxjs'; import { delay, filter, map, startWith, switchMap, take, takeUntil, tap } from 'rxjs/operators'; import { MatSelectSearchClearDirective } from './mat-select-search-clear.directive'; import { configurableDefaultOptions, MAT_SELECTSEARCH_DEFAULT_OPTIONS } from './default-options'; import { MatSelectNoEntriesFoundDirective } from './mat-select-no-entries-found.directive'; import * as i0 from "@angular/core"; import * as i1 from "@angular/cdk/scrolling"; import * as i2 from "@angular/common"; import * as i3 from "@angular/forms"; import * as i4 from "@angular/material/button"; import * as i5 from "@angular/material/checkbox"; import * as i6 from "@angular/material/icon"; import * as i7 from "@angular/material/progress-spinner"; import * as i8 from "@angular/material/tooltip"; import * as i9 from "@angular/material/divider"; import * as i10 from "@angular/material/select"; import * as i11 from "@angular/material/core"; import * as i12 from "@angular/material/form-field"; /** * Component providing an input field for searching MatSelect options. * * Example usage: * * interface Bank { * id: string; * name: string; * } * * @Component({ * selector: 'my-app-data-selection', * template: ` * <mat-form-field> * <mat-select [formControl]="bankCtrl" placeholder="Bank"> * <mat-option> * <ngx-mat-select-search [formControl]="bankFilterCtrl"></ngx-mat-select-search> * </mat-option> * <mat-option *ngFor="let bank of filteredBanks | async" [value]="bank.id"> * {{bank.name}} * </mat-option> * </mat-select> * </mat-form-field> * ` * }) * export class DataSelectionComponent implements OnInit, OnDestroy { * * // control for the selected bank * public bankCtrl: FormControl = new FormControl(); * // control for the MatSelect filter keyword * public bankFilterCtrl: FormControl = new FormControl(); * * // list of banks * private banks: Bank[] = [{name: 'Bank A', id: 'A'}, {name: 'Bank B', id: 'B'}, {name: 'Bank C', id: 'C'}]; * // list of banks filtered by search keyword * public filteredBanks: ReplaySubject<Bank[]> = new ReplaySubject<Bank[]>(1); * * // Subject that emits when the component has been destroyed. * private _onDestroy = new Subject<void>(); * * * ngOnInit() { * // load the initial bank list * this.filteredBanks.next(this.banks.slice()); * // listen for search field value changes * this.bankFilterCtrl.valueChanges * .pipe(takeUntil(this._onDestroy)) * .subscribe(() => { * this.filterBanks(); * }); * } * * ngOnDestroy() { * this._onDestroy.next(); * this._onDestroy.complete(); * } * * private filterBanks() { * if (!this.banks) { * return; * } * * // get the search keyword * let search = this.bankFilterCtrl.value; * if (!search) { * this.filteredBanks.next(this.banks.slice()); * return; * } else { * search = search.toLowerCase(); * } * * // filter the banks * this.filteredBanks.next( * this.banks.filter(bank => bank.name.toLowerCase().indexOf(search) > -1) * ); * } * } */ export class MatSelectSearchComponent { matSelect; changeDetectorRef; _viewportRuler; matOption; matFormField; /** Label of the search placeholder */ placeholderLabel = 'Suche'; /** Type of the search input field */ type = 'text'; /** Font-based icon used for displaying Close-Icon */ closeIcon = 'close'; /** SVG-based icon used for displaying Close-Icon. If set, closeIcon is overridden */ closeSvgIcon; /** Label to be shown when no entries are found. Set to null if no message should be shown. */ noEntriesFoundLabel = 'Keine Optionen gefunden'; /** * Whether the search field should be cleared after the dropdown menu is closed. * Useful for server-side filtering. See [#3](https://github.com/bithost-gmbh/ngx-mat-select-search/issues/3) */ clearSearchInput = true; /** Whether to show the search-in-progress indicator */ searching = false; /** Disables initial focusing of the input field */ disableInitialFocus = false; /** Enable clear input on escape pressed */ enableClearOnEscapePressed = false; /** * Prevents home / end key being propagated to mat-select, * allowing to move the cursor within the search input instead of navigating the options */ preventHomeEndKeyPropagation = false; /** Disables scrolling to active options when option list changes. Useful for server-side search */ disableScrollToActiveOnOptionsChanged = false; /** Adds 508 screen reader support for search box */ ariaLabel = 'dropdown search'; /** Whether to show Select All Checkbox (for mat-select[multi=true]) */ showToggleAllCheckbox = false; /** Select all checkbox checked state */ toggleAllCheckboxChecked = false; /** select all checkbox indeterminate state */ toggleAllCheckboxIndeterminate = false; /** Display a message in a tooltip on the toggle-all checkbox */ toggleAllCheckboxTooltipMessage = ''; /** Define the position of the tooltip on the toggle-all checkbox. */ toggleAllCheckboxTooltipPosition = 'below'; /** Show/Hide the search clear button of the search input */ hideClearSearchButton = false; /** * Always restore selected options on selectionChange for mode multi (e.g. for lazy loading/infinity scrolling). * Defaults to false, so selected options are only restored while filtering is active. */ alwaysRestoreSelectedOptionsMulti = false; /** * Recreate array of selected values for multi-selects. * * This is useful if the selected values are stored in an immutable data structure. */ recreateValuesArray = false; /** Output emitter to send to parent component with the toggle all boolean */ toggleAll = new EventEmitter(); /** Reference to the search input field */ searchSelectInput; /** Reference to the search input field */ innerSelectSearch; /** Reference to custom search input clear icon */ clearIcon; /** Reference to custom no entries found element */ noEntriesFound; /** Current search value */ get value() { return this._formControl.value; } _lastExternalInputValue; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type,@typescript-eslint/no-explicit-any,@typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars onTouched = (_) => { }; /** Reference to the MatSelect options */ set _options(_options) { this._options$.next(_options); } get _options() { return this._options$.getValue(); } // eslint-disable-next-line @typescript-eslint/no-explicit-any _options$ = new BehaviorSubject(null); optionsList$ = this._options$.pipe(switchMap(_options => _options ? _options.changes.pipe(map(options => options.toArray()), startWith(_options.toArray())) : of(null))); optionsLength$ = this.optionsList$.pipe(map(options => options ? options.length : 0)); /** Previously selected values when using <mat-select [multiple]="true">*/ previousSelectedValues; _formControl = new FormControl('', { nonNullable: true }); /** Whether to show the no entries found message */ _showNoEntriesFound$ = combineLatest([ this._formControl.valueChanges, this.optionsLength$ ]).pipe(map(([value, optionsLength]) => !!(this.noEntriesFoundLabel && value && optionsLength === this.getOptionsLengthOffset()))); /** Subject that emits when the component has been destroyed. */ _onDestroy = new Subject(); /** Reference to active descendant for ARIA Support. */ activeDescendant; constructor(matSelect, changeDetectorRef, _viewportRuler, matOption, matFormField, defaultOptions) { this.matSelect = matSelect; this.changeDetectorRef = changeDetectorRef; this._viewportRuler = _viewportRuler; this.matOption = matOption; this.matFormField = matFormField; this.applyDefaultOptions(defaultOptions); } applyDefaultOptions(defaultOptions) { if (!defaultOptions) { return; } for (const key of configurableDefaultOptions) { if (Object.prototype.hasOwnProperty.call(defaultOptions, key)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any this[key] = defaultOptions[key]; } } } ngOnInit() { // set custom mat-option class if the component was placed inside a mat-option if (this.matOption) { this.matOption.disabled = true; this.matOption._getHostElement().classList.add('contains-mat-select-search'); this.matOption._getHostElement().setAttribute('role', 'presentation'); } else { console.error('<ngx-mat-select-search> must be placed inside a <mat-option> element'); } // when the select dropdown panel is opened or closed this.matSelect.openedChange .pipe(delay(1), takeUntil(this._onDestroy)) .subscribe((opened) => { if (opened) { this.updateInputWidth(); // focus the search field when opening if (!this.disableInitialFocus) { this._focus(); } } else { // clear it when closing if (this.clearSearchInput) { this._reset(); } } }); // set the first item active after the options changed this.matSelect.openedChange .pipe(take(1), switchMap(() => { this._options = this.matSelect.options; // Closure variable for tracking the most recent first option. // In order to avoid causing the list to // scroll to the top when options are added to the bottom of // the list (eg: infinite scroll), we compare only // the changes to the first options to determine if we // should set the first item as active. // This prevents unnecessary scrolling to the top of the list // when options are appended, but allows the first item // in the list to be set as active by default when there // is no active selection let previousFirstOption = this._options.toArray()[this.getOptionsLengthOffset()]; return this._options.changes .pipe(tap(() => { // avoid "expression has been changed" error setTimeout(() => { // Convert the QueryList to an array const options = this._options.toArray(); // The true first item is offset by 1 const currentFirstOption = options[this.getOptionsLengthOffset()]; const keyManager = this.matSelect._keyManager; if (keyManager && this.matSelect.panelOpen && currentFirstOption) { // set first item active and input width // Check to see if the first option in these changes is different from the previous. const firstOptionIsChanged = !previousFirstOption || !this.matSelect.compareWith(previousFirstOption.value, currentFirstOption.value); // CASE: The first option is different now. // Indicates we should set it as active and scroll to the top. if (firstOptionIsChanged || !keyManager.activeItem || !options.find(option => this.matSelect.compareWith(option.value, keyManager.activeItem?.value))) { keyManager.setActiveItem(this.getOptionsLengthOffset()); } // wait for panel width changes setTimeout(() => { this.updateInputWidth(); }); } // Update our reference previousFirstOption = currentFirstOption; }); })); })) .pipe(takeUntil(this._onDestroy)) .subscribe(); // add or remove css class depending on whether to show the no entries found message // note: this is hacky this._showNoEntriesFound$.pipe(takeUntil(this._onDestroy)).subscribe(showNoEntriesFound => { // set no entries found class on mat option if (this.matOption) { if (showNoEntriesFound) { this.matOption._getHostElement().classList.add('mat-select-search-no-entries-found'); } else { this.matOption._getHostElement().classList.remove('mat-select-search-no-entries-found'); } } }); // resize the input width when the viewport is resized, i.e. the trigger width could potentially be resized this._viewportRuler.change() .pipe(takeUntil(this._onDestroy)) .subscribe(() => { if (this.matSelect.panelOpen) { this.updateInputWidth(); } }); this.initMultipleHandling(); this.optionsList$.pipe(takeUntil(this._onDestroy)).subscribe(() => { // update view when available options change this.changeDetectorRef.markForCheck(); }); } _emitSelectAllBooleanToParent(state) { this.toggleAll.emit(state); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } _isToggleAllCheckboxVisible() { return this.matSelect.multiple && this.showToggleAllCheckbox; } /** * Handles the key down event with MatSelect. * Allows e.g. selecting with enter key, navigation with arrow keys, etc. * @param event */ _handleKeydown(event) { // Prevent propagation for all alphanumeric characters in order to avoid selection issues // Needed to avoid handling in https://github.com/angular/components/blob/5439460d1fe166f8ec34ab7d48f05e0dd7f6a946/src/material/select/select.ts#L965 if ((event.key && event.key.length === 1) || (this.preventHomeEndKeyPropagation && (event.key === 'Home' || event.key === 'End'))) { event.stopPropagation(); } if (this.matSelect.multiple && event.key && event.key === 'Enter') { // Regain focus after multiselect, so we can further type setTimeout(() => this._focus()); } // Special case if click Escape, if input is empty, close the dropdown, if not, empty out the search field if (this.enableClearOnEscapePressed && event.key === 'Escape' && this.value) { this._reset(true); event.stopPropagation(); } } /** * Handles the key up event with MatSelect. * Allows e.g. the announcing of the currently activeDescendant by screen readers. */ _handleKeyup(event) { if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { const ariaActiveDescendantId = this.matSelect._getAriaActiveDescendant(); const index = this._options.toArray().findIndex(item => item.id === ariaActiveDescendantId); if (index !== -1) { this.unselectActiveDescendant(); this.activeDescendant = this._options.toArray()[index]._getHostElement(); this.activeDescendant.setAttribute('aria-selected', 'true'); this.searchSelectInput.nativeElement.setAttribute('aria-activedescendant', ariaActiveDescendantId); } } } writeValue(value) { this._lastExternalInputValue = value; this._formControl.setValue(value); this.changeDetectorRef.markForCheck(); } onBlur() { this.unselectActiveDescendant(); this.onTouched(); } registerOnChange(fn) { this._formControl.valueChanges.pipe(filter(value => value !== this._lastExternalInputValue), tap(() => this._lastExternalInputValue = undefined), takeUntil(this._onDestroy)).subscribe(fn); } // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type registerOnTouched(fn) { this.onTouched = fn; } /** * Focuses the search input field */ _focus() { if (!this.searchSelectInput || !this.matSelect.panel) { return; } // save and restore scrollTop of panel, since it will be reset by focus() // note: this is hacky const panel = this.matSelect.panel.nativeElement; const scrollTop = panel.scrollTop; // focus this.searchSelectInput.nativeElement.focus(); panel.scrollTop = scrollTop; } /** * Resets the current search value * @param focus whether to focus after resetting */ _reset(focus) { this._formControl.setValue(''); if (focus) { this._focus(); } } /** * Initializes handling <mat-select [multiple]="true"> * Note: to improve this code, mat-select should be extended to allow disabling resetting the selection while filtering. */ initMultipleHandling() { if (!this.matSelect.ngControl) { if (this.matSelect.multiple) { // note: the access to matSelect.ngControl (instead of matSelect.value / matSelect.valueChanges) // is necessary to properly work in multi-selection mode. console.error('the mat-select containing ngx-mat-select-search must have a ngModel or formControl directive when multiple=true'); } return; } // if <mat-select [multiple]="true"> // store previously selected values and restore them when they are deselected // because the option is not available while we are currently filtering this.previousSelectedValues = this.matSelect.ngControl.value; if (!this.matSelect.ngControl.valueChanges) { return; } this.matSelect.ngControl.valueChanges .pipe(takeUntil(this._onDestroy)) .subscribe((values) => { let restoreSelectedValues = false; if (this.matSelect.multiple) { if ((this.alwaysRestoreSelectedOptionsMulti || (this._formControl.value && this._formControl.value.length)) && this.previousSelectedValues && Array.isArray(this.previousSelectedValues)) { if (!values || !Array.isArray(values)) { values = []; } const optionValues = this.matSelect.options.map(option => option.value); this.previousSelectedValues.forEach(previousValue => { if (!values.some(v => this.matSelect.compareWith(v, previousValue)) && !optionValues.some(v => this.matSelect.compareWith(v, previousValue))) { // if a value that was selected before is deselected and not found in the options, it was deselected // due to the filtering, so we restore it. if (this.recreateValuesArray) { values = [...values, previousValue]; } else { values.push(previousValue); } restoreSelectedValues = true; } }); } } this.previousSelectedValues = values; if (restoreSelectedValues) { this.matSelect._onChange(values); } }); } /** * Set the width of the innerSelectSearch to fit even custom scrollbars * And support all Operating Systems */ updateInputWidth() { if (!this.innerSelectSearch || !this.innerSelectSearch.nativeElement) { return; } let element = this.innerSelectSearch.nativeElement; let panelElement = null; while (element && element.parentElement) { element = element.parentElement; if (element.classList.contains('mat-select-panel')) { panelElement = element; break; } } if (panelElement) { this.innerSelectSearch.nativeElement.style.width = panelElement.clientWidth + 'px'; } } /** * Determine the offset to length that can be caused by the optional matOption used as a search input. */ getOptionsLengthOffset() { if (this.matOption) { return 1; } else { return 0; } } unselectActiveDescendant() { this.activeDescendant?.removeAttribute('aria-selected'); this.searchSelectInput.nativeElement.removeAttribute('aria-activedescendant'); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: MatSelectSearchComponent, deps: [{ token: MatSelect }, { token: i0.ChangeDetectorRef }, { token: i1.ViewportRuler }, { token: MatOption, optional: true }, { token: MatFormField, optional: true }, { token: MAT_SELECTSEARCH_DEFAULT_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.10", type: MatSelectSearchComponent, selector: "ngx-mat-select-search", inputs: { placeholderLabel: "placeholderLabel", type: "type", closeIcon: "closeIcon", closeSvgIcon: "closeSvgIcon", noEntriesFoundLabel: "noEntriesFoundLabel", clearSearchInput: "clearSearchInput", searching: "searching", disableInitialFocus: "disableInitialFocus", enableClearOnEscapePressed: "enableClearOnEscapePressed", preventHomeEndKeyPropagation: "preventHomeEndKeyPropagation", disableScrollToActiveOnOptionsChanged: "disableScrollToActiveOnOptionsChanged", ariaLabel: "ariaLabel", showToggleAllCheckbox: "showToggleAllCheckbox", toggleAllCheckboxChecked: "toggleAllCheckboxChecked", toggleAllCheckboxIndeterminate: "toggleAllCheckboxIndeterminate", toggleAllCheckboxTooltipMessage: "toggleAllCheckboxTooltipMessage", toggleAllCheckboxTooltipPosition: "toggleAllCheckboxTooltipPosition", hideClearSearchButton: "hideClearSearchButton", alwaysRestoreSelectedOptionsMulti: "alwaysRestoreSelectedOptionsMulti", recreateValuesArray: "recreateValuesArray" }, outputs: { toggleAll: "toggleAll" }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MatSelectSearchComponent), multi: true } ], queries: [{ propertyName: "clearIcon", first: true, predicate: MatSelectSearchClearDirective, descendants: true }, { propertyName: "noEntriesFound", first: true, predicate: MatSelectNoEntriesFoundDirective, descendants: true }], viewQueries: [{ propertyName: "searchSelectInput", first: true, predicate: ["searchSelectInput"], descendants: true, read: ElementRef, static: true }, { propertyName: "innerSelectSearch", first: true, predicate: ["innerSelectSearch"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<!--\nCopyright (c) 2018 Bithost GmbH All Rights Reserved.\n\nUse of this source code is governed by an MIT-style license that can be\nfound in the LICENSE file at https://angular.io/license\n-->\n<!-- Placeholder to adjust vertical offset of the mat-option elements -->\n<input matInput class=\"mat-select-search-input mat-select-search-hidden\"/>\n\n<!-- Note: the mat-datepicker-content mat-tab-header are needed to inherit the material theme colors, see PR #22 -->\n<div\n #innerSelectSearch\n class=\"mat-select-search-inner mat-typography mat-datepicker-content mat-tab-header\"\n [ngClass]=\"{'mat-select-search-inner-multiple': matSelect.multiple, 'mat-select-search-inner-toggle-all': _isToggleAllCheckboxVisible() }\">\n\n <div class=\"mat-select-search-inner-row\">\n <mat-checkbox *ngIf=\"_isToggleAllCheckboxVisible()\"\n [color]=\"matFormField?.color\"\n class=\"mat-select-search-toggle-all-checkbox\"\n [checked]=\"toggleAllCheckboxChecked\"\n [indeterminate]=\"toggleAllCheckboxIndeterminate\"\n [matTooltip]=\"toggleAllCheckboxTooltipMessage\"\n matTooltipClass=\"ngx-mat-select-search-toggle-all-tooltip\"\n [matTooltipPosition]=\"toggleAllCheckboxTooltipPosition\"\n (change)=\"_emitSelectAllBooleanToParent($event.checked)\"\n ></mat-checkbox>\n\n <input class=\"mat-select-search-input\"\n autocomplete=\"off\"\n [type]=\"type\"\n [formControl]=\"_formControl\"\n #searchSelectInput\n (keydown)=\"_handleKeydown($event)\"\n (keyup)=\"_handleKeyup($event)\"\n (blur)=\"onBlur()\"\n [placeholder]=\"placeholderLabel\"\n [attr.aria-label]=\"ariaLabel\"\n />\n <mat-spinner *ngIf=\"searching\"\n class=\"mat-select-search-spinner\"\n diameter=\"16\"></mat-spinner>\n\n <button *ngIf=\"!hideClearSearchButton && value && !searching\"\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"_reset(true)\"\n class=\"mat-select-search-clear\">\n <ng-content *ngIf=\"clearIcon; else defaultIcon\" select=\"[ngxMatSelectSearchClear]\"></ng-content>\n <ng-template #defaultIcon>\n <mat-icon [svgIcon]=\"closeSvgIcon\">\n {{!closeSvgIcon ? closeIcon : null}}\n </mat-icon>\n </ng-template>\n </button>\n\n <ng-content select=\".mat-select-search-custom-header-content\"></ng-content>\n </div>\n\n <mat-divider></mat-divider>\n</div>\n\n<div *ngIf=\"_showNoEntriesFound$ | async\"\n class=\"mat-select-search-no-entries-found\">\n <ng-content *ngIf=\"noEntriesFound; else defaultNoEntriesFound\"\n select=\"[ngxMatSelectNoEntriesFound]\"></ng-content>\n <ng-template #defaultNoEntriesFound>{{noEntriesFoundLabel}}</ng-template>\n</div>\n\n", styles: [".mat-select-search-hidden{visibility:hidden}.mat-select-search-inner{position:absolute;top:0;left:0;width:100%;z-index:100;font-size:inherit;box-shadow:none;background-color:var(--mat-select-panel-background-color)}.mat-select-search-inner.mat-select-search-inner-multiple.mat-select-search-inner-toggle-all .mat-select-search-inner-row{display:flex;align-items:center}.mat-select-search-input{box-sizing:border-box;width:100%;border:none;font-family:inherit;font-size:inherit;color:currentColor;outline:none;background-color:var(--mat-select-panel-background-color);padding:0 44px 0 16px;height:calc(3em - 1px);line-height:calc(3em - 1px)}:host-context([dir=rtl]) .mat-select-search-input{padding-right:16px;padding-left:44px}.mat-select-search-inner-toggle-all .mat-select-search-input{padding-left:5px}.mat-select-search-no-entries-found{padding-top:8px}.mat-select-search-clear{position:absolute;right:4px;top:0}:host-context([dir=rtl]) .mat-select-search-clear{right:auto;left:4px}.mat-select-search-spinner{position:absolute;right:16px;top:calc(50% - 8px)}:host-context([dir=rtl]) .mat-select-search-spinner{right:auto;left:16px}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search{position:sticky;top:-8px;z-index:1;opacity:1;margin-top:-8px;pointer-events:all}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search .mat-icon{margin-right:0;margin-left:0}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search mat-pseudo-checkbox{display:none}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search .mdc-list-item__primary-text{opacity:1}.mat-select-search-toggle-all-checkbox{padding-left:5px}:host-context([dir=rtl]) .mat-select-search-toggle-all-checkbox{padding-left:0;padding-right:5px}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i5.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: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: i9.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: MatSelectSearchComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-mat-select-search', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MatSelectSearchComponent), multi: true } ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\nCopyright (c) 2018 Bithost GmbH All Rights Reserved.\n\nUse of this source code is governed by an MIT-style license that can be\nfound in the LICENSE file at https://angular.io/license\n-->\n<!-- Placeholder to adjust vertical offset of the mat-option elements -->\n<input matInput class=\"mat-select-search-input mat-select-search-hidden\"/>\n\n<!-- Note: the mat-datepicker-content mat-tab-header are needed to inherit the material theme colors, see PR #22 -->\n<div\n #innerSelectSearch\n class=\"mat-select-search-inner mat-typography mat-datepicker-content mat-tab-header\"\n [ngClass]=\"{'mat-select-search-inner-multiple': matSelect.multiple, 'mat-select-search-inner-toggle-all': _isToggleAllCheckboxVisible() }\">\n\n <div class=\"mat-select-search-inner-row\">\n <mat-checkbox *ngIf=\"_isToggleAllCheckboxVisible()\"\n [color]=\"matFormField?.color\"\n class=\"mat-select-search-toggle-all-checkbox\"\n [checked]=\"toggleAllCheckboxChecked\"\n [indeterminate]=\"toggleAllCheckboxIndeterminate\"\n [matTooltip]=\"toggleAllCheckboxTooltipMessage\"\n matTooltipClass=\"ngx-mat-select-search-toggle-all-tooltip\"\n [matTooltipPosition]=\"toggleAllCheckboxTooltipPosition\"\n (change)=\"_emitSelectAllBooleanToParent($event.checked)\"\n ></mat-checkbox>\n\n <input class=\"mat-select-search-input\"\n autocomplete=\"off\"\n [type]=\"type\"\n [formControl]=\"_formControl\"\n #searchSelectInput\n (keydown)=\"_handleKeydown($event)\"\n (keyup)=\"_handleKeyup($event)\"\n (blur)=\"onBlur()\"\n [placeholder]=\"placeholderLabel\"\n [attr.aria-label]=\"ariaLabel\"\n />\n <mat-spinner *ngIf=\"searching\"\n class=\"mat-select-search-spinner\"\n diameter=\"16\"></mat-spinner>\n\n <button *ngIf=\"!hideClearSearchButton && value && !searching\"\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"_reset(true)\"\n class=\"mat-select-search-clear\">\n <ng-content *ngIf=\"clearIcon; else defaultIcon\" select=\"[ngxMatSelectSearchClear]\"></ng-content>\n <ng-template #defaultIcon>\n <mat-icon [svgIcon]=\"closeSvgIcon\">\n {{!closeSvgIcon ? closeIcon : null}}\n </mat-icon>\n </ng-template>\n </button>\n\n <ng-content select=\".mat-select-search-custom-header-content\"></ng-content>\n </div>\n\n <mat-divider></mat-divider>\n</div>\n\n<div *ngIf=\"_showNoEntriesFound$ | async\"\n class=\"mat-select-search-no-entries-found\">\n <ng-content *ngIf=\"noEntriesFound; else defaultNoEntriesFound\"\n select=\"[ngxMatSelectNoEntriesFound]\"></ng-content>\n <ng-template #defaultNoEntriesFound>{{noEntriesFoundLabel}}</ng-template>\n</div>\n\n", styles: [".mat-select-search-hidden{visibility:hidden}.mat-select-search-inner{position:absolute;top:0;left:0;width:100%;z-index:100;font-size:inherit;box-shadow:none;background-color:var(--mat-select-panel-background-color)}.mat-select-search-inner.mat-select-search-inner-multiple.mat-select-search-inner-toggle-all .mat-select-search-inner-row{display:flex;align-items:center}.mat-select-search-input{box-sizing:border-box;width:100%;border:none;font-family:inherit;font-size:inherit;color:currentColor;outline:none;background-color:var(--mat-select-panel-background-color);padding:0 44px 0 16px;height:calc(3em - 1px);line-height:calc(3em - 1px)}:host-context([dir=rtl]) .mat-select-search-input{padding-right:16px;padding-left:44px}.mat-select-search-inner-toggle-all .mat-select-search-input{padding-left:5px}.mat-select-search-no-entries-found{padding-top:8px}.mat-select-search-clear{position:absolute;right:4px;top:0}:host-context([dir=rtl]) .mat-select-search-clear{right:auto;left:4px}.mat-select-search-spinner{position:absolute;right:16px;top:calc(50% - 8px)}:host-context([dir=rtl]) .mat-select-search-spinner{right:auto;left:16px}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search{position:sticky;top:-8px;z-index:1;opacity:1;margin-top:-8px;pointer-events:all}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search .mat-icon{margin-right:0;margin-left:0}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search mat-pseudo-checkbox{display:none}::ng-deep .mat-mdc-option[aria-disabled=true].contains-mat-select-search .mdc-list-item__primary-text{opacity:1}.mat-select-search-toggle-all-checkbox{padding-left:5px}:host-context([dir=rtl]) .mat-select-search-toggle-all-checkbox{padding-left:0;padding-right:5px}\n"] }] }], ctorParameters: () => [{ type: i10.MatSelect, decorators: [{ type: Inject, args: [MatSelect] }] }, { type: i0.ChangeDetectorRef }, { type: i1.ViewportRuler }, { type: i11.MatOption, decorators: [{ type: Optional }, { type: Inject, args: [MatOption] }] }, { type: i12.MatFormField, decorators: [{ type: Optional }, { type: Inject, args: [MatFormField] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_SELECTSEARCH_DEFAULT_OPTIONS] }] }], propDecorators: { placeholderLabel: [{ type: Input }], type: [{ type: Input }], closeIcon: [{ type: Input }], closeSvgIcon: [{ type: Input }], noEntriesFoundLabel: [{ type: Input }], clearSearchInput: [{ type: Input }], searching: [{ type: Input }], disableInitialFocus: [{ type: Input }], enableClearOnEscapePressed: [{ type: Input }], preventHomeEndKeyPropagation: [{ type: Input }], disableScrollToActiveOnOptionsChanged: [{ type: Input }], ariaLabel: [{ type: Input }], showToggleAllCheckbox: [{ type: Input }], toggleAllCheckboxChecked: [{ type: Input }], toggleAllCheckboxIndeterminate: [{ type: Input }], toggleAllCheckboxTooltipMessage: [{ type: Input }], toggleAllCheckboxTooltipPosition: [{ type: Input }], hideClearSearchButton: [{ type: Input }], alwaysRestoreSelectedOptionsMulti: [{ type: Input }], recreateValuesArray: [{ type: Input }], toggleAll: [{ type: Output }], searchSelectInput: [{ type: ViewChild, args: ['searchSelectInput', { read: ElementRef, static: true }] }], innerSelectSearch: [{ type: ViewChild, args: ['innerSelectSearch', { read: ElementRef, static: true }] }], clearIcon: [{ type: ContentChild, args: [MatSelectSearchClearDirective] }], noEntriesFound: [{ type: ContentChild, args: [MatSelectNoEntriesFoundDirective] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0LXNlbGVjdC1zZWFyY2guY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC9tYXQtc2VsZWN0LXNlYXJjaC9tYXQtc2VsZWN0LXNlYXJjaC5jb21wb25lbnQudHMiLCIuLi8uLi9zcmMvYXBwL21hdC1zZWxlY3Qtc2VhcmNoL21hdC1zZWxlY3Qtc2VhcmNoLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVFBLE9BQU8sRUFDTCx1QkFBdUIsRUFFdkIsU0FBUyxFQUNULFlBQVksRUFDWixVQUFVLEVBQ1YsWUFBWSxFQUNaLFVBQVUsRUFDVixNQUFNLEVBQ04sS0FBSyxFQUdMLFFBQVEsRUFDUixNQUFNLEVBRU4sU0FBUyxFQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBd0IsV0FBVyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDckQsT0FBTyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQWMsRUFBRSxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2hHLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxnQ0FBZ0MsRUFBMEIsTUFBTSxtQkFBbUIsQ0FBQztBQUN6SCxPQUFPLEVBQUUsZ0NBQWdDLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUFFM0Y7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBNkVHO0FBY0gsTUFBTSxPQUFPLHdCQUF3QjtJQThJRztJQUM3QjtJQUNDO0lBQzhCO0lBQ0c7SUFoSjNDLHNDQUFzQztJQUM3QixnQkFBZ0IsR0FBRyxPQUFPLENBQUM7SUFFcEMscUNBQXFDO0lBQzVCLElBQUksR0FBRyxNQUFNLENBQUM7SUFFdkIscURBQXFEO0lBQzVDLFNBQVMsR0FBRyxPQUFPLENBQUM7SUFFN0IscUZBQXFGO0lBQzVFLFlBQVksQ0FBVTtJQUUvQiw4RkFBOEY7SUFDckYsbUJBQW1CLEdBQUcseUJBQXlCLENBQUM7SUFFekQ7OztRQUdJO0lBQ0ssZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO0lBRWpDLHVEQUF1RDtJQUM5QyxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBRTNCLG1EQUFtRDtJQUMxQyxtQkFBbUIsR0FBRyxLQUFLLENBQUM7SUFFckMsMkNBQTJDO0lBQ2xDLDBCQUEwQixHQUFHLEtBQUssQ0FBQztJQUU1Qzs7O09BR0c7SUFDTSw0QkFBNEIsR0FBRyxLQUFLLENBQUM7SUFFOUMsbUdBQW1HO0lBQzFGLHFDQUFxQyxHQUFHLEtBQUssQ0FBQztJQUV2RCxvREFBb0Q7SUFDM0MsU0FBUyxHQUFHLGlCQUFpQixDQUFDO0lBRXZDLHVFQUF1RTtJQUM5RCxxQkFBcUIsR0FBRyxLQUFLLENBQUM7SUFFdkMsd0NBQXdDO0lBQy9CLHdCQUF3QixHQUFHLEtBQUssQ0FBQztJQUUxQyw4Q0FBOEM7SUFDckMsOEJBQThCLEdBQUcsS0FBSyxDQUFDO0lBRWhELGdFQUFnRTtJQUN2RCwrQkFBK0IsR0FBRyxFQUFFLENBQUM7SUFFOUMscUVBQXFFO0lBQzVELGdDQUFnQyxHQUE4RCxPQUFPLENBQUM7SUFFL0csNERBQTREO0lBQ25ELHFCQUFxQixHQUFHLEtBQUssQ0FBQztJQUV2Qzs7O09BR0c7SUFDTSxpQ0FBaUMsR0FBRyxLQUFLLENBQUM7SUFFbkQ7Ozs7T0FJRztJQUNNLG1CQUFtQixHQUFHLEtBQUssQ0FBQztJQUVyQyw2RUFBNkU7SUFDbkUsU0FBUyxHQUFHLElBQUksWUFBWSxFQUFXLENBQUM7SUFFbEQsMENBQTBDO0lBQzBCLGlCQUFpQixDQUFhO0lBRWxHLDBDQUEwQztJQUMwQixpQkFBaUIsQ0FBYTtJQUVsRyxrREFBa0Q7SUFDTCxTQUFTLENBQWdDO0lBRXRGLG1EQUFtRDtJQUNILGNBQWMsQ0FBbUM7SUFFakcsMkJBQTJCO0lBQzNCLElBQUksS0FBSztRQUNQLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUM7SUFDakMsQ0FBQztJQUNPLHVCQUF1QixDQUFVO0lBRXpDLGdMQUFnTDtJQUNoTCxTQUFTLEdBQWEsQ0FBQyxDQUFNLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUV0Qyx5Q0FBeUM7SUFDekMsSUFBVyxRQUFRLENBQUMsUUFBOEI7UUFDaEQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUNELElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDbkMsQ0FBQztJQUNELDhEQUE4RDtJQUN2RCxTQUFTLEdBQTBDLElBQUksZUFBZSxDQUF1QixJQUFXLENBQUMsQ0FBQztJQUV6RyxZQUFZLEdBQW1DLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUN4RSxTQUFTLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM5QixRQUFRLENBQUMsT0FBTyxDQUFDLElBQUksQ0FDbkIsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLEVBQ2pDLFNBQVMsQ0FBYyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FDM0MsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUNiLENBQ0YsQ0FBQztJQUVNLGNBQWMsR0FBdUIsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQ2pFLEdBQUcsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQzdDLENBQUM7SUFFRiwwRUFBMEU7SUFDbEUsc0JBQXNCLENBQUs7SUFFNUIsWUFBWSxHQUF3QixJQUFJLFdBQVcsQ0FBUyxFQUFFLEVBQUUsRUFBQyxXQUFXLEVBQUUsSUFBSSxFQUFDLENBQUMsQ0FBQztJQUU1RixtREFBbUQ7SUFDNUMsb0JBQW9CLEdBQXdCLGFBQWEsQ0FBQztRQUMvRCxJQUFJLENBQUMsWUFBWSxDQUFDLFlBQVk7UUFDOUIsSUFBSSxDQUFDLGNBQWM7S0FDcEIsQ0FBQyxDQUFDLElBQUksQ0FDTCxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxhQUFhLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLG1CQUFtQixJQUFJLEtBQUs7V0FDL0QsYUFBYSxLQUFLLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDLENBQUMsQ0FDdkQsQ0FBQztJQUVGLGdFQUFnRTtJQUN4RCxVQUFVLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztJQUV6Qyx1REFBdUQ7SUFDL0MsZ0JBQWdCLENBQWM7SUFFdEMsWUFBc0MsU0FBb0IsRUFDakQsaUJBQW9DLEVBQ25DLGNBQTZCLEVBQ0MsU0FBb0IsRUFDakIsWUFBMEIsRUFDYixjQUF1QztRQUx6RCxjQUFTLEdBQVQsU0FBUyxDQUFXO1FBQ2pELHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBbUI7UUFDbkMsbUJBQWMsR0FBZCxjQUFjLENBQWU7UUFDQyxjQUFTLEdBQVQsU0FBUyxDQUFXO1FBQ2pCLGlCQUFZLEdBQVosWUFBWSxDQUFjO1FBR25FLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRU8sbUJBQW1CLENBQUMsY0FBdUM7UUFDakUsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3BCLE9BQU87UUFDVCxDQUFDO1FBQ0QsS0FBSyxNQUFNLEdBQUcsSUFBSSwwQkFBMEIsRUFBRSxDQUFDO1lBQzdDLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUM5RCw4REFBOEQ7Z0JBQzdELElBQUksQ0FBQyxHQUFHLENBQVMsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDM0MsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQsUUFBUTtRQUNOLDhFQUE4RTtRQUM5RSxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNuQixJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7WUFDL0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLDRCQUE0QixDQUFDLENBQUM7WUFDN0UsSUFBSSxDQUFDLFNBQVMsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLGNBQWMsQ0FBQyxDQUFDO1FBQ3hFLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxDQUFDLEtBQUssQ0FBQyxzRUFBc0UsQ0FBQyxDQUFDO1FBQ3hGLENBQUM7UUFFRCxxREFBcUQ7UUFDckQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZO2FBQ3hCLElBQUksQ0FDSCxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQ1IsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FDM0I7YUFDQSxTQUFTLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUNwQixJQUFJLE1BQU0sRUFBRSxDQUFDO2dCQUNYLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO2dCQUN4QixzQ0FBc0M7Z0JBQ3RDLElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztvQkFDOUIsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNoQixDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLHdCQUF3QjtnQkFDeEIsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztvQkFDMUIsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNoQixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBSUwsc0RBQXNEO1FBQ3RELElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWTthQUN4QixJQUFJLENBQ0gsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUNQLFNBQVMsQ0FBQyxHQUFHLEVBQUU7WUFDZixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDO1lBRXZDLDhEQUE4RDtZQUM5RCx3Q0FBd0M7WUFDeEMsNERBQTREO1lBQzVELGtEQUFrRDtZQUNsRCxzREFBc0Q7WUFDdEQsdUNBQXVDO1lBQ3ZDLDZEQUE2RDtZQUM3RCx1REFBdUQ7WUFDdkQsd0RBQXdEO1lBQ3hELHlCQUF5QjtZQUN6QixJQUFJLG1CQUFtQixHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUMsQ0FBQztZQUVqRixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTztpQkFDekIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUU7Z0JBQ2IsNENBQTRDO2dCQUM1QyxVQUFVLENBQUMsR0FBRyxFQUFFO29CQUNkLG9DQUFvQztvQkFDcEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQztvQkFFeEMscUNBQXFDO29CQUNyQyxNQUFNLGtCQUFrQixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQyxDQUFDO29CQUVsRSxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFdBQVcsQ0FBQztvQkFDOUMsSUFBSSxVQUFVLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLElBQUksa0JBQWtCLEVBQUUsQ0FBQzt3QkFFakUsd0NBQXdDO3dCQUV4QyxvRkFBb0Y7d0JBQ3BGLE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxtQkFBbUI7K0JBQzVDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFDO3dCQUV0RiwyQ0FBMkM7d0JBQzNDLDhEQUE4RDt3QkFDOUQsSUFBSSxvQkFBb0I7K0JBQ25CLENBQUMsVUFBVSxDQUFDLFVBQVU7K0JBQ3RCLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsVUFBVSxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7NEJBQ3JHLFVBQVUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUMsQ0FBQzt3QkFDMUQsQ0FBQzt3QkFFRCwrQkFBK0I7d0JBQy9CLFVBQVUsQ0FBQyxHQUFHLEVBQUU7NEJBQ2QsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7d0JBQzFCLENBQUMsQ0FBQyxDQUFDO29CQUNMLENBQUM7b0JBRUQsdUJBQXVCO29CQUN2QixtQkFBbUIsR0FBRyxrQkFBa0IsQ0FBQztnQkFDM0MsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ04sQ0FBQyxDQUFDLENBQ0g7YUFDQSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQzthQUNoQyxTQUFTLEVBQUUsQ0FBQztRQUVmLG9GQUFvRjtRQUNwRixzQkFBc0I7UUFDdEIsSUFBSSxDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FDNUIsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FDM0IsQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsRUFBRTtZQUMvQiwyQ0FBMkM7WUFDM0MsSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ25CLElBQUksa0JBQWtCLEVBQUUsQ0FBQztvQkFDdkIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLG9DQUFvQyxDQUFDLENBQUM7Z0JBQ3ZGLENBQUM7cUJBQU0sQ0FBQztvQkFDTixJQUFJLENBQUMsU0FBUyxDQUFDLGVBQWUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsb0NBQW9DLENBQUMsQ0FBQztnQkFDMUYsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUVILDJHQUEyRztRQUMzRyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRTthQUN6QixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQzthQUNoQyxTQUFTLENBQUMsR0FBRyxFQUFFO1lBQ2QsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUM3QixJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUMxQixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFTCxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUU1QixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FDcEIsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FDM0IsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFO1lBQ2YsNENBQTRDO1lBQzVDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUN4QyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCw2QkFBNkIsQ0FBQyxLQUFjO1FBQzFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzdCLENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzdCLENBQUM7SUFFRCwyQkFBMkI7UUFDekIsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMscUJBQXFCLENBQUM7SUFDL0QsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxjQUFjLENBQUMsS0FBb0I7UUFDakMseUZBQXlGO1FBRXpGLHFKQUFxSjtRQUNySixJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUM7ZUFDcEMsQ0FBQyxJQUFJLENBQUMsNEJBQTRCLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLEtBQUssQ0FBQyxDQUFDLEVBQ3ZGLENBQUM7WUFDRCxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDMUIsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLElBQUksS0FBSyxDQUFDLEdBQUcsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQ2xFLHlEQUF5RDtZQUN6RCxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDbEMsQ0FBQztRQUVELDBHQUEwRztRQUMxRyxJQUFJLElBQUksQ0FBQywwQkFBMEIsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFFBQVEsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNsQixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDMUIsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSCxZQUFZLENBQUMsS0FBb0I7UUFDL0IsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQ3pELE1BQU0sc0JBQXNCLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyx3QkFBd0IsRUFBRSxDQUFDO1lBQ3pFLE1BQU0sS0FBSyxHQUFHLElBQUk