@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
JavaScript
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0LXNlbGVjdC1zZWFyY2guY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC9tYXQtc2VsZWN0LXNlYXJjaC9tYXQtc2VsZWN0LXNlYXJjaC5jb21wb25lbnQudHMiLCIuLi8uLi9zcmMvYXBwL21hdC1zZWxlY3Qtc2VhcmNoL21hdC1zZWxlY3Qtc2VhcmNoLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVFBLE9BQU8sRUFDTCx1QkFBdUIsRUFFdkIsU0FBUyxFQUNULFlBQVksRUFDWixVQUFVLEVBQ1YsWUFBWSxFQUNaLFVBQVUsRUFDVixNQUFNLEVBQ04sS0FBSyxFQUdMLFFBQVEsRUFDUixNQUFNLEVBRU4sU0FBUyxFQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBd0IsV0FBVyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDckQsT0FBTyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQWMsRUFBRSxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2hHLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxnQ0FBZ0MsRUFBMEIsTUFBTSxtQkFBbUIsQ0FBQztBQUN6SCxPQUFPLEVBQUUsZ0NBQWdDLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQzs7Ozs7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