@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
410 lines • 79.4 kB
JavaScript
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import { ENTER } from '@angular/cdk/keycodes';
import { Platform } from '@angular/cdk/platform';
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, forwardRef, HostBinding, inject, Input, Output, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { BehaviorSubject, ReplaySubject, Subject } from 'rxjs';
import { debounceTime, delay, distinctUntilChanged, filter, map, skip, take, takeUntil } from 'rxjs/operators';
import { TypeaheadKeyService } from '../typeahead/index';
import * as i0 from "@angular/core";
import * as i1 from "../../directives/accessibility/focus-indicator/focus-indicator.directive";
import * as i2 from "@angular/common";
import * as i3 from "@angular/forms";
import * as i4 from "../tag-input/tag-input.component";
import * as i5 from "../typeahead/typeahead.component";
let uniqueId = 0;
export const SELECT_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectComponent),
multi: true
};
export class SelectComponent {
constructor() {
this._element = inject(ElementRef);
this._platform = inject(Platform);
this._typeaheadKeyService = inject(TypeaheadKeyService);
this._changeDetector = inject(ChangeDetectorRef);
this._document = inject(DOCUMENT);
/** A unique id for the component. */
this.id = `ux-select-${++uniqueId}`;
/**
* Controls whether the value of the single select control can be cleared by deleting the selected value in the
* input field. This does not affect the initial state of the control, so specify a value for `value` if null should
* never be allowed.
*/
this.allowNull = false;
/** Controls the disabled state of the tag input. */
this.disabled = false;
/** The positioning of the typeahead dropdown in relation to its parent. */
this.dropDirection = 'down';
/** The maximum height of the typeahead dropdown, as a CSS value. */
this.maxHeight = '250px';
/**
* Controls whether the user can select more than one option in the select control. If set to true, selected
* options will appear as tags in the input area. If set to false, the selected value will appear as editable text
* in the input area.
*/
this.multiple = false;
/**
* The number of options to request in a page. This should ideally be more than twice the number of items which
* fit into the height of the dropdown, but this is not required.
*/
this.pageSize = 20;
/** The placeholder text which appears in the text input area when it is empty. */
this.placeholder = '';
/**
* Defines the `autocomplete` property on the `input` element which can be used to prevent the browser from
* displaying autocomplete suggestions.
*/
this.autocomplete = 'off';
/** If `true` the input field will be readonly and selection can only occur by using the dropdown. */
this.readonlyInput = false;
/** Determine if we should show the clear all button */
this.clearButton = false;
/** Determine an aria label for the clear button */
this.clearButtonAriaLabel = 'Reset selection';
/** Emits when `value` changes. */
this.valueChange = new EventEmitter();
/** Emits when `input` changes. */
this.inputChange = new EventEmitter();
/** Emits when `dropdownOpen` changes. */
this.dropdownOpenChange = new EventEmitter();
/** Emits when recently selected options change. */
this.recentOptionsChange = new EventEmitter();
this._value$ = new ReplaySubject(1);
this._hasValue = false;
this._input$ = new BehaviorSubject({ userInteraction: false, value: '' });
this._dropdownOpen = false;
this._userInput = false;
this._filterDebounceTime = 200;
this._autoCloseDropdown = true;
// eslint-disable-next-line @typescript-eslint/no-empty-function
this._onChange = (_) => { };
// eslint-disable-next-line @typescript-eslint/no-empty-function
this._onTouched = () => { };
this._onDestroy = new Subject();
}
/** The selected option (for single select) or array of options (for multiple select). */
set value(value) {
this._value$.next(value);
}
get value() {
return this._value;
}
/** The text in the input area. This is used to filter the options dropdown. */
set input(value) {
this._input$.next({ ...this._input$.value, value });
}
get input() {
return this._input$.value.value;
}
/** The status of the typeahead dropdown. */
set dropdownOpen(value) {
this._dropdownOpen = value;
this.dropdownOpenChange.emit(value);
}
get dropdownOpen() {
return this._dropdownOpen;
}
/** Determine if the dropdown panel should close on external click.*/
set autoCloseDropdown(value) {
this._autoCloseDropdown = coerceBooleanProperty(value);
}
get autoCloseDropdown() {
return this._autoCloseDropdown;
}
/** Specify the debounceTime value for the select filter */
get filterDebounceTime() {
return this._filterDebounceTime;
}
set filterDebounceTime(filterDebounceTime) {
this._filterDebounceTime = coerceNumberProperty(filterDebounceTime);
}
ngOnInit() {
// Emit change events
this._value$.pipe(takeUntil(this._onDestroy), distinctUntilChanged()).subscribe(value => {
this._value = value;
this._hasValue = !!value;
});
// Changes to the input field
this._input$.pipe(skip(1), filter(() => this.allowNull), filter(value => !this.multiple && value.value !== this.getDisplay(this.value)), takeUntil(this._onDestroy)).subscribe((input) => {
if (input.userInteraction && input.value === '') {
this.value = null;
this._onChange(null);
this.valueChange.next(null);
}
});
// open the dropdown once the filter debounce has elapsed
this.filter$.pipe(filter(() => this._userInput), take(1), takeUntil(this._onDestroy))
.subscribe(() => {
this.dropdownOpen = true;
this._userInput = false;
});
// Update the single-select input when the model changes
this._value$.pipe(distinctUntilChanged(), delay(0), filter(value => value !== null && !this.multiple), takeUntil(this._onDestroy)).subscribe(value => {
const inputValue = this.getDisplay(value);
// check if the input value has changed and if so the emit
if (inputValue !== this.input) {
this.input = inputValue;
this.inputChange.emit(this.input);
}
this._changeDetector.markForCheck();
});
}
ngOnChanges(changes) {
if (changes.multiple && !changes.multiple.firstChange && changes.multiple.currentValue !== changes.multiple.previousValue) {
this.input = '';
}
// Set up filter from input
this.filter$ = this._input$.pipe(map(input => !this.multiple && input.value === this.getDisplay(this.value) ? '' : input.value), debounceTime(this.filterDebounceTime));
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
writeValue(obj) {
if (obj !== undefined && obj !== this.value) {
this.value = obj;
this._changeDetector.markForCheck();
}
}
registerOnChange(fn) {
this._onChange = fn;
}
registerOnTouched(fn) {
this._onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
this._changeDetector.markForCheck();
}
inputClickHandler() {
this.selectInputText();
this.dropdownOpen = true;
}
inputBlurHandler() {
// If a click on the typeahead is in progress, just refocus the input.
// This works around an issue in IE where clicking a scrollbar drops focus.
if (this.singleTypeahead && this.singleTypeahead.clicking) {
this.singleInput.nativeElement.focus();
return;
}
// Close dropdown and reset text input if focus is lost
setTimeout(() => {
if (!this._element.nativeElement.contains(this._document.activeElement) && this._autoCloseDropdown) {
this.dropdownOpen = false;
if (!this.multiple) {
this.input = this.getDisplay(this.value);
}
}
}, 200);
}
/**
* Key handler for single select only. Multiple select key handling is in TagInputComponent.
*/
inputKeyHandler(event) {
// Standard keys for typeahead (up/down/esc)
this._typeaheadKeyService.handleKey(event, this.singleTypeahead);
if (event.keyCode === ENTER) {
if (this._dropdownOpen) {
// Set the highlighted option as the value and close
this.singleTypeahead.selectHighlighted();
}
else {
this.dropdownOpen = true;
}
// Update the input field. If dropdown isn't open then reset it to the previous value.
this.input = this.getDisplay(this.value);
event.preventDefault();
}
// when the user types and the value is not empty then we should open the dropdown except for non printable keys.
if (event.key.length === 1) {
this._userInput = true;
this._dropdownOpen = true;
}
}
/** This gets called whenever the user types in the input */
onInputChange(input) {
this._input$.next({
value: input,
userInteraction: true
});
this.inputChange.emit(this.input);
}
/** Whenever a single select item is selected emit the values */
_singleOptionSelected(event) {
if (event.option !== null && event.option !== this.value) {
this.value = event.option;
this.dropdownOpen = false;
this.valueChange.emit(this.value);
this._onChange(this.value);
}
}
/** Whenever a multi-select item is selected emit the values */
_multipleOptionSelected(selection) {
// update the internal selection
this._value$.next(selection);
this.valueChange.emit(this.value);
this._onChange(this.value);
}
/**
* Returns the display value of the given option.
*/
getDisplay(option) {
if (option === null || option === undefined) {
return '';
}
if (typeof this.display === 'function') {
return this.display(option);
}
// eslint-disable-next-line no-prototype-builtins
if (typeof this.display === 'string' && typeof option === 'object' && option.hasOwnProperty(this.display)) {
return option[this.display];
}
return option;
}
/** Toggle the dropdown open state */
toggle() {
// if the select is disabled then do not show the dropdown
if (this.disabled) {
return;
}
if (this.dropdownOpen) {
this.dropdownOpen = false;
}
else {
this.inputClickHandler();
}
}
/** Handle input focus events */
onFocus() {
// mark form control as touched
this._onTouched();
// if the input is readonly we do not want to select the text on focus
if (this.readonlyInput) {
// cast the select input element
const element = this.singleInput.nativeElement;
// firefox requires a delay before clearing the selection (other browsers don't)
this._platform.FIREFOX ? requestAnimationFrame(() => element.setSelectionRange(0, 0)) : element.setSelectionRange(0, 0);
}
}
clear() {
if (this.disabled) {
return;
}
// clear the value and input text
this.value = null;
this.input = null;
this.selectInputText();
// emit the latest values
this.valueChange.emit(this.value);
this._onChange(this.value);
this.inputChange.emit(this.input);
}
selectInputText() {
if (!this.readonlyInput) {
this.singleInput.nativeElement.select();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: SelectComponent, selector: "ux-select, ux-combobox, ux-dropdown", inputs: { id: "id", value: "value", input: "input", dropdownOpen: "dropdownOpen", options: "options", display: "display", key: "key", allowNull: "allowNull", ariaLabel: "ariaLabel", ariaLabelledby: "ariaLabelledby", listboxAriaLabel: "listboxAriaLabel", disabled: "disabled", dropDirection: "dropDirection", maxHeight: "maxHeight", multiple: "multiple", pageSize: "pageSize", placeholder: "placeholder", tagTemplate: "tagTemplate", optionsHeadingTemplate: "optionsHeadingTemplate", recentOptionsHeadingTemplate: "recentOptionsHeadingTemplate", autocomplete: "autocomplete", loadingTemplate: "loadingTemplate", noOptionsTemplate: "noOptionsTemplate", readonlyInput: "readonlyInput", clearButton: "clearButton", clearButtonAriaLabel: "clearButtonAriaLabel", autoCloseDropdown: "autoCloseDropdown", optionTemplate: "optionTemplate", recentOptions: "recentOptions", recentOptionsMaxCount: "recentOptionsMaxCount", required: "required", filterDebounceTime: "filterDebounceTime" }, outputs: { valueChange: "valueChange", inputChange: "inputChange", dropdownOpenChange: "dropdownOpenChange", recentOptionsChange: "recentOptionsChange" }, host: { properties: { "class.ux-select-custom-icon": "!!icon", "class.ux-select-disabled": "disabled", "attr.id": "this.id" } }, providers: [SELECT_VALUE_ACCESSOR], queries: [{ propertyName: "icon", first: true, predicate: ["icon"], descendants: true }], viewQueries: [{ propertyName: "singleInput", first: true, predicate: ["singleInput"], descendants: true }, { propertyName: "tagInput", first: true, predicate: ["tagInput"], descendants: true }, { propertyName: "multipleTypeahead", first: true, predicate: ["multipleTypeahead"], descendants: true }, { propertyName: "singleTypeahead", first: true, predicate: ["singleTypeahead"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (multiple) {\n <ux-tag-input\n #tagInput=\"ux-tag-input\"\n [id]=\"id + '-input'\"\n [tags]=\"_value$ | async\"\n (tagsChange)=\"_multipleOptionSelected($event)\"\n [ariaLabelledby]=\"ariaLabelledby\"\n [(input)]=\"input\"\n [required]=\"required\"\n (inputChange)=\"onInputChange($event)\"\n [ariaLabel]=\"ariaLabel\"\n [autocomplete]=\"autocomplete\"\n [addOnPaste]=\"false\"\n [disabled]=\"disabled\"\n [display]=\"display\"\n [freeInput]=\"false\"\n [placeholder]=\"placeholder || ''\"\n [tagTemplate]=\"tagTemplate\"\n (inputFocus)=\"onFocus()\"\n [showTypeaheadOnClick]=\"true\"\n [readonlyInput]=\"readonlyInput\"\n [icon]=\"icon\"\n [clearButton]=\"clearButton\"\n [autoCloseDropdown]=\"autoCloseDropdown\"\n [clearButtonAriaLabel]=\"clearButtonAriaLabel\">\n <ux-typeahead #multipleTypeahead\n [ariaLabel]=\"listboxAriaLabel\"\n [id]=\"id + '-typeahead'\"\n [options]=\"options\"\n [filter]=\"filter$ | async\"\n [(open)]=\"dropdownOpen\"\n [display]=\"display\"\n [key]=\"key\"\n [disabledOptions]=\"_value$ | async\"\n [dropDirection]=\"dropDirection\"\n [maxHeight]=\"maxHeight\"\n [multiselectable]=\"true\"\n [pageSize]=\"pageSize\"\n [selectFirst]=\"true\"\n [loadingTemplate]=\"loadingTemplate\"\n [optionTemplate]=\"optionTemplate\"\n [noOptionsTemplate]=\"noOptionsTemplate\"\n [recentOptions]=\"recentOptions\"\n [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n (recentOptionsChange)=\"recentOptionsChange.emit($event)\"\n [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\">\n </ux-typeahead>\n </ux-tag-input>\n}\n\n@if (!multiple) {\n <div\n class=\"ux-select-container\"\n [class.disabled]=\"disabled\"\n aria-haspopup=\"listbox\">\n <input #singleInput type=\"text\"\n [attr.id]=\"id + '-input'\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n class=\"form-control\"\n [required]=\"required\"\n [class.ux-tag-input-clear-inset]=\"clearButton && allowNull && _hasValue\"\n [attr.aria-activedescendant]=\"highlightedElement?.id\"\n aria-autocomplete=\"list\"\n role=\"combobox\"\n [attr.aria-controls]=\"id + '-typeahead'\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-expanded]=\"dropdownOpen\"\n [autocomplete]=\"autocomplete\"\n [(ngModel)]=\"input\"\n (ngModelChange)=\"onInputChange($event)\"\n [placeholder]=\"placeholder || ''\"\n [disabled]=\"disabled\"\n (click)=\"toggle()\"\n (focus)=\"onFocus()\"\n (blur)=\"inputBlurHandler()\"\n (keydown)=\"inputKeyHandler($event)\"\n [readonly]=\"readonlyInput\">\n <div class=\"ux-select-icons\">\n @if (clearButton && allowNull && _hasValue) {\n <i\n uxFocusIndicator\n [attr.tabindex]=\"disabled ? -1 : 0\"\n [attr.aria-label]=\"clearButtonAriaLabel\"\n class=\"ux-select-icon ux-icon ux-icon-close ux-select-clear-icon\"\n (click)=\"clear(); $event.stopPropagation()\"\n (keydown.enter)=\"clear(); $event.stopPropagation()\">\n </i>\n }\n @if (!icon) {\n <i\n class=\"ux-select-icon ux-icon ux-select-chevron-icon\"\n [class.ux-icon-up]=\"dropDirection === 'up'\"\n [class.ux-icon-down]=\"dropDirection === 'down'\"\n (click)=\"toggle(); $event.stopPropagation(); singleInput.focus();\">\n </i>\n }\n @if (icon) {\n <div class=\"ux-custom-icon\">\n <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n </div>\n }\n </div>\n <ux-typeahead #singleTypeahead\n [ariaLabel]=\"listboxAriaLabel\"\n [id]=\"id + '-typeahead'\"\n [active]=\"_value$ | async\"\n [options]=\"options\"\n [filter]=\"filter$ | async\"\n [(open)]=\"dropdownOpen\"\n [display]=\"display\"\n [key]=\"key\"\n [dropDirection]=\"dropDirection\"\n [maxHeight]=\"maxHeight\"\n [multiselectable]=\"false\"\n [openOnFilterChange]=\"false\"\n [pageSize]=\"pageSize\"\n [selectFirst]=\"true\"\n [loadingTemplate]=\"loadingTemplate\"\n [optionTemplate]=\"optionTemplate\"\n [noOptionsTemplate]=\"noOptionsTemplate\"\n [recentOptions]=\"recentOptions\"\n [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\"\n (optionSelected)=\"_singleOptionSelected($event)\"\n (highlightedElementChange)=\"highlightedElement = $event\"\n (recentOptionsChange)=\"recentOptionsChange.emit($any($event))\">\n </ux-typeahead>\n </div>\n }\n", dependencies: [{ kind: "directive", type: i1.FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.TagInputComponent, selector: "ux-tag-input", inputs: ["id", "tags", "input", "display", "addOnPaste", "ariaLabel", "ariaLabelledby", "disabled", "required", "enforceTagLimits", "freeInput", "readonlyInput", "maxTags", "minTags", "placeholder", "showTypeaheadOnClick", "tagDelimiters", "tagPattern", "tagTemplate", "tagClass", "validationErrors", "autocomplete", "createTag", "icon", "clearButton", "clearButtonAriaLabel", "autoCloseDropdown"], outputs: ["tagsChange", "inputChange", "tagAdding", "tagAdded", "tagInvalidated", "tagRemoving", "tagRemoved", "tagClick", "inputFocus", "inputBlur"], exportAs: ["ux-tag-input"] }, { kind: "component", type: i5.TypeaheadComponent, selector: "ux-typeahead", inputs: ["id", "options", "filter", "open", "display", "key", "disabledOptions", "dropDirection", "maxHeight", "multiselectable", "ariaLabel", "openOnFilterChange", "pageSize", "selectFirst", "selectOnEnter", "loading", "loadingTemplate", "optionTemplate", "noOptionsTemplate", "active", "recentOptions", "recentOptionsMaxCount", "recentOptionsHeadingTemplate", "optionsHeadingTemplate"], outputs: ["openChange", "optionSelected", "highlightedChange", "highlightedElementChange", "recentOptionsChange"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: SelectComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-select, ux-combobox, ux-dropdown', providers: [SELECT_VALUE_ACCESSOR], host: {
'[class.ux-select-custom-icon]': '!!icon',
'[class.ux-select-disabled]': 'disabled'
}, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (multiple) {\n <ux-tag-input\n #tagInput=\"ux-tag-input\"\n [id]=\"id + '-input'\"\n [tags]=\"_value$ | async\"\n (tagsChange)=\"_multipleOptionSelected($event)\"\n [ariaLabelledby]=\"ariaLabelledby\"\n [(input)]=\"input\"\n [required]=\"required\"\n (inputChange)=\"onInputChange($event)\"\n [ariaLabel]=\"ariaLabel\"\n [autocomplete]=\"autocomplete\"\n [addOnPaste]=\"false\"\n [disabled]=\"disabled\"\n [display]=\"display\"\n [freeInput]=\"false\"\n [placeholder]=\"placeholder || ''\"\n [tagTemplate]=\"tagTemplate\"\n (inputFocus)=\"onFocus()\"\n [showTypeaheadOnClick]=\"true\"\n [readonlyInput]=\"readonlyInput\"\n [icon]=\"icon\"\n [clearButton]=\"clearButton\"\n [autoCloseDropdown]=\"autoCloseDropdown\"\n [clearButtonAriaLabel]=\"clearButtonAriaLabel\">\n <ux-typeahead #multipleTypeahead\n [ariaLabel]=\"listboxAriaLabel\"\n [id]=\"id + '-typeahead'\"\n [options]=\"options\"\n [filter]=\"filter$ | async\"\n [(open)]=\"dropdownOpen\"\n [display]=\"display\"\n [key]=\"key\"\n [disabledOptions]=\"_value$ | async\"\n [dropDirection]=\"dropDirection\"\n [maxHeight]=\"maxHeight\"\n [multiselectable]=\"true\"\n [pageSize]=\"pageSize\"\n [selectFirst]=\"true\"\n [loadingTemplate]=\"loadingTemplate\"\n [optionTemplate]=\"optionTemplate\"\n [noOptionsTemplate]=\"noOptionsTemplate\"\n [recentOptions]=\"recentOptions\"\n [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n (recentOptionsChange)=\"recentOptionsChange.emit($event)\"\n [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\">\n </ux-typeahead>\n </ux-tag-input>\n}\n\n@if (!multiple) {\n <div\n class=\"ux-select-container\"\n [class.disabled]=\"disabled\"\n aria-haspopup=\"listbox\">\n <input #singleInput type=\"text\"\n [attr.id]=\"id + '-input'\"\n [attr.aria-labelledby]=\"ariaLabelledby\"\n class=\"form-control\"\n [required]=\"required\"\n [class.ux-tag-input-clear-inset]=\"clearButton && allowNull && _hasValue\"\n [attr.aria-activedescendant]=\"highlightedElement?.id\"\n aria-autocomplete=\"list\"\n role=\"combobox\"\n [attr.aria-controls]=\"id + '-typeahead'\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-expanded]=\"dropdownOpen\"\n [autocomplete]=\"autocomplete\"\n [(ngModel)]=\"input\"\n (ngModelChange)=\"onInputChange($event)\"\n [placeholder]=\"placeholder || ''\"\n [disabled]=\"disabled\"\n (click)=\"toggle()\"\n (focus)=\"onFocus()\"\n (blur)=\"inputBlurHandler()\"\n (keydown)=\"inputKeyHandler($event)\"\n [readonly]=\"readonlyInput\">\n <div class=\"ux-select-icons\">\n @if (clearButton && allowNull && _hasValue) {\n <i\n uxFocusIndicator\n [attr.tabindex]=\"disabled ? -1 : 0\"\n [attr.aria-label]=\"clearButtonAriaLabel\"\n class=\"ux-select-icon ux-icon ux-icon-close ux-select-clear-icon\"\n (click)=\"clear(); $event.stopPropagation()\"\n (keydown.enter)=\"clear(); $event.stopPropagation()\">\n </i>\n }\n @if (!icon) {\n <i\n class=\"ux-select-icon ux-icon ux-select-chevron-icon\"\n [class.ux-icon-up]=\"dropDirection === 'up'\"\n [class.ux-icon-down]=\"dropDirection === 'down'\"\n (click)=\"toggle(); $event.stopPropagation(); singleInput.focus();\">\n </i>\n }\n @if (icon) {\n <div class=\"ux-custom-icon\">\n <ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\n </div>\n }\n </div>\n <ux-typeahead #singleTypeahead\n [ariaLabel]=\"listboxAriaLabel\"\n [id]=\"id + '-typeahead'\"\n [active]=\"_value$ | async\"\n [options]=\"options\"\n [filter]=\"filter$ | async\"\n [(open)]=\"dropdownOpen\"\n [display]=\"display\"\n [key]=\"key\"\n [dropDirection]=\"dropDirection\"\n [maxHeight]=\"maxHeight\"\n [multiselectable]=\"false\"\n [openOnFilterChange]=\"false\"\n [pageSize]=\"pageSize\"\n [selectFirst]=\"true\"\n [loadingTemplate]=\"loadingTemplate\"\n [optionTemplate]=\"optionTemplate\"\n [noOptionsTemplate]=\"noOptionsTemplate\"\n [recentOptions]=\"recentOptions\"\n [recentOptionsMaxCount]=\"recentOptionsMaxCount\"\n [optionsHeadingTemplate]=\"optionsHeadingTemplate\"\n [recentOptionsHeadingTemplate]=\"recentOptionsHeadingTemplate\"\n (optionSelected)=\"_singleOptionSelected($event)\"\n (highlightedElementChange)=\"highlightedElement = $event\"\n (recentOptionsChange)=\"recentOptionsChange.emit($any($event))\">\n </ux-typeahead>\n </div>\n }\n" }]
}], propDecorators: { id: [{
type: Input
}, {
type: HostBinding,
args: ['attr.id']
}], value: [{
type: Input
}], input: [{
type: Input
}], dropdownOpen: [{
type: Input
}], options: [{
type: Input
}], display: [{
type: Input
}], key: [{
type: Input
}], allowNull: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaLabelledby: [{
type: Input
}], listboxAriaLabel: [{
type: Input
}], disabled: [{
type: Input
}], dropDirection: [{
type: Input
}], maxHeight: [{
type: Input
}], multiple: [{
type: Input
}], pageSize: [{
type: Input
}], placeholder: [{
type: Input
}], tagTemplate: [{
type: Input
}], optionsHeadingTemplate: [{
type: Input
}], recentOptionsHeadingTemplate: [{
type: Input
}], autocomplete: [{
type: Input
}], loadingTemplate: [{
type: Input
}], noOptionsTemplate: [{
type: Input
}], readonlyInput: [{
type: Input
}], clearButton: [{
type: Input
}], clearButtonAriaLabel: [{
type: Input
}], autoCloseDropdown: [{
type: Input
}], optionTemplate: [{
type: Input
}], recentOptions: [{
type: Input
}], recentOptionsMaxCount: [{
type: Input
}], required: [{
type: Input
}], filterDebounceTime: [{
type: Input
}], valueChange: [{
type: Output
}], inputChange: [{
type: Output
}], dropdownOpenChange: [{
type: Output
}], recentOptionsChange: [{
type: Output
}], icon: [{
type: ContentChild,
args: ['icon', { static: false }]
}], singleInput: [{
type: ViewChild,
args: ['singleInput', { static: false }]
}], tagInput: [{
type: ViewChild,
args: ['tagInput', { static: false }]
}], multipleTypeahead: [{
type: ViewChild,
args: ['multipleTypeahead', { static: false }]
}], singleTypeahead: [{
type: ViewChild,
args: ['singleTypeahead', { static: false }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VsZWN0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3NlbGVjdC9zZWxlY3QuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvc2VsZWN0L3NlbGVjdC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQWdCLHFCQUFxQixFQUFFLG9CQUFvQixFQUFlLE1BQU0sdUJBQXVCLENBQUM7QUFDL0csT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzlDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNqRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUFFLHVCQUF1QixFQUFFLGlCQUFpQixFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQWdDLE1BQU0sRUFBOEMsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25RLE9BQU8sRUFBd0IsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6RSxPQUFPLEVBQUUsZUFBZSxFQUFjLGFBQWEsRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDM0UsT0FBTyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRy9HLE9BQU8sRUFBc0IsbUJBQW1CLEVBQXdCLE1BQU0sb0JBQW9CLENBQUM7Ozs7Ozs7QUFHbkcsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0FBRWpCLE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFtQjtJQUNq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QixFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRTtnQkFDQSxlQUFlO3NCQUEvRCxTQUFTO3VCQUFDLGlCQUFpQixFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEJvb2xlYW5JbnB1dCwgY29lcmNlQm9vbGVhblByb3BlcnR5LCBjb2VyY2VOdW1iZXJQcm9wZXJ0eSwgTnVtYmVySW5wdXQgfSBmcm9tICdAYW5ndWxhci9jZGsvY29lcmNpb24nO1xyXG5pbXBvcnQgeyBFTlRFUiB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9rZXljb2Rlcyc7XHJcbmltcG9ydCB7IFBsYXRmb3JtIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL3BsYXRmb3JtJztcclxuaW1wb3J0IHsgRE9DVU1FTlQgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xyXG5pbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ2hhbmdlRGV0ZWN0b3JSZWYsIENvbXBvbmVudCwgQ29udGVudENoaWxkLCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIGZvcndhcmRSZWYsIEhvc3RCaW5kaW5nLCBpbmplY3QsIElucHV0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSwgT25Jbml0LCBPdXRwdXQsIFNpbXBsZUNoYW5nZXMsIFN0YXRpY1Byb3ZpZGVyLCBUZW1wbGF0ZVJlZiwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBOR19WQUxVRV9BQ0NFU1NPUiB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBPYnNlcnZhYmxlLCBSZXBsYXlTdWJqZWN0LCBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XHJcbmltcG9ydCB7IGRlYm91bmNlVGltZSwgZGVsYXksIGRpc3RpbmN0VW50aWxDaGFuZ2VkLCBmaWx0ZXIsIG1hcCwgc2tpcCwgdGFrZSwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xyXG5pbXBvcnQgeyBJbmZpbml0ZVNjcm9sbExvYWRGdW5jdGlvbiB9IGZyb20gJy4uLy4uL2RpcmVjdGl2ZXMvaW5maW5pdGUtc2Nyb2xsL2luZGV4JztcclxuaW1wb3J0IHsgVGFnSW5wdXRDb21wb25lbnQsIFRhZ1RlbXBsYXRlQ29udGV4dCB9IGZyb20gJy4uL3RhZy1pbnB1dC9pbmRleCc7XHJcbmltcG9ydCB7IFR5cGVhaGVhZENvbXBvbmVudCwgVHlwZWFoZWFkS2V5U2VydmljZSwgVHlwZWFoZWFkT3B0aW9uRXZlbnQgfSBmcm9tICcuLi90eXBlYWhlYWQvaW5kZXgnO1xyXG5pbXBvcnQgeyBUeXBlYWhlYWRPcHRpb25Db250ZXh0IH0gZnJvbSAnLi4vdHlwZWFoZWFkL3R5cGVhaGVhZC1vcHRpb24tY29udGV4dCc7XHJcblxyXG5sZXQgdW5pcXVlSWQgPSAwO1xyXG5cclxuZXhwb3J0IGNvbnN0IFNFTEVDVF9WQUxVRV9BQ0NFU1NPUjogU3RhdGljUHJvdmlkZXIgPSB7XHJcbiAgICBwcm92aWRlOiBOR19WQUxVRV9BQ0NFU1NPUixcclxuICAgIHVzZUV4aXN0aW5nOiBmb3J3YXJkUmVmKCgpID0+IFNlbGVjdENvbXBvbmVudCksXHJcbiAgICBtdWx0aTogdHJ1ZVxyXG59O1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgICBzZWxlY3RvcjogJ3V4LXNlbGVjdCwgdXgtY29tYm9ib3gsIHV4LWRyb3Bkb3duJyxcclxuICAgIHRlbXBsYXRlVXJsOiAnc2VsZWN0LmNvbXBvbmVudC5odG1sJyxcclxuICAgIHByb3ZpZGVyczogW1NFTEVDVF9WQUxVRV9BQ0NFU1NPUl0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgICAgJ1tjbGFzcy51eC1zZWxlY3QtY3VzdG9tLWljb25dJzogJyEhaWNvbicsXHJcbiAgICAgICAgJ1tjbGFzcy51eC1zZWxlY3QtZGlzYWJsZWRdJzogJ2Rpc2FibGVkJ1xyXG4gICAgfSxcclxuICAgIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgU2VsZWN0Q29tcG9uZW50PFQ+IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSwgQ29udHJvbFZhbHVlQWNjZXNzb3Ige1xyXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZWxlbWVudCA9IGluamVjdChFbGVtZW50UmVmKTtcclxuXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9wbGF0Zm9ybSA9IGluamVjdChQbGF0Zm9ybSk7XHJcblxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBfdHlwZWFoZWFkS2V5U2VydmljZSA9IGluamVjdChUeXBlYWhlYWRLZXlTZXJ2aWNlKTtcclxuXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jaGFuZ2VEZXRlY3RvciA9IGluamVjdChDaGFuZ2VEZXRlY3RvclJlZik7XHJcblxyXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZG9jdW1lbnQgPSBpbmplY3Q8RG9jdW1lbnQ+KERPQ1VNRU5UKTtcclxuXHJcbiAgICAvKiogQSB1bmlxdWUgaWQgZm9yIHRoZSBjb21wb25lbnQuICovXHJcbiAgICBASW5wdXQoKSBASG9zdEJpbmRpbmcoJ2F0dHIuaWQnKSBpZDogc3RyaW5nID0gYHV4LXNlbGVjdC0keysrdW5pcXVlSWR9YDtcclxuXHJcbiAgICAvKiogVGhlIHNlbGVjdGVkIG9wdGlvbiAoZm9yIHNpbmdsZSBzZWxlY3QpIG9yIGFycmF5IG9mIG9wdGlvbnMgKGZvciBtdWx0aXBsZSBzZWxlY3QpLiAqL1xyXG4gICAgQElucHV0KClcclxuICAgIHNldCB2YWx1ZSh2YWx1ZTogVCB8IFJlYWRvbmx5QXJyYXk8VD4pIHtcclxuICAgICAgICB0aGlzLl92YWx1ZSQubmV4dCh2YWx1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IHZhbHVlKCk6IFQgfCBSZWFkb25seUFycmF5PFQ+IHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fdmFsdWU7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqIFRoZSB0ZXh0IGluIHRoZSBpbnB1dCBhcmVhLiBUaGlzIGlzIHVzZWQgdG8gZmlsdGVyIHRoZSBvcHRpb25zIGRyb3Bkb3duLiAqL1xyXG4gICAgQElucHV0KClcclxuICAgIHNldCBpbnB1dCh2YWx1ZTogc3RyaW5nKSB7XHJcbiAgICAgICAgdGhpcy5faW5wdXQkLm5leHQoeyAuLi50aGlzLl9pbnB1dCQudmFsdWUsIHZhbHVlIH0pO1xyXG4gICAgfVxyXG4gICAgZ2V0IGlucHV0KCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9pbnB1dCQudmFsdWUudmFsdWU7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqIFRoZSBzdGF0dXMgb2YgdGhlIHR5cGVhaGVhZCBkcm9wZG93bi4gKi9cclxuICAgIEBJbnB1dCgpXHJcbiAgICBzZXQgZHJvcGRvd25PcGVuKHZhbHVlOiBib29sZWFuKSB7XHJcbiAgICAgICAgdGhpcy5fZHJvcGRvd25PcGVuID0gdmFsdWU7XHJcbiAgICAgICAgdGhpcy5kcm9wZG93bk9wZW5DaGFuZ2UuZW1pdCh2YWx1ZSk7XHJcbiAgICB9XHJcbiAgICBnZXQgZHJvcGRvd25PcGVuKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9kcm9wZG93bk9wZW47XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBJZiBhbiBhcnJheSBpcyBwcm92aWRlZCwgdGhpcyBpcyB0aGUgbGlzdCBvZiBvcHRpb25zIHdoaWNoIGNhbiBiZSBjaG9zZW4gZnJvbS4gSXQgY2FuIGJlIGFuIGFycmF5IG9mIHN0cmluZ3Mgb3JcclxuICAgICAqIGN1c3RvbSBvYmplY3RzLiBJZiBjdXN0b20gb2JqZWN0cyBhcmUgc