ngx-mat-select-search
Version:
Angular component providing an input field for searching / filtering MatSelect options of the Angular Material library.
734 lines (726 loc) • 45 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, InjectionToken, EventEmitter, ElementRef, forwardRef, ContentChild, ViewChild, Output, Input, Inject, Optional, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
import * as i3 from '@angular/forms';
import { FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import * as i11 from '@angular/material/core';
import { MatOption } from '@angular/material/core';
import * as i12 from '@angular/material/form-field';
import { MatFormField } from '@angular/material/form-field';
import * as i10 from '@angular/material/select';
import { MatSelect } from '@angular/material/select';
import { BehaviorSubject, of, combineLatest, Subject } from 'rxjs';
import { switchMap, map, startWith, delay, takeUntil, take, tap, filter } from 'rxjs/operators';
import * as i1 from '@angular/cdk/scrolling';
import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i4 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import * as i5 from '@angular/material/checkbox';
import { MatCheckboxModule } from '@angular/material/checkbox';
import * as i6 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i7 from '@angular/material/progress-spinner';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import * as i8 from '@angular/material/tooltip';
import { MatTooltipModule } from '@angular/material/tooltip';
import * as i9 from '@angular/material/divider';
import { MatDividerModule } from '@angular/material/divider';
/**
* Directive for providing a custom clear-icon.
* e.g.
* <ngx-mat-select-search [formControl]="bankFilterCtrl">
* <mat-icon ngxMatSelectSearchClear>delete</mat-icon>
* </ngx-mat-select-search>
*/
class MatSelectSearchClearDirective {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: MatSelectSearchClearDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.5", type: MatSelectSearchClearDirective, isStandalone: false, selector: "[ngxMatSelectSearchClear]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: MatSelectSearchClearDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxMatSelectSearchClear]',
standalone: false
}]
}] });
/** List of inputs of NgxMatSelectSearchComponent that can be configured with a global default. */
const configurableDefaultOptions = [
'ariaLabel',
'clearSearchInput',
'closeIcon',
'closeSvgIcon',
'disableInitialFocus',
'disableScrollToActiveOnOptionsChanged',
'enableClearOnEscapePressed',
'hideClearSearchButton',
'noEntriesFoundLabel',
'placeholderLabel',
'preventHomeEndKeyPropagation',
'searching',
];
/**
* InjectionToken that can be used to specify global options. e.g.
*
* ```typescript
* providers: [
* {
* provide: MAT_SELECTSEARCH_DEFAULT_OPTIONS,
* useValue: <MatSelectSearchOptions>{
* closeIcon: 'delete',
* noEntriesFoundLabel: 'No options found'
* }
* }
* ]
* ```
*
* See the corresponding inputs of `MatSelectSearchComponent` for documentation.
*/
const MAT_SELECTSEARCH_DEFAULT_OPTIONS = new InjectionToken('mat-selectsearch-default-options');
/**
* Directive for providing a custom no entries found element.
* e.g.
* <ngx-mat-select-search [formControl]="bankFilterCtrl">
* <span ngxMatSelectNoEntriesFound>
* No entries found <button>Add</button>
* </span>
* </ngx-mat-select-search>
*/
class MatSelectNoEntriesFoundDirective {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: MatSelectNoEntriesFoundDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.1.5", type: MatSelectNoEntriesFoundDirective, isStandalone: false, selector: "[ngxMatSelectNoEntriesFound]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: MatSelectNoEntriesFoundDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxMatSelectNoEntriesFound]',
standalone: false
}]
}] });
/* tslint:disable:member-ordering component-selector */
/**
* 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)
* );
* }
* }
*/
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;
onTouched = (_) => { };
/** Reference to the MatSelect options */
set _options(_options) {
this._options$.next(_options);
}
get _options() {
return this._options$.getValue();
}
_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 (defaultOptions.hasOwnProperty(key)) {
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
// tslint:disable-next-line:max-line-length
// 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);
}
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: "19.1.5", 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: "19.1.5", type: MatSelectSearchComponent, isStandalone: false, 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-input::placeholder{color:var(--mdc-filled-text-field-input-text-placeholder-color)}.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", "aria-expanded", "aria-controls", "aria-owns", "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: "19.1.5", ngImport: i0, type: MatSelectSearchComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-mat-select-search', standalone: false, 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-input::placeholder{color:var(--mdc-filled-text-field-input-text-placeholder-color)}.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]
}] } });
/**
* Copyright (c) 2018 Bithost GmbH All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
const MatSelectSearchVersion = '8.0.0';
class NgxMatSelectSearchModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NgxMatSelectSearchModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.5", ngImport: i0, type: NgxMatSelectSearchModule, declarations: [MatSelectSearchComponent,
MatSelectSearchClearDirective,
MatSelectNoEntriesFoundDirective], imports: [CommonModule,
ReactiveFormsModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
MatProgressSpinnerModule,
MatTooltipModule,
MatDividerModule], exports: [MatSelectSearchComponent,
MatSelectSearchClearDirective,
MatSelectNoEntriesFoundDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NgxMatSelectSearchModule, imports: [CommonModule,
ReactiveFormsModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
MatProgressSpinnerModule,
MatTooltipModule,
MatDividerModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NgxMatSelectSearchModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
ReactiveFormsModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
MatProgressSpinnerModule,
MatTooltipModule,
MatDividerModule,
],
declarations: [
MatSelectSearchComponent,
MatSelectSearchClearDirective,
MatSelectNoEntriesFoundDirective
],
exports: [
MatSelectSearchComponent,
MatSelectSearchClearDirective,
MatSelectNoEntriesFoundDirective
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { MAT_SELECTSEARCH_DEFAULT_OPTIONS, MatSelectNoEntriesFoundDirective, MatSelectSearchClearDirective, MatSelectSearchComponent, MatSelectSearchVersion, NgxMatSelectSearchModule, configurableDefaultOptions };
//# sourceMappingURL=ngx-mat-select-search.mjs.map