@ng-select/ng-select
Version:
Angular ng-select - All in One UI Select, Multiselect and Autocomplete
3,786 lines • 218 kB
JavaScript
import { FlexibleConnectedPositionStrategy, OverlayContainer, createFlexibleConnectedPositionStrategy, createOverlayRef, createNoopScrollStrategy } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { DOCUMENT, NgTemplateOutlet, NgClass } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, ElementRef, input, effect, Directive, TemplateRef, Injectable, booleanAttribute, output, Renderer2, NgZone, DestroyRef, viewChild, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, signal, afterEveryRender, InjectionToken, HostAttributeToken, linkedSignal, numberAttribute, model, contentChild, contentChildren, forwardRef, ViewContainerRef, ChangeDetectorRef, Injector, untracked, runInInjectionContext, isDevMode, HostListener, NgModule } from '@angular/core';
import { EventPhase } from '@angular/core/primitives/event-dispatch';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { animationFrameScheduler, asapScheduler, fromEvent, Subject } from 'rxjs';
import { auditTime, tap, debounceTime, filter, map } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
/**
* @deprecated Default labels are plain text via interpolation. Prefer `{{ label }}` or
* custom templates (`ng-label-tmp` / `ng-option-tmp`) for rich markup. Kept for public API
* compatibility; always writes `textContent` (the `escape` input is ignored).
*/
class NgItemLabelDirective {
constructor() {
this.element = inject(ElementRef);
this.ngItemLabel = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "ngItemLabel" }] : /* istanbul ignore next */ []));
/** @deprecated Ignored — labels are always written as text. */
this.escape = input(true, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "escape" }] : /* istanbul ignore next */ []));
effect(() => {
this.element.nativeElement.textContent = this.ngItemLabel() ?? '';
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgItemLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: NgItemLabelDirective, isStandalone: true, selector: "[ngItemLabel]", inputs: { ngItemLabel: { classPropertyName: "ngItemLabel", publicName: "ngItemLabel", isSignal: true, isRequired: false, transformFunction: null }, escape: { classPropertyName: "escape", publicName: "escape", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgItemLabelDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngItemLabel]',
standalone: true,
}]
}], ctorParameters: () => [], propDecorators: { ngItemLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngItemLabel", required: false }] }], escape: [{ type: i0.Input, args: [{ isSignal: true, alias: "escape", required: false }] }] } });
class NgOptionTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgOptionTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgOptionTemplateDirective, isStandalone: true, selector: "[ng-option-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgOptionTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-option-tmp]',
standalone: true,
}]
}] });
class NgOptgroupTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgOptgroupTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgOptgroupTemplateDirective, isStandalone: true, selector: "[ng-optgroup-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgOptgroupTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-optgroup-tmp]',
standalone: true,
}]
}] });
class NgLabelTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgLabelTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgLabelTemplateDirective, isStandalone: true, selector: "[ng-label-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgLabelTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-label-tmp]',
standalone: true,
}]
}] });
class NgMultiLabelTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgMultiLabelTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgMultiLabelTemplateDirective, isStandalone: true, selector: "[ng-multi-label-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgMultiLabelTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-multi-label-tmp]',
standalone: true,
}]
}] });
class NgHeaderTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgHeaderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgHeaderTemplateDirective, isStandalone: true, selector: "[ng-header-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgHeaderTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-header-tmp]',
standalone: true,
}]
}] });
class NgFooterTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgFooterTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgFooterTemplateDirective, isStandalone: true, selector: "[ng-footer-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgFooterTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-footer-tmp]',
standalone: true,
}]
}] });
class NgNotFoundTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgNotFoundTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgNotFoundTemplateDirective, isStandalone: true, selector: "[ng-notfound-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgNotFoundTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-notfound-tmp]',
standalone: true,
}]
}] });
class NgPlaceholderTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgPlaceholderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgPlaceholderTemplateDirective, isStandalone: true, selector: "[ng-placeholder-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgPlaceholderTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-placeholder-tmp]',
standalone: true,
}]
}] });
class NgTypeToSearchTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgTypeToSearchTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgTypeToSearchTemplateDirective, isStandalone: true, selector: "[ng-typetosearch-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgTypeToSearchTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-typetosearch-tmp]',
standalone: true,
}]
}] });
class NgLoadingTextTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgLoadingTextTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgLoadingTextTemplateDirective, isStandalone: true, selector: "[ng-loadingtext-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgLoadingTextTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-loadingtext-tmp]',
standalone: true,
}]
}] });
class NgTagTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgTagTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgTagTemplateDirective, isStandalone: true, selector: "[ng-tag-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgTagTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-tag-tmp]',
standalone: true,
}]
}] });
class NgLoadingSpinnerTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgLoadingSpinnerTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgLoadingSpinnerTemplateDirective, isStandalone: true, selector: "[ng-loadingspinner-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgLoadingSpinnerTemplateDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ng-loadingspinner-tmp]',
standalone: true,
}]
}] });
// eslint-disable-next-line @angular-eslint/directive-selector
class NgClearButtonTemplateDirective {
constructor() {
this.template = inject((TemplateRef));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgClearButtonTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: NgClearButtonTemplateDirective, isStandalone: true, selector: "[ng-clearbutton-tmp]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgClearButtonTemplateDirective, decorators: [{
type: Directive,
args: [{ selector: '[ng-clearbutton-tmp]', standalone: true }]
}] });
/** Configuration provider for the NgSelect component. You can inject this service and provide application wide configuration. */
class NgSelectConfig {
constructor() {
/** Set placeholder visible even when an item is selected. */
this.fixedPlaceholder = true;
/** Set custom text when filter returns empty result. */
this.notFoundText = 'No items found';
/** Set custom text when using Typeahead. */
this.typeToSearchText = 'Type to search';
/** Set custom text when using tagging. */
this.addTagText = 'Add item';
/** Set custom text when loading items. */
this.loadingText = 'Loading...';
/** Set custom text for clear all icon title. */
this.clearAllText = 'Clear all';
/** Set custom text prefixed to the option label in the aria-label of the remove icon on selected values (multiple mode). */
this.removeText = 'Remove';
/** Set custom aria-label for the dropdown options list. */
this.ariaLabelDropdown = 'Options List';
/** Disable virtual scroll for the dropdown panel. */
this.disableVirtualScroll = true;
/** Open dropdown using enter. */
this.openOnEnter = true;
/** Allows to select dropdown appearance. Set to `outline` or `fill` for Material form-field styles (applies only to Material theme). */
this.appearance = 'underline';
/** Control tab navigation behavior for the clear button. */
this.tabFocusOnClear = true;
/**
* Controls which DOM event is used to detect outside clicks for closing the dropdown.
* Defaults to 'click'. Set to 'mousedown' to handle early outside interactions
* (useful when backdrops load on click and would otherwise close the dropdown).
*/
this.outsideClickEvent = 'click';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectConfig, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectConfig, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ConsoleService {
warn(message) {
console.warn(message);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ConsoleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ConsoleService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ConsoleService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
function newId() {
// First character is an 'a', it's good practice to tag id to begin with a letter
return 'axxxxxxxxxxx'.replace(/[x]/g, () => {
const val = (Math.random() * 16) | 0;
return val.toString(16);
});
}
const diacritics = {
'\u24B6': 'A',
'\uFF21': 'A',
'\u00C0': 'A',
'\u00C1': 'A',
'\u00C2': 'A',
'\u1EA6': 'A',
'\u1EA4': 'A',
'\u1EAA': 'A',
'\u1EA8': 'A',
'\u00C3': 'A',
'\u0100': 'A',
'\u0102': 'A',
'\u1EB0': 'A',
'\u1EAE': 'A',
'\u1EB4': 'A',
'\u1EB2': 'A',
'\u0226': 'A',
'\u01E0': 'A',
'\u00C4': 'A',
'\u01DE': 'A',
'\u1EA2': 'A',
'\u00C5': 'A',
'\u01FA': 'A',
'\u01CD': 'A',
'\u0200': 'A',
'\u0202': 'A',
'\u1EA0': 'A',
'\u1EAC': 'A',
'\u1EB6': 'A',
'\u1E00': 'A',
'\u0104': 'A',
'\u023A': 'A',
'\u2C6F': 'A',
'\uA732': 'AA',
'\u00C6': 'AE',
'\u01FC': 'AE',
'\u01E2': 'AE',
'\uA734': 'AO',
'\uA736': 'AU',
'\uA738': 'AV',
'\uA73A': 'AV',
'\uA73C': 'AY',
'\u24B7': 'B',
'\uFF22': 'B',
'\u1E02': 'B',
'\u1E04': 'B',
'\u1E06': 'B',
'\u0243': 'B',
'\u0182': 'B',
'\u0181': 'B',
'\u24B8': 'C',
'\uFF23': 'C',
'\u0106': 'C',
'\u0108': 'C',
'\u010A': 'C',
'\u010C': 'C',
'\u00C7': 'C',
'\u1E08': 'C',
'\u0187': 'C',
'\u023B': 'C',
'\uA73E': 'C',
'\u24B9': 'D',
'\uFF24': 'D',
'\u1E0A': 'D',
'\u010E': 'D',
'\u1E0C': 'D',
'\u1E10': 'D',
'\u1E12': 'D',
'\u1E0E': 'D',
'\u0110': 'D',
'\u018B': 'D',
'\u018A': 'D',
'\u0189': 'D',
'\uA779': 'D',
'\u01F1': 'DZ',
'\u01C4': 'DZ',
'\u01F2': 'Dz',
'\u01C5': 'Dz',
'\u24BA': 'E',
'\uFF25': 'E',
'\u00C8': 'E',
'\u00C9': 'E',
'\u00CA': 'E',
'\u1EC0': 'E',
'\u1EBE': 'E',
'\u1EC4': 'E',
'\u1EC2': 'E',
'\u1EBC': 'E',
'\u0112': 'E',
'\u1E14': 'E',
'\u1E16': 'E',
'\u0114': 'E',
'\u0116': 'E',
'\u00CB': 'E',
'\u1EBA': 'E',
'\u011A': 'E',
'\u0204': 'E',
'\u0206': 'E',
'\u1EB8': 'E',
'\u1EC6': 'E',
'\u0228': 'E',
'\u1E1C': 'E',
'\u0118': 'E',
'\u1E18': 'E',
'\u1E1A': 'E',
'\u0190': 'E',
'\u018E': 'E',
'\u24BB': 'F',
'\uFF26': 'F',
'\u1E1E': 'F',
'\u0191': 'F',
'\uA77B': 'F',
'\u24BC': 'G',
'\uFF27': 'G',
'\u01F4': 'G',
'\u011C': 'G',
'\u1E20': 'G',
'\u011E': 'G',
'\u0120': 'G',
'\u01E6': 'G',
'\u0122': 'G',
'\u01E4': 'G',
'\u0193': 'G',
'\uA7A0': 'G',
'\uA77D': 'G',
'\uA77E': 'G',
'\u24BD': 'H',
'\uFF28': 'H',
'\u0124': 'H',
'\u1E22': 'H',
'\u1E26': 'H',
'\u021E': 'H',
'\u1E24': 'H',
'\u1E28': 'H',
'\u1E2A': 'H',
'\u0126': 'H',
'\u2C67': 'H',
'\u2C75': 'H',
'\uA78D': 'H',
'\u24BE': 'I',
'\uFF29': 'I',
'\u00CC': 'I',
'\u00CD': 'I',
'\u00CE': 'I',
'\u0128': 'I',
'\u012A': 'I',
'\u012C': 'I',
'\u0130': 'I',
'\u00CF': 'I',
'\u1E2E': 'I',
'\u1EC8': 'I',
'\u01CF': 'I',
'\u0208': 'I',
'\u020A': 'I',
'\u1ECA': 'I',
'\u012E': 'I',
'\u1E2C': 'I',
'\u0197': 'I',
'\u24BF': 'J',
'\uFF2A': 'J',
'\u0134': 'J',
'\u0248': 'J',
'\u24C0': 'K',
'\uFF2B': 'K',
'\u1E30': 'K',
'\u01E8': 'K',
'\u1E32': 'K',
'\u0136': 'K',
'\u1E34': 'K',
'\u0198': 'K',
'\u2C69': 'K',
'\uA740': 'K',
'\uA742': 'K',
'\uA744': 'K',
'\uA7A2': 'K',
'\u24C1': 'L',
'\uFF2C': 'L',
'\u013F': 'L',
'\u0139': 'L',
'\u013D': 'L',
'\u1E36': 'L',
'\u1E38': 'L',
'\u013B': 'L',
'\u1E3C': 'L',
'\u1E3A': 'L',
'\u0141': 'L',
'\u023D': 'L',
'\u2C62': 'L',
'\u2C60': 'L',
'\uA748': 'L',
'\uA746': 'L',
'\uA780': 'L',
'\u01C7': 'LJ',
'\u01C8': 'Lj',
'\u24C2': 'M',
'\uFF2D': 'M',
'\u1E3E': 'M',
'\u1E40': 'M',
'\u1E42': 'M',
'\u2C6E': 'M',
'\u019C': 'M',
'\u24C3': 'N',
'\uFF2E': 'N',
'\u01F8': 'N',
'\u0143': 'N',
'\u00D1': 'N',
'\u1E44': 'N',
'\u0147': 'N',
'\u1E46': 'N',
'\u0145': 'N',
'\u1E4A': 'N',
'\u1E48': 'N',
'\u0220': 'N',
'\u019D': 'N',
'\uA790': 'N',
'\uA7A4': 'N',
'\u01CA': 'NJ',
'\u01CB': 'Nj',
'\u24C4': 'O',
'\uFF2F': 'O',
'\u00D2': 'O',
'\u00D3': 'O',
'\u00D4': 'O',
'\u1ED2': 'O',
'\u1ED0': 'O',
'\u1ED6': 'O',
'\u1ED4': 'O',
'\u00D5': 'O',
'\u1E4C': 'O',
'\u022C': 'O',
'\u1E4E': 'O',
'\u014C': 'O',
'\u1E50': 'O',
'\u1E52': 'O',
'\u014E': 'O',
'\u022E': 'O',
'\u0230': 'O',
'\u00D6': 'O',
'\u022A': 'O',
'\u1ECE': 'O',
'\u0150': 'O',
'\u01D1': 'O',
'\u020C': 'O',
'\u020E': 'O',
'\u01A0': 'O',
'\u1EDC': 'O',
'\u1EDA': 'O',
'\u1EE0': 'O',
'\u1EDE': 'O',
'\u1EE2': 'O',
'\u1ECC': 'O',
'\u1ED8': 'O',
'\u01EA': 'O',
'\u01EC': 'O',
'\u00D8': 'O',
'\u01FE': 'O',
'\u0186': 'O',
'\u019F': 'O',
'\uA74A': 'O',
'\uA74C': 'O',
'\u01A2': 'OI',
'\uA74E': 'OO',
'\u0222': 'OU',
'\u24C5': 'P',
'\uFF30': 'P',
'\u1E54': 'P',
'\u1E56': 'P',
'\u01A4': 'P',
'\u2C63': 'P',
'\uA750': 'P',
'\uA752': 'P',
'\uA754': 'P',
'\u24C6': 'Q',
'\uFF31': 'Q',
'\uA756': 'Q',
'\uA758': 'Q',
'\u024A': 'Q',
'\u24C7': 'R',
'\uFF32': 'R',
'\u0154': 'R',
'\u1E58': 'R',
'\u0158': 'R',
'\u0210': 'R',
'\u0212': 'R',
'\u1E5A': 'R',
'\u1E5C': 'R',
'\u0156': 'R',
'\u1E5E': 'R',
'\u024C': 'R',
'\u2C64': 'R',
'\uA75A': 'R',
'\uA7A6': 'R',
'\uA782': 'R',
'\u24C8': 'S',
'\uFF33': 'S',
'\u1E9E': 'S',
'\u015A': 'S',
'\u1E64': 'S',
'\u015C': 'S',
'\u1E60': 'S',
'\u0160': 'S',
'\u1E66': 'S',
'\u1E62': 'S',
'\u1E68': 'S',
'\u0218': 'S',
'\u015E': 'S',
'\u2C7E': 'S',
'\uA7A8': 'S',
'\uA784': 'S',
'\u24C9': 'T',
'\uFF34': 'T',
'\u1E6A': 'T',
'\u0164': 'T',
'\u1E6C': 'T',
'\u021A': 'T',
'\u0162': 'T',
'\u1E70': 'T',
'\u1E6E': 'T',
'\u0166': 'T',
'\u01AC': 'T',
'\u01AE': 'T',
'\u023E': 'T',
'\uA786': 'T',
'\uA728': 'TZ',
'\u24CA': 'U',
'\uFF35': 'U',
'\u00D9': 'U',
'\u00DA': 'U',
'\u00DB': 'U',
'\u0168': 'U',
'\u1E78': 'U',
'\u016A': 'U',
'\u1E7A': 'U',
'\u016C': 'U',
'\u00DC': 'U',
'\u01DB': 'U',
'\u01D7': 'U',
'\u01D5': 'U',
'\u01D9': 'U',
'\u1EE6': 'U',
'\u016E': 'U',
'\u0170': 'U',
'\u01D3': 'U',
'\u0214': 'U',
'\u0216': 'U',
'\u01AF': 'U',
'\u1EEA': 'U',
'\u1EE8': 'U',
'\u1EEE': 'U',
'\u1EEC': 'U',
'\u1EF0': 'U',
'\u1EE4': 'U',
'\u1E72': 'U',
'\u0172': 'U',
'\u1E76': 'U',
'\u1E74': 'U',
'\u0244': 'U',
'\u24CB': 'V',
'\uFF36': 'V',
'\u1E7C': 'V',
'\u1E7E': 'V',
'\u01B2': 'V',
'\uA75E': 'V',
'\u0245': 'V',
'\uA760': 'VY',
'\u24CC': 'W',
'\uFF37': 'W',
'\u1E80': 'W',
'\u1E82': 'W',
'\u0174': 'W',
'\u1E86': 'W',
'\u1E84': 'W',
'\u1E88': 'W',
'\u2C72': 'W',
'\u24CD': 'X',
'\uFF38': 'X',
'\u1E8A': 'X',
'\u1E8C': 'X',
'\u24CE': 'Y',
'\uFF39': 'Y',
'\u1EF2': 'Y',
'\u00DD': 'Y',
'\u0176': 'Y',
'\u1EF8': 'Y',
'\u0232': 'Y',
'\u1E8E': 'Y',
'\u0178': 'Y',
'\u1EF6': 'Y',
'\u1EF4': 'Y',
'\u01B3': 'Y',
'\u024E': 'Y',
'\u1EFE': 'Y',
'\u24CF': 'Z',
'\uFF3A': 'Z',
'\u0179': 'Z',
'\u1E90': 'Z',
'\u017B': 'Z',
'\u017D': 'Z',
'\u1E92': 'Z',
'\u1E94': 'Z',
'\u01B5': 'Z',
'\u0224': 'Z',
'\u2C7F': 'Z',
'\u2C6B': 'Z',
'\uA762': 'Z',
'\u24D0': 'a',
'\uFF41': 'a',
'\u1E9A': 'a',
'\u00E0': 'a',
'\u00E1': 'a',
'\u00E2': 'a',
'\u1EA7': 'a',
'\u1EA5': 'a',
'\u1EAB': 'a',
'\u1EA9': 'a',
'\u00E3': 'a',
'\u0101': 'a',
'\u0103': 'a',
'\u1EB1': 'a',
'\u1EAF': 'a',
'\u1EB5': 'a',
'\u1EB3': 'a',
'\u0227': 'a',
'\u01E1': 'a',
'\u00E4': 'a',
'\u01DF': 'a',
'\u1EA3': 'a',
'\u00E5': 'a',
'\u01FB': 'a',
'\u01CE': 'a',
'\u0201': 'a',
'\u0203': 'a',
'\u1EA1': 'a',
'\u1EAD': 'a',
'\u1EB7': 'a',
'\u1E01': 'a',
'\u0105': 'a',
'\u2C65': 'a',
'\u0250': 'a',
'\uA733': 'aa',
'\u00E6': 'ae',
'\u01FD': 'ae',
'\u01E3': 'ae',
'\uA735': 'ao',
'\uA737': 'au',
'\uA739': 'av',
'\uA73B': 'av',
'\uA73D': 'ay',
'\u24D1': 'b',
'\uFF42': 'b',
'\u1E03': 'b',
'\u1E05': 'b',
'\u1E07': 'b',
'\u0180': 'b',
'\u0183': 'b',
'\u0253': 'b',
'\u24D2': 'c',
'\uFF43': 'c',
'\u0107': 'c',
'\u0109': 'c',
'\u010B': 'c',
'\u010D': 'c',
'\u00E7': 'c',
'\u1E09': 'c',
'\u0188': 'c',
'\u023C': 'c',
'\uA73F': 'c',
'\u2184': 'c',
'\u24D3': 'd',
'\uFF44': 'd',
'\u1E0B': 'd',
'\u010F': 'd',
'\u1E0D': 'd',
'\u1E11': 'd',
'\u1E13': 'd',
'\u1E0F': 'd',
'\u0111': 'd',
'\u018C': 'd',
'\u0256': 'd',
'\u0257': 'd',
'\uA77A': 'd',
'\u01F3': 'dz',
'\u01C6': 'dz',
'\u24D4': 'e',
'\uFF45': 'e',
'\u00E8': 'e',
'\u00E9': 'e',
'\u00EA': 'e',
'\u1EC1': 'e',
'\u1EBF': 'e',
'\u1EC5': 'e',
'\u1EC3': 'e',
'\u1EBD': 'e',
'\u0113': 'e',
'\u1E15': 'e',
'\u1E17': 'e',
'\u0115': 'e',
'\u0117': 'e',
'\u00EB': 'e',
'\u1EBB': 'e',
'\u011B': 'e',
'\u0205': 'e',
'\u0207': 'e',
'\u1EB9': 'e',
'\u1EC7': 'e',
'\u0229': 'e',
'\u1E1D': 'e',
'\u0119': 'e',
'\u1E19': 'e',
'\u1E1B': 'e',
'\u0247': 'e',
'\u025B': 'e',
'\u01DD': 'e',
'\u24D5': 'f',
'\uFF46': 'f',
'\u1E1F': 'f',
'\u0192': 'f',
'\uA77C': 'f',
'\u24D6': 'g',
'\uFF47': 'g',
'\u01F5': 'g',
'\u011D': 'g',
'\u1E21': 'g',
'\u011F': 'g',
'\u0121': 'g',
'\u01E7': 'g',
'\u0123': 'g',
'\u01E5': 'g',
'\u0260': 'g',
'\uA7A1': 'g',
'\u1D79': 'g',
'\uA77F': 'g',
'\u24D7': 'h',
'\uFF48': 'h',
'\u0125': 'h',
'\u1E23': 'h',
'\u1E27': 'h',
'\u021F': 'h',
'\u1E25': 'h',
'\u1E29': 'h',
'\u1E2B': 'h',
'\u1E96': 'h',
'\u0127': 'h',
'\u2C68': 'h',
'\u2C76': 'h',
'\u0265': 'h',
'\u0195': 'hv',
'\u24D8': 'i',
'\uFF49': 'i',
'\u00EC': 'i',
'\u00ED': 'i',
'\u00EE': 'i',
'\u0129': 'i',
'\u012B': 'i',
'\u012D': 'i',
'\u00EF': 'i',
'\u1E2F': 'i',
'\u1EC9': 'i',
'\u01D0': 'i',
'\u0209': 'i',
'\u020B': 'i',
'\u1ECB': 'i',
'\u012F': 'i',
'\u1E2D': 'i',
'\u0268': 'i',
'\u0131': 'i',
'\u24D9': 'j',
'\uFF4A': 'j',
'\u0135': 'j',
'\u01F0': 'j',
'\u0249': 'j',
'\u24DA': 'k',
'\uFF4B': 'k',
'\u1E31': 'k',
'\u01E9': 'k',
'\u1E33': 'k',
'\u0137': 'k',
'\u1E35': 'k',
'\u0199': 'k',
'\u2C6A': 'k',
'\uA741': 'k',
'\uA743': 'k',
'\uA745': 'k',
'\uA7A3': 'k',
'\u24DB': 'l',
'\uFF4C': 'l',
'\u0140': 'l',
'\u013A': 'l',
'\u013E': 'l',
'\u1E37': 'l',
'\u1E39': 'l',
'\u013C': 'l',
'\u1E3D': 'l',
'\u1E3B': 'l',
'\u017F': 'l',
'\u0142': 'l',
'\u019A': 'l',
'\u026B': 'l',
'\u2C61': 'l',
'\uA749': 'l',
'\uA781': 'l',
'\uA747': 'l',
'\u01C9': 'lj',
'\u24DC': 'm',
'\uFF4D': 'm',
'\u1E3F': 'm',
'\u1E41': 'm',
'\u1E43': 'm',
'\u0271': 'm',
'\u026F': 'm',
'\u24DD': 'n',
'\uFF4E': 'n',
'\u01F9': 'n',
'\u0144': 'n',
'\u00F1': 'n',
'\u1E45': 'n',
'\u0148': 'n',
'\u1E47': 'n',
'\u0146': 'n',
'\u1E4B': 'n',
'\u1E49': 'n',
'\u019E': 'n',
'\u0272': 'n',
'\u0149': 'n',
'\uA791': 'n',
'\uA7A5': 'n',
'\u01CC': 'nj',
'\u24DE': 'o',
'\uFF4F': 'o',
'\u00F2': 'o',
'\u00F3': 'o',
'\u00F4': 'o',
'\u1ED3': 'o',
'\u1ED1': 'o',
'\u1ED7': 'o',
'\u1ED5': 'o',
'\u00F5': 'o',
'\u1E4D': 'o',
'\u022D': 'o',
'\u1E4F': 'o',
'\u014D': 'o',
'\u1E51': 'o',
'\u1E53': 'o',
'\u014F': 'o',
'\u022F': 'o',
'\u0231': 'o',
'\u00F6': 'o',
'\u022B': 'o',
'\u1ECF': 'o',
'\u0151': 'o',
'\u01D2': 'o',
'\u020D': 'o',
'\u020F': 'o',
'\u01A1': 'o',
'\u1EDD': 'o',
'\u1EDB': 'o',
'\u1EE1': 'o',
'\u1EDF': 'o',
'\u1EE3': 'o',
'\u1ECD': 'o',
'\u1ED9': 'o',
'\u01EB': 'o',
'\u01ED': 'o',
'\u00F8': 'o',
'\u01FF': 'o',
'\u0254': 'o',
'\uA74B': 'o',
'\uA74D': 'o',
'\u0275': 'o',
'\u01A3': 'oi',
'\u0223': 'ou',
'\uA74F': 'oo',
'\u24DF': 'p',
'\uFF50': 'p',
'\u1E55': 'p',
'\u1E57': 'p',
'\u01A5': 'p',
'\u1D7D': 'p',
'\uA751': 'p',
'\uA753': 'p',
'\uA755': 'p',
'\u24E0': 'q',
'\uFF51': 'q',
'\u024B': 'q',
'\uA757': 'q',
'\uA759': 'q',
'\u24E1': 'r',
'\uFF52': 'r',
'\u0155': 'r',
'\u1E59': 'r',
'\u0159': 'r',
'\u0211': 'r',
'\u0213': 'r',
'\u1E5B': 'r',
'\u1E5D': 'r',
'\u0157': 'r',
'\u1E5F': 'r',
'\u024D': 'r',
'\u027D': 'r',
'\uA75B': 'r',
'\uA7A7': 'r',
'\uA783': 'r',
'\u24E2': 's',
'\uFF53': 's',
'\u00DF': 's',
'\u015B': 's',
'\u1E65': 's',
'\u015D': 's',
'\u1E61': 's',
'\u0161': 's',
'\u1E67': 's',
'\u1E63': 's',
'\u1E69': 's',
'\u0219': 's',
'\u015F': 's',
'\u023F': 's',
'\uA7A9': 's',
'\uA785': 's',
'\u1E9B': 's',
'\u24E3': 't',
'\uFF54': 't',
'\u1E6B': 't',
'\u1E97': 't',
'\u0165': 't',
'\u1E6D': 't',
'\u021B': 't',
'\u0163': 't',
'\u1E71': 't',
'\u1E6F': 't',
'\u0167': 't',
'\u01AD': 't',
'\u0288': 't',
'\u2C66': 't',
'\uA787': 't',
'\uA729': 'tz',
'\u24E4': 'u',
'\uFF55': 'u',
'\u00F9': 'u',
'\u00FA': 'u',
'\u00FB': 'u',
'\u0169': 'u',
'\u1E79': 'u',
'\u016B': 'u',
'\u1E7B': 'u',
'\u016D': 'u',
'\u00FC': 'u',
'\u01DC': 'u',
'\u01D8': 'u',
'\u01D6': 'u',
'\u01DA': 'u',
'\u1EE7': 'u',
'\u016F': 'u',
'\u0171': 'u',
'\u01D4': 'u',
'\u0215': 'u',
'\u0217': 'u',
'\u01B0': 'u',
'\u1EEB': 'u',
'\u1EE9': 'u',
'\u1EEF': 'u',
'\u1EED': 'u',
'\u1EF1': 'u',
'\u1EE5': 'u',
'\u1E73': 'u',
'\u0173': 'u',
'\u1E77': 'u',
'\u1E75': 'u',
'\u0289': 'u',
'\u24E5': 'v',
'\uFF56': 'v',
'\u1E7D': 'v',
'\u1E7F': 'v',
'\u028B': 'v',
'\uA75F': 'v',
'\u028C': 'v',
'\uA761': 'vy',
'\u24E6': 'w',
'\uFF57': 'w',
'\u1E81': 'w',
'\u1E83': 'w',
'\u0175': 'w',
'\u1E87': 'w',
'\u1E85': 'w',
'\u1E98': 'w',
'\u1E89': 'w',
'\u2C73': 'w',
'\u24E7': 'x',
'\uFF58': 'x',
'\u1E8B': 'x',
'\u1E8D': 'x',
'\u24E8': 'y',
'\uFF59': 'y',
'\u1EF3': 'y',
'\u00FD': 'y',
'\u0177': 'y',
'\u1EF9': 'y',
'\u0233': 'y',
'\u1E8F': 'y',
'\u00FF': 'y',
'\u1EF7': 'y',
'\u1E99': 'y',
'\u1EF5': 'y',
'\u01B4': 'y',
'\u024F': 'y',
'\u1EFF': 'y',
'\u24E9': 'z',
'\uFF5A': 'z',
'\u017A': 'z',
'\u1E91': 'z',
'\u017C': 'z',
'\u017E': 'z',
'\u1E93': 'z',
'\u1E95': 'z',
'\u01B6': 'z',
'\u0225': 'z',
'\u0240': 'z',
'\u2C6C': 'z',
'\uA763': 'z',
'\u0386': '\u0391',
'\u0388': '\u0395',
'\u0389': '\u0397',
'\u038A': '\u0399',
'\u03AA': '\u0399',
'\u038C': '\u039F',
'\u038E': '\u03A5',
'\u03AB': '\u03A5',
'\u038F': '\u03A9',
'\u03AC': '\u03B1',
'\u03AD': '\u03B5',
'\u03AE': '\u03B7',
'\u03AF': '\u03B9',
'\u03CA': '\u03B9',
'\u0390': '\u03B9',
'\u03CC': '\u03BF',
'\u03CD': '\u03C5',
'\u03CB': '\u03C5',
'\u03B0': '\u03C5',
'\u03C9': '\u03C9',
'\u03C2': '\u03C3',
};
function stripSpecialChars(text) {
const match = (a) => diacritics[a] || a;
// eslint-disable-next-line no-control-regex
return text.replace(/[^\u0000-\u007E]/g, match);
}
function isDefined(value) {
return value !== undefined && value !== null;
}
function isObject(value) {
return typeof value === 'object' && isDefined(value);
}
function isPromise(value) {
return value instanceof Promise;
}
function isFunction(value) {
return value instanceof Function;
}
class ItemsList {
constructor(_ngSelect, _selectionModel) {
this._ngSelect = _ngSelect;
this._selectionModel = _selectionModel;
this._items = [];
this._filteredItems = [];
this._markedIndex = -1;
}
get items() {
return this._items;
}
get filteredItems() {
return this._filteredItems;
}
get markedIndex() {
return this._markedIndex;
}
get selectedItems() {
return this._selectionModel.value;
}
get markedItem() {
return this._filteredItems[this._markedIndex];
}
get noItemsToSelect() {
return this._ngSelect.hideSelected() && this._items.length === this.selectedItems.length;
}
get maxItemsSelected() {
return this._ngSelect.multiple() && this._ngSelect.maxSelectedItems() <= this.selectedItems.length;
}
get lastSelectedItem() {
let i = this.selectedItems.length - 1;
for (; i >= 0; i--) {
const item = this.selectedItems[i];
if (!item.disabled) {
return item;
}
}
return null;
}
setItems(items) {
this._items = items.map((item, index) => this.mapItem(item, index));
const groupBy = this._ngSelect.groupBy();
if (groupBy) {
this._groups = this._groupBy(this._items, groupBy);
this._items = this._flatten(this._groups);
}
else {
this._groups = new Map();
this._groups.set(undefined, this._items);
}
this._filteredItems = [...this._items];
}
select(item) {
if (item.selected || this.maxItemsSelected) {
return;
}
const multiple = this._ngSelect.multiple();
if (!multiple) {
this.clearSelected(false);
}
this._selectionModel.select(item, multiple, this._ngSelect.selectableGroupAsModel());
if (this._ngSelect.hideSelected()) {
this._hideSelected(item);
}
}
unselect(item) {
if (!item.selected) {
return;
}
const multiple = this._ngSelect.multiple();
this._selectionModel.unselect(item, multiple);
if (this._ngSelect.hideSelected() && isDefined(item.index) && multiple) {
this._showSelected(item);
}
}
findItem(value) {
let findBy;
if (this._ngSelect.compareWith()) {
findBy = (item) => this._ngSelect.compareWith()(item.value, value);
}
else if (this._ngSelect.bindValue()) {
findBy = (item) => !item.children && this.resolveNested(item.value, this._ngSelect.bindValue()) === value;
}
else {
findBy = (item) => item.value === value || (!item.children && item.label && item.label === this.resolveNested(value, this._ngSelect.bindLabel()));
}
return this._items.find((item) => findBy(item));
}
addItem(item) {
const option = this.mapItem(item, this._items.length);
this._items.push(option);
this._filteredItems.push(option);
return option;
}
clearSelected(keepDisabled) {
this._selectionModel.clear(keepDisabled);
this._items.forEach((item) => {
item.selected = keepDisabled && item.selected && item.disabled;
item.marked = false;
});
if (this._ngSelect.hideSelected()) {
this.resetFilteredItems();
}
}
findByLabel(term) {
term = stripSpecialChars(term).toLocaleLowerCase();
return this.filteredItems.find((item) => {
const label = stripSpecialChars(item.label).toLocaleLowerCase();
return label.substr(0, term.length) === term;
});
}
filter(term) {
if (!term) {
this.resetFilteredItems();
return;
}
this._filteredItems = [];
term = this._ngSelect.searchFn() ? term : stripSpecialChars(term).toLocaleLowerCase();
const match = this._ngSelect.searchFn() || this._defaultSearchFn;
const hideSelected = this._ngSelect.hideSelected();
for (const key of Array.from(this._groups.keys())) {
const matchedItems = [];
for (const item of this._groups.get(key)) {
if (hideSelected && ((item.parent && item.parent.selected) || item.selected)) {
continue;
}
const searchItem = this._ngSelect.searchFn() ? item.value : item;
if (match(term, searchItem)) {
matchedItems.push(item);
}
}
if (matchedItems.length > 0) {
const [last] = matchedItems.slice(-1);
if (last.parent) {
const head = this._items.find((x) => x === last.parent);
this._filteredItems.push(head);
}
this._filteredItems.push(...matchedItems);
}
}
}
resetFilteredItems() {
if (this._filteredItems.length === this._items.length) {
return;
}
if (this._ngSelect.hideSelected() && this.selectedItems.length > 0) {
this._filteredItems = this._items.filter((x) => !x.selected);
}
else {
this._filteredItems = this._items;
}
}
unmarkItem() {
this._markedIndex = -1;
}
markNextItem() {
this._stepToItem(+1);
}
markPreviousItem() {
this._stepToItem(-1);
}
markItem(item) {
this._markedIndex = this._filteredItems.indexOf(item);
}
markSelectedOrDefault(markDefault) {
if (this._filteredItems.length === 0) {
return;
}
const lastMarkedIndex = this._getLastMarkedIndex();
if (lastMarkedIndex > -1) {
this._markedIndex = lastMarkedIndex;
}
else {
this._markedIndex = markDefault ? this.filteredItems.findIndex((x) => !x.disabled) : -1;
}
}
resolveNested(option, key) {
if (!isObject(option)) {
return option;
}
if (key.indexOf('.') === -1) {
return option[key];
}
else {
const keys = key.split('.');
let value = option;
for (let i = 0, len = keys.length; i < len; ++i) {
if (value == null) {
return null;
}
value = value[keys[i]];
}
return value;
}
}
mapItem(item, index) {
const hasNgOptionLabel = isObject(item) && '$ngOptionLabel' in item;
const hasNgOptionValue = isObject(item) && '$ngOptionValue' in item;
const hasNgOptionClasses = isObject(item) && '$ngOptionClasses' in item;
const label = hasNgOptionLabel ? item.$ngOptionLabel : this.resolveNested(item, this._ngSelect.bindLabel());
const value = hasNgOptionValue ? item.$ngOptionValue : item;
return {
index,
label: isDefined(label) ? label.toString() : '',
value,
disabled: item && item.disabled ? item.disabled : false,
classes: hasNgOptionClasses ? item.$ngOptionClasses : '',
htmlId: `${this._ngSelect.dropdownId}-${index}`,
};
}
mapSelectedItems() {
const multiple = this._ngSelect.multiple();
for (const selected of this.selectedItems) {
const bindValue = this._ngSelect.bindValue();
const value = bindValue ? this.resolveNested(selected.value, bindValue) : selected.value;
const valueFound = isDefined(value);
let item = valueFound ? this.findItem(value) : null;
if (!item && !valueFound && this._ngSelect.compareWith()) {
item = this._items.find((item) => this._ngSelect.compareWith()(item.value, selected.value));
}
this._selectionModel.unselect(selected, multiple);
this._selectionModel.select(item || selected, multiple, this._ngSelect.selectableGroupAsModel());
}
if (this._ngSelect.hideSelected()) {
this._filteredItems = this.filteredItems.filter((x) => this.selectedItems.indexOf(x) === -1);
}
}
_showSelected(item) {
this._filteredItems.push(item);
if (item.parent) {
const parent = item.parent;
const parentExists = this._filteredItems.find((x) => x === parent);
if (!parentExists) {
this._filteredItems.push(parent);
}
}
else if (item.children) {
for (const child of item.children) {
child.selected = false;
this._filteredItems.push(child);
}
}
this._filteredItems = [...this._filteredItems.sort((a, b) => a.index - b.index)];
}
_hideSelected(item) {
this._filteredItems = this._filteredItems.filter((x) => x !== item);
if (item.parent) {
const children = item.parent.children;
if (children.every((x) => x.selected)) {
this._filteredItems = this._filteredItems.filter((x) => x !== item.parent);
}
}
else if (item.children) {
this._filteredItems = this.filteredItems.filter((x) => x.parent !== item);
}
}
_defaultSearchFn(search, opt) {
const label = stripSpecialChars(opt.label).toLocaleLowerCase();
return label.indexOf(search) > -1;
}
_getNextItemIndex(steps) {
if (steps > 0) {
return this._markedIndex >= this._filteredItems.length - 1 ? 0 : this._markedIndex + 1;
}
return this._markedIndex <= 0 ? this._filteredItems.length - 1 : this._markedIndex - 1;
}
_stepToItem(steps) {
if (this._filteredItems.length === 0 || this._filteredItems.every((x) => x.disabled)) {
return;
}
this._markedIndex = this._getNextItemIndex(steps);
if (this.markedItem.disabled) {
this._stepToItem(steps);
}
}
_getLastMarkedIndex() {
if (this._ngSelect.hideSelected()) {
return -1;
}
if (this._markedIndex > -1 && this.markedItem === undefined) {
return -1;
}
const selectedIndex = this._getFirstSelectedIndex();
if (this.lastSelectedItem && selectedIndex < 0) {
return -1;
}
return selectedIndex > -1 ? selectedIndex : this.markedIndex;
}
/**
* Index of the first selected, non-disabled option in filtered list order.
* Per the WAI-ARIA listbox pattern, focus lands on the first selected option when the list opens.
*/
_getFirstSelectedIndex() {
let index = -1;
for (const selected of this.selectedItems) {
if (selected.disabled) {
continue;
}
const i = this._filteredItems.indexOf(selected);
if (i > -1 && (index === -1 || i < index)) {
index = i;
}
}
return index;
}
_groupBy(items, prop) {
const groups = new Map();
if (items.length === 0) {
return groups;
}
// Check if items are already grouped by given key.
const firstValue = items[0].value;
if (firstValue != null && Array.isArray(firstValue[prop])) {
for (const item of items) {
const children = (item.value?.[prop] || []).map((x, index) => this.mapItem(x, index));
groups.set(item, children);
}
return groups;
}
const isFnKey = isFunction(this._ngSelect.groupBy());
const keyFn = (item) => {
const key = isFnKey ? prop(item.value) : item.value?.[prop];
return isDefined(key) ? key : undefined;
};
// Group items by key.
for (const item of items) {
const key = keyFn(item);
const group = groups.get(key);
if (group) {
group.push(item);
}
else {
groups.set(key, [item]);
}
}
return groups;
}
_flatten(groups) {
const isGroupByFn = isFunction(this._ngSelect.groupBy());
const items = [];
for (const key of Array.from(groups.keys())) {
let i = items.length;
if (key === undefined) {
const withoutGroup = groups.get(undefined) || [];
items.push(...withoutGroup.map((x) => {
x.index = i++;
x.htmlId = `${this._ngSelect.dropdownId}-${x.index}`;
return x;
}));
continue;
}
const isObjectKey = isObject(key);
const parent = {
label: isObjectKey ? '' : String(key),
children: undefined,
parent: null,
index: i++,
disabled: !this._ngSelect.selectableGroup(),
htmlId: newId(),
};
const groupKey = isGroupByFn ? this._ngSelect.bindLabel() : this._ngSelect.groupBy();
const groupValue = this._ngSelect.groupValue() ||
(() => {
if (isObjectKey) {
return key.value;
}
return { [groupKey]: key };
});
const children = groups.get(key).map((x) => {
x.parent = parent;
x.children = undefined;
x.index = i++;
x.htmlId = `${this._ngSelect.dropdownId}-${x.index}`;
return x;
});
parent.children = children;
parent.value = groupValue(key, children.map((x) => x.value));
items.push(parent);
items.push(...children);
}
return items;
}
}
class NgDropdownPanelService {
constructor() {
this._dimensions = {
itemHeight: 0,
groupHeight: 0,
panelHeight: 0,
itemsPerViewport: 0,
};
}
get dimensions() {
return this._dimensions;
}
calculateItems(scrollPos, itemsLength, buffer, items) {
const d = this._dimensions;
const useVariableHeights = !!items && d.groupHeight > 0 && d.groupHeight !== d.itemHeight;
if (useVariableHeights) {
return this._calculateItemsVariable(scrollPos, items, buffer);
}
const scrollHeight = d.itemHeight * itemsLength;
const scrollTop = Math.max(0, scrollPos);
const indexByScrollTop = (scrollTop / scrollHeight) * itemsLength;
let end = Math.min(itemsLength, Math.ceil(indexByScrollTop) + (d.itemsPerViewport + 1));
const maxStartEnd = end;
const maxStart = Math.max(0, maxStartEnd - d.itemsPerViewport);
let start = Math.min(maxStart, Math.floor(indexByScrollTop));
let topPadding = d.itemHeight * Math.ceil(start) - d.itemHeight * Math.min(start, buffer);
topPadding = !isNaN(topPadding) ? topPadding : 0;
start = !isNaN(start) ? start : -1;
end = !isNaN(end) ? end : -1;
start -= buffer;
start = Math.max(0, start);
end += buffer;
end = Math.min(itemsLength, end);
return {
topPadding,
scrollHeight,
start,
end,
};
}
setDimensions(itemHeight, panelHeight, groupHeight = itemHeight) {
const effectiveItemHeight = itemHeight > 0 ? itemHeight : groupHeight;
const itemsPerViewport = effectiveItemHeight > 0 ? Math.max(1, Math.floor(panelHeight / effectiveItemHeight)) : 0;
this._dimensions = {
itemHeight,
groupHeight: groupHeight > 0 ? groupHeight : itemHeight,
panelHeight,
itemsPerViewport,
};
}
getItemHeight(item) {
const d = this._dimensions;
if (item?.children) {
return d.groupHeight || d.itemHeight;
}
return d.itemHeight || d.groupHeight;
}
getItemOffset(items, index) {
let offset = 0;
const end = Math.max(0, Math.min(index, items.length));
for (let i = 0; i < end; i++) {
offset += this.getItemHeight(items[i]);
}
return offset;
}
getScrollHeight(items) {
return this.getItemOffset(items, items.length);
}
getScrollTo(itemTop, itemHeight, lastScroll) {
const { panelHeight } = this.dimensions;
const itemBottom = itemTop + itemHeight;
const top = lastScroll;
const bottom = top + panelHeight;
if (panelHeight >= itemBottom && lastScroll === itemTop) {
return null;
}
if (itemBottom > bottom) {
return top + itemBottom - bottom;
}
else if (itemTop <= top) {
return itemTop;
}
return null;
}
_calculateItemsVariable(scrollPos, items, buffer) {
const { panelHeight } = this._dimensions;
const itemsLength = items.length;
const scrollHeight = this.getScrollHeight(items);
const scrollTop = Math.max(0, scrollPos);
let accumulated = 0;
let start = 0;
for (let i = 0; i < itemsLength; i++) {
const height = this.getItemHeight(items[i]);
if (accumulated + height > scrollTop) {
start = i;
break;
}
accumulated += height;
start = i + 1;
}
let end = start;
let visibleHeight = 0;
while (end < itemsLength && visibleHeight < panelHeight) {
visibleHeight += this.getItemHeight(items[end]);
end++;
}
// Match the uniform path's +1 viewport slack so the last partially-visible row is included.
end = Math.min(itemsLength, end + 1);
const bufferedStart = Math.max(0, start - buffer);
const bufferedEnd = Math.min(itemsLength, end + buffer);
const topPadding = this.getItemOffset(items, bufferedStart);
return {
topPadding,
scrollHeight,
start: bufferedStart,
end: bufferedEnd,
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgDropdownPanelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgDropdownPanelService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgDropdownPanelService, decorators: [{
type: Injectable
}] });
const CSS_POSITIONS = ['top', 'right', 'bottom', 'left'];
const SCROLL_SCHEDULER = typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler;
class NgDropdownPanelComponent {
constructor() {
this.items = input([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
this.showAddTag = input(false, { ...(ngDevMode ? { debugName: "showAddTag" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
this.markedItem = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "markedItem" }] : /* istanbul ignore next */ []));
this.position = input('auto', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
this.bufferAmount = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "bufferAmount" }] : /* istanbul ignore next */ []));
this.virtualScroll = input(false, { ...(ngDevMode ? { debugName: "virtualScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
this.headerTemplate = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "headerTemplate" }] : /* istanbul ignore next */ []));
this.footerTemplate = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "footerTemplate" }] : /* istanbul ignore next */ []));
this.filterValue = input(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "filterValue" }] : /* istanbul ignore next */ []));
this.ariaLabelDropdown = input(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ariaLabelDropdown" }] : /* istanbul ignore next */ []));
this.listboxId = input(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "listboxId" }] : /* istanbul ignore next */ []));
/**
* Which DOM event to listen to for outside click detection
*/
this.outsideClickEvent = input('click', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "outsideClickEvent" }] : /* istanbul ignore next */ []));
/** @deprecated Has no effect: the CDK overlay renders in the native Popover API top layer automatically in supporting browsers. Will be removed in a future major version. */
this.popover = input(false, { ...(ngDevMode ? { debugName: "popover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Overlay hosting this panel. Used to request repositioning when the rendered content changes. */
this.overlayRef = input(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "overlayRef" }] : /* istanbul ignore next */ []));
/** Host `ng-select` element. The panel's DOM lives in the overlay, so the host cannot be derived from the DOM tree. */
this.selectElement = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectElement" }] : /* istanbul ignore next */ []));
this.update = output();
this.scroll = output();
this.scrollToEnd = output();
this.outsideClick = output();
this._renderer = inject(Renderer2);
this._zone = inject(NgZone);
this._panelService = inject(NgDropdownPanelService);
this._document = inject(DOCUMENT, { optional: true });
this._destroyRef = inject(DestroyRef);
this._dropdown = inject((ElementRef)).nativeElement;
this.contentElementRef = viewChild('content', { ...(ngDevMode ? { debugName: "contentElementRef" } : /* istanbul ignore next */ {}), read: ElementRef });
this.scrollElementRef = viewChild('scroll', { ...(ngDevMode ? { debugName: "scrollElementRef" } : /* istanbul ignore next */ {}), read: ElementRef });
this.paddingElementRef = viewChild('padding', { ...(ngDevMode ? { debugName: "paddingElementRef" } : /* istanbul ignore next */ {}), read: ElementRef });
this._virtualPadding = computed(() => this.paddingElementRef()?.nativeElement, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_virtualPadding" }] : /* istanbul ignore next */ []));
this._scrollablePanel = computed(() => this.scrollElementRef()?.nativeElement, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_scrollablePanel" }] : /* istanbul ignore next */ []));
this._contentPanel = computed(() => this.contentElementRef()?.nativeElement, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_contentPanel" }] : /* istanbul ignore next */ []));
this._scrollToEndFired = false;
this._updateScrollHeight = false;
this._lastScrollPosition = 0;
this._lastMousedownInside = false;
}
get currentPosition() {
return this._currentPosition;
}
get itemsLength() {
return this._itemsLength;
}
set itemsLength(value) {
if (value !== this._itemsLength) {
this._itemsLength = value;
this._onItemsLengthChanged();
}
}
get _startOffset() {
if (this.markedItem()) {
const { panelHeight } = this._panelService.dimensions;
const offset = this._panelService.getItemOffset(this.items(), this.markedItem().index);
return panelHeight > offset ? 0 : offset;
}
return 0;
}
ngOnInit() {
this._select = this.selectElement() ?? this._dropdown.parentElement;
this._handleScroll();
this._handleOutsideClick();
this._setupMousedownListener();
this._subscribeOverlayPosition();
this._handleDocumentScroll();
this._handleSelectResize();
}
ngOnChanges(changes) {
if (changes.items) {
const change = changes.items;
this._onItemsOrShowAddTagChange(change.currentValue, this.showAddTag(), change.firstChange);
}
if (changes.showAddTag) {
const change = changes.showAddTag;
this._onItemsOrShowAddTagChange(this.items(), change.currentValue, change.firstChange);
}
}
scrollTo(option, startFromOption = false) {
if (!option) {
return;
}
const index = this.items().indexOf(option);
if (index < 0 || index >= this.itemsLength) {
return;
}
let scrollTo;
if (this.virtualScroll()) {
const items = this.items();
const itemHeight = this._panelService.getItemHeight(option);
const itemTop = this._panelService.getItemOffset(items, index);
scrollTo = this._panelService.getScrollTo(itemTop, itemHeight, this._lastScrollPosition);
}
else {
const item = this._dropdown.querySelector(`#${option.htmlId}`);
if (!item) {
return;
}
const lastScroll = startFromOption ? item.offsetTop : this._lastScrollPosition;
scrollTo = this._panelService.getScrollTo(item.offsetTop, item.clientHeight, lastScroll);
}
if (isDefined(scrollTo)) {
this._scrollablePanel().scrollTop = scrollTo;
// Programmatic scrollTop does not always deliver a `scroll` event before the next
// keyboard step (and animationFrame-audited listeners can lag). Keep the virtual
// window in sync immediately so the marked row is rendered (#2744).
if (this.virtualScroll()) {
this._onContentScrolled(scrollTo);
}
else {
this._lastScrollPosition = scrollTo;
}
}
}
scrollToTag() {
const panel = this._scrollablePanel();
panel.scrollTop = panel.scrollHeight - panel.clientHeight;
}
adjustPosition() {
this.overlayRef()?.updatePosition();
}
/**
* Applies the position chosen by the overlay's position strategy to the panel and host
* as `ng-select-top` / `ng-select-bottom` (and `-left` / `-right`) classes, which the
* shipped themes use for borders, radius and spacing.
*/
_setCurrentPosition(position) {
this._currentPosition = position;
if (CSS_POSITIONS.includes(position)) {
this._updateDropdownClass(position);
}
else {
this._updateDropdownClass('bottom');
}
}
/**
* Positions the freshly rendered panel. The overlay measures the real DOM, so header and
* footer templates and the actual item count are part of the auto placement decision
* (#2575). Only then is the panel made visible to avoid a flash at a stale position.
*/
_positionDropdown() {
const overlayRef = this.overlayRef();
if (overlayRef) {
// Applies the best fitting position and synchronously emits positionChanges,
// which updates the position classes before the panel becomes visible
overlayRef.updatePosition();
}
else if (!isDefined(this._currentPosition)) {
// Standalone usage without an overlay: reflect the configured side directly
this._setCurrentPosition(this.position() === 'auto' ? 'bottom' : this.position());
}
this._dropdown.style.opacity = '1';
}
_subscribeOverlayPosition() {
const strategy = this.overlayRef()?.getConfig().positionStrategy;
if (!(strategy instanceof FlexibleConnectedPositionStrategy)) {
return;
}
strategy.positionChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((change) => {
this._setCurrentPosition(this._connectionPairToPosition(change.connectionPair));
});
}
_connectionPairToPosition(pair) {
if (pair.originY === 'bottom' && pair.overlayY === 'top') {
return 'bottom';
}
if (pair.originY === 'top' && pair.overlayY === 'bottom') {
return 'top';
}
if (pair.originX === 'end' && pair.overlayX === 'start') {
return 'right';
}
if (pair.originX === 'start' && pair.overlayX === 'end') {
return 'left';
}
return 'bottom';
}
_updateDropdownClass(currentPosition) {
CSS_POSITIONS.forEach((position) => {
const REMOVE_CSS_CLASS = `ng-select-${position}`;
this._renderer.removeClass(this._dropdown, REMOVE_CSS_CLASS);
this._renderer.removeClass(this._select, REMOVE_CSS_CLASS);
});
const ADD_CSS_CLASS = `ng-select-${currentPosition}`;
this._renderer.addClass(this._dropdown, ADD_CSS_CLASS);
this._renderer.addClass(this._select, ADD_CSS_CLASS);
}
_handleScroll() {
this._zone.runOutsideAngular(() => {
const scrollablePanel = this._scrollablePanel();
if (!scrollablePanel) {
return;
}
fromEvent(scrollablePanel, 'scroll')
.pipe(takeUntilDestroyed(this._destroyRef), auditTime(0, SCROLL_SCHEDULER))
.subscribe(() => {
this._onContentScrolled(scrollablePanel.scrollTop);
});
});
}
_handleOutsideClick() {
if (!this._document) {
return;
}
const outsideEvent = this.outsideClickEvent() ?? 'click';
this._zone.runOutsideAngular(() => {
// A click is judged by where the press began, not where it ended. Between
// mousedown and click the layout can move under the cursor — focusing the
// select auto-scrolls it into view (#2773), or the freshly opened panel
// shifts the page (#2441) — leaving the click target on an unrelated
// element. Irrelevant when closing on mousedown itself, so skip the
// listener in that mode
if (outsideEvent === 'click') {
fromEvent(this._document, 'mousedown', { capture: true })
.pipe(takeUntilDestroyed(this._destroyRef))
.subscribe(($event) => (this._lastMousedownInside = this._isEventInside($event)));
}
fromEvent(this._document, outsideEvent, { capture: true })
.pipe(takeUntilDestroyed(this._destroyRef))
.subscribe(($event) => this._checkToClose($event));
});
}
_checkToClose($event) {
const pressStartedInside = this._lastMousedownInside;
this._lastMousedownInside = false;
if (pressStartedInside || this._isEventInside($event)) {
return;
}
this._zone.run(() => this.outsideClick.emit());
}
_isEventInside($event) {
// An event crossing a shadow boundary is retargeted to the shadow host, which
// hides its real origin from contains(). composedPath() still lists every node
// the event bubbled through, so matching the select or dropdown anywhere along
// it works identically for light DOM, shadow DOM, and nested roots (#2726)
const path = $event.path || ($event.composedPath && $event.composedPath());
if (path?.length) {
return path.includes(this._select) || path.includes(this._dropdown);
}
return this._select.contains($event.target) || this._dropdown.contains($event.target);
}
_onItemsOrShowAddTagChange(items = [], showAddTag, firstChange) {
this._scrollToEndFired = false;
this.itemsLength = items.length;
if (showAddTag && items.length) {
this.itemsLength++;
}
if (this.virtualScroll()) {
this._updateItemsRange(firstChange);
}
else {
this._setVirtualHeight();
this._updateItems(firstChange);
}
}
_updateItems(firstChange) {
this.update.emit(this.items());
this._zone.runOutsideAngular(() => {
Promise.resolve().then(() => {
// Typeahead/filter can open empty (panelHeight ≈ type-to-search) then replace
// items while open. Refresh the scrollport height so keyboard scrollTo math
// matches the real max-height panel — without changing scroll position (#2744).
this._syncPanelHeightFromDom();
if (!firstChange) {
// Re-anchor so a top-placed panel grows upward instead of covering the
// select, and `auto` can flip once the content no longer fits (#2092)
this.overlayRef()?.updatePosition();
return;
}
this._positionDropdown();
this.scrollTo(this.markedItem(), firstChange);
});
});
}
_updateItemsRange(firstChange) {
this._zone.runOutsideAngular(() => {
this._measureDimensions().then(() => {
const scrollTop = firstChange ? this._startOffset : (this._scrollablePanel()?.scrollTop ?? 0);
this._renderItemsRange(scrollTop);
// Measurement / a short prior list leaves the panel content-sized. After the
// provisional range paints, sync max-height panelHeight for scrollTo (#2744).
// If viewport capacity grows, re-render — otherwise DOM stays on the stale
// (too-small) range while dimensions already reflect the larger panel.
const itemsPerViewportBefore = this._panelService.dimensions.itemsPerViewport;
this._syncPanelHeightFromDom();
if (this._panelService.dimensions.itemsPerViewport !== itemsPerViewportBefore) {
this._lastScrollPosition = -1;
this._renderItemsRange(this._scrollablePanel()?.scrollTop ?? scrollTop);
}
if (firstChange) {
this._positionDropdown();
}
else {
this.overlayRef()?.updatePosition();
}
});
});
}
/** Updates cached panelHeight from the live scrollport, preserving row-height measurements. */
_syncPanelHeightFromDom() {
const panel = this._scrollablePanel();
if (!panel) {
return;
}
const panelHeight = panel.clientHeight;
if (panelHeight <= 0) {
return;
}
const { itemHeight, groupHeight } = this._panelService.dimensions;
this._panelService.setDimensions(itemHeight, panelHeight, groupHeight);
}
_onContentScrolled(scrollTop) {
if (this.virtualScroll()) {
this._renderItemsRange(scrollTop);
}
this._lastScrollPosition = scrollTop;
this._fireScrollToEnd(scrollTop);
}
_updateVirtualHeight(height) {
if (this._updateScrollHeight) {
this._virtualPadding().style.height = `${height}px`;
this._updateScrollHeight = false;
}
}
_setVirtualHeight() {
if (!this._virtualPadding()) {
return;
}
this._virtualPadding().style.height = `0px`;
}
_onItemsLengthChanged() {
this._updateScrollHeight = true;
}
_renderItemsRange(scrollTop = null) {
if (scrollTop && this._lastScrollPosition === scrollTop) {
return;
}
scrollTop = scrollTop || this._scrollablePanel().scrollTop;
const range = this._panelService.calculateItems(scrollTop, this.itemsLength, this.bufferAmount(), this.items());
this._updateVirtualHeight(range.scrollHeight);
this._contentPanel().style.transform = `translateY(${range.topPadding}px)`;
// These outputs must stay template-bound in ng-select.component.html: the
// template listener wrapper is what schedules CD under zoneless (zone.run is a
// no-op there). A programmatic subscribe would need an explicit markForCheck
this._zone.run(() => {
this.update.emit(this.items().slice(range.start, range.end));
this.scroll.emit({ start: range.start, end: range.end });
});
if (isDefined(scrollTop) && this._lastScrollPosition === 0) {
this._scrollablePanel().scrollTop = scrollTop;
this._lastScrollPosition = scrollTop;
}
}
_measureDimensions() {
if (this._panelService.dimensions.itemHeight > 0 || this.itemsLength === 0) {
return Promise.resolve(this._panelService.dimensions);
}
const items = this.items();
const firstGroup = items.find((item) => !!item.children);
const firstOption = items.find((item) => !item.children);
const toMeasure = [firstGroup, firstOption].filter((item) => !!item);
if (toMeasure.length === 0) {
return Promise.resolve(this._panelService.dimensions);
}
// Relies on synchronous template execution: the emitted items render in the
// same CD pass (the parent's template listener runs mid-pass and the @for sits
// later in the template), so the microtask below measures real DOM with and
// without zone.js alike. Measuring both a group header and an option avoids
// under-sizing virtual scroll when their templates have different heights (#2762).
this._zone.run(() => this.update.emit(toMeasure));
return Promise.resolve()
.then(() => this._readMeasuredDimensions(items, firstOption, firstGroup))
.then((dims) => {
if (dims.itemHeight > 0) {
return dims;
}
// Empty→items (typeahead) can measure before the projected options paint.
// Retry once on the next frame (#2744).
return new Promise((resolve) => {
requestAnimationFrame(() => {
this._zone.run(() => this.update.emit(toMeasure));
Promise.resolve().then(() => resolve(this._readMeasuredDimensions(items, firstOption, firstGroup)));
});
});
});
}
_readMeasuredDimensions(items, firstOption, firstGroup) {
const optionEl = firstOption ? this._dropdown.querySelector(`#${firstOption.htmlId}`) : null;
const groupEl = firstGroup ? this._dropdown.querySelector(`#${firstGroup.htmlId}`) : null;
if (!optionEl && !groupEl) {
return this._panelService.dimensions;
}
const measuredOptionHeight = optionEl?.offsetHeight ?? 0;
const measuredGroupHeight = groupEl?.offsetHeight ?? 0;
const optionHeight = measuredOptionHeight || measuredGroupHeight;
const groupHeight = measuredGroupHeight || measuredOptionHeight;
const panelHeight = this._scrollablePanel().clientHeight;
this._panelService.setDimensions(optionHeight, panelHeight, groupHeight);
this._virtualPadding().style.height = `${this._panelService.getScrollHeight(items)}px`;
return this._panelService.dimensions;
}
_fireScrollToEnd(scrollTop) {
if (this._scrollToEndFired || scrollTop === 0) {
return;
}
const padding = this.virtualScroll() ? this._virtualPadding() : this._contentPanel();
if (scrollTop + this._dropdown.clientHeight >= padding.clientHeight - 1) {
this._zone.run(() => this.scrollToEnd.emit());
this._scrollToEndFired = true;
}
}
_setupMousedownListener() {
this._zone.runOutsideAngular(() => {
fromEvent(this._dropdown, 'mousedown')
.pipe(takeUntilDestroyed(this._destroyRef))
.subscribe((event) => {
const target = event.target;
if (target.tagName === 'INPUT') {
return;
}
event.preventDefault();
});
});
}
_handleDocumentScroll() {
if (!this._document || !this.overlayRef()) {
return;
}
this._zone.runOutsideAngular(() => {
// The capture phase sees scrolls of the window and of every ancestor scroll
// container — including plain overflow elements that CDK's ScrollDispatcher
// cannot observe without a cdkScrollable marker — so the panel stays anchored
// to the select wherever the page moves (#2788, #2829)
fromEvent(this._document, 'scroll', { capture: true, passive: true })
.pipe(takeUntilDestroyed(this._destroyRef), auditTime(0, SCROLL_SCHEDULER))
.subscribe(($event) => {
const target = $event.target;
// Scrolling the option list itself does not move the anchor
if (target && this._dropdown.contains(target)) {
return;
}
this.overlayRef().updatePosition();
});
});
}
_handleSelectResize() {
const overlayRef = this.overlayRef();
if (!overlayRef || !this._select || typeof ResizeObserver === 'undefined') {
return;
}
this._zone.runOutsideAngular(() => {
const observer = new ResizeObserver(() => {
overlayRef.updateSize({ width: this._select.getBoundingClientRect().width });
overlayRef.updatePosition();
});
observer.observe(this._select);
this._destroyRef.onDestroy(() => observer.disconnect());
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgDropdownPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: NgDropdownPanelComponent, isStandalone: true, selector: "ng-dropdown-panel", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, showAddTag: { classPropertyName: "showAddTag", publicName: "showAddTag", isSignal: true, isRequired: false, transformFunction: null }, markedItem: { classPropertyName: "markedItem", publicName: "markedItem", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, bufferAmount: { classPropertyName: "bufferAmount", publicName: "bufferAmount", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, headerTemplate: { classPropertyName: "headerTemplate", publicName: "headerTemplate", isSignal: true, isRequired: false, transformFunction: null }, footerTemplate: { classPropertyName: "footerTemplate", publicName: "footerTemplate", isSignal: true, isRequired: false, transformFunction: null }, filterValue: { classPropertyName: "filterValue", publicName: "filterValue", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelDropdown: { classPropertyName: "ariaLabelDropdown", publicName: "ariaLabelDropdown", isSignal: true, isRequired: false, transformFunction: null }, listboxId: { classPropertyName: "listboxId", publicName: "listboxId", isSignal: true, isRequired: false, transformFunction: null }, outsideClickEvent: { classPropertyName: "outsideClickEvent", publicName: "outsideClickEvent", isSignal: true, isRequired: false, transformFunction: null }, popover: { classPropertyName: "popover", publicName: "popover", isSignal: true, isRequired: false, transformFunction: null }, overlayRef: { classPropertyName: "overlayRef", publicName: "overlayRef", isSignal: true, isRequired: false, transformFunction: null }, selectElement: { classPropertyName: "selectElement", publicName: "selectElement", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { update: "update", scroll: "scroll", scrollToEnd: "scrollToEnd", outsideClick: "outsideClick" }, viewQueries: [{ propertyName: "contentElementRef", first: true, predicate: ["content"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "scrollElementRef", first: true, predicate: ["scroll"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "paddingElementRef", first: true, predicate: ["padding"], descendants: true, read: ElementRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
@if (headerTemplate()) {
<div class="ng-dropdown-header">
<ng-container [ngTemplateOutlet]="headerTemplate()" [ngTemplateOutletContext]="{ searchTerm: filterValue() }" />
</div>
}
<div #scroll role="listbox" class="ng-dropdown-panel-items scroll-host" [attr.id]="listboxId()" [attr.aria-label]="ariaLabelDropdown()">
<div #padding [class.total-padding]="virtualScroll()"></div>
<div #content [class.scrollable-content]="virtualScroll() && items().length">
<ng-content />
</div>
</div>
@if (footerTemplate()) {
<div class="ng-dropdown-footer">
<ng-container [ngTemplateOutlet]="footerTemplate()" [ngTemplateOutletContext]="{ searchTerm: filterValue() }" />
</div>
}
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgDropdownPanelComponent, decorators: [{
type: Component,
args: [{
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
selector: 'ng-dropdown-panel',
template: `
@if (headerTemplate()) {
<div class="ng-dropdown-header">
<ng-container [ngTemplateOutlet]="headerTemplate()" [ngTemplateOutletContext]="{ searchTerm: filterValue() }" />
</div>
}
<div #scroll role="listbox" class="ng-dropdown-panel-items scroll-host" [attr.id]="listboxId()" [attr.aria-label]="ariaLabelDropdown()">
<div #padding [class.total-padding]="virtualScroll()"></div>
<div #content [class.scrollable-content]="virtualScroll() && items().length">
<ng-content />
</div>
</div>
@if (footerTemplate()) {
<div class="ng-dropdown-footer">
<ng-container [ngTemplateOutlet]="footerTemplate()" [ngTemplateOutletContext]="{ searchTerm: filterValue() }" />
</div>
}
`,
imports: [NgTemplateOutlet],
}]
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], showAddTag: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAddTag", required: false }] }], markedItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "markedItem", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], bufferAmount: [{ type: i0.Input, args: [{ isSignal: true, alias: "bufferAmount", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], headerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTemplate", required: false }] }], footerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerTemplate", required: false }] }], filterValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterValue", required: false }] }], ariaLabelDropdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelDropdown", required: false }] }], listboxId: [{ type: i0.Input, args: [{ isSignal: true, alias: "listboxId", required: false }] }], outsideClickEvent: [{ type: i0.Input, args: [{ isSignal: true, alias: "outsideClickEvent", required: false }] }], popover: [{ type: i0.Input, args: [{ isSignal: true, alias: "popover", required: false }] }], overlayRef: [{ type: i0.Input, args: [{ isSignal: true, alias: "overlayRef", required: false }] }], selectElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectElement", required: false }] }], update: [{ type: i0.Output, args: ["update"] }], scroll: [{ type: i0.Output, args: ["scroll"] }], scrollToEnd: [{ type: i0.Output, args: ["scrollToEnd"] }], outsideClick: [{ type: i0.Output, args: ["outsideClick"] }], contentElementRef: [{ type: i0.ViewChild, args: ['content', { ...{ read: ElementRef }, isSignal: true }] }], scrollElementRef: [{ type: i0.ViewChild, args: ['scroll', { ...{ read: ElementRef }, isSignal: true }] }], paddingElementRef: [{ type: i0.ViewChild, args: ['padding', { ...{ read: ElementRef }, isSignal: true }] }] } });
/** Declares an option for ng-select declared via HTML. Use the value input to bind the option's value and the element content as its label. */
class NgOptionComponent {
constructor() {
/** Value bound to the option. The projected element content is used as the option's label. */
this.value = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
/** Whether the option is disabled and cannot be selected. */
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
this.elementRef = inject((ElementRef));
this.label = signal('', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
this.classes = signal('', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
/** True when this component's inputs are initialized (after first change detection). */
this.isInitialized = signal(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isInitialized" }] : /* istanbul ignore next */ []));
afterEveryRender(() => {
const element = this.elementRef.nativeElement;
// textContent (not innerHTML): default labels are plain text, like Material viewValue.
const currentLabel = (element.textContent || '').trim();
if (currentLabel !== this.label()) {
this.label.set(currentLabel);
}
const currentClasses = Array.from(element.classList)
.filter((className) => className !== 'ng-star-inserted')
.join(' ');
if (currentClasses !== this.classes()) {
this.classes.set(currentClasses);
}
});
}
ngOnInit() {
this.isInitialized.set(true);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgOptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: NgOptionComponent, isStandalone: true, selector: "ng-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgOptionComponent, decorators: [{
type: Component,
args: [{
selector: 'ng-option',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-content />`,
}]
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
var KeyCode;
(function (KeyCode) {
KeyCode["Tab"] = "Tab";
KeyCode["Enter"] = "Enter";
KeyCode["Esc"] = "Escape";
KeyCode["Space"] = " ";
KeyCode["ArrowUp"] = "ArrowUp";
KeyCode["ArrowDown"] = "ArrowDown";
KeyCode["Backspace"] = "Backspace";
})(KeyCode || (KeyCode = {}));
function DefaultSelectionModelFactory() {
return new DefaultSelectionModel();
}
class DefaultSelectionModel {
constructor() {
this._selected = [];
}
get value() {
return this._selected;
}
select(item, multiple, groupAsModel) {
item.selected = true;
if (!item.children || (!multiple && groupAsModel)) {
this._selected.push(item);
}
if (multiple) {
if (item.parent) {
const childrenCount = item.parent.children.length;
const selectedCount = item.parent.children.filter((x) => x.selected).length;
item.parent.selected = childrenCount === selectedCount;
}
else if (item.children) {
this._setChildrenSelectedState(item.children, true);
this._removeChildren(item);
if (groupAsModel && this._activeChildren(item)) {
this._selected = [...this._selected.filter((x) => x.parent !== item), item];
}
else {
this._selected = [...this._selected, ...item.children.filter((x) => !x.disabled)];
}
}
}
}
unselect(item, multiple) {
this._selected = this._selected.filter((x) => x !== item);
item.selected = false;
if (multiple) {
if (item.parent && item.parent.selected) {
const children = item.parent.children;
this._removeParent(item.parent);
this._removeChildren(item.parent);
this._selected.push(...children.filter((x) => x !== item && !x.disabled));
item.parent.selected = false;
}
else if (item.children) {
this._setChildrenSelectedState(item.children, false);
this._removeChildren(item);
}
}
}
clear(keepDisabled) {
this._selected = keepDisabled ? this._selected.filter((x) => x.disabled) : [];
}
_setChildrenSelectedState(children, selected) {
for (const child of children) {
if (child.disabled) {
continue;
}
child.selected = selected;
}
}
_removeChildren(parent) {
this._selected = [...this._selected.filter((x) => x.parent !== parent), ...parent.children.filter((x) => x.parent === parent && x.disabled && x.selected)];
}
_removeParent(parent) {
this._selected = this._selected.filter((x) => x !== parent);
}
_activeChildren(item) {
return item.children.every((x) => !x.disabled || x.selected);
}
}
/** DI token for SelectionModel implementation. You can provide custom implementation changing selection behaviour. */
const SELECTION_MODEL_FACTORY = new InjectionToken('ng-select-selection-model');
function optionalBooleanAttribute(value) {
return value == null ? undefined : booleanAttribute(value);
}
const DROPDOWN_POSITION_BELOW = { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' };
const DROPDOWN_POSITION_ABOVE = { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' };
const DROPDOWN_POSITION_AFTER = { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' };
const DROPDOWN_POSITION_BEFORE = { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top' };
const DROPDOWN_POSITIONS = {
// 'auto' prefers below and flips above when the panel does not fit the viewport;
// the fit check measures the rendered overlay, so header/footer templates and
// item count are accounted for (#2687, #2575)
auto: [DROPDOWN_POSITION_BELOW, DROPDOWN_POSITION_ABOVE],
bottom: [DROPDOWN_POSITION_BELOW],
top: [DROPDOWN_POSITION_ABOVE],
right: [DROPDOWN_POSITION_AFTER],
left: [DROPDOWN_POSITION_BEFORE],
};
/**
* Overlay container created inside the `appendTo` host. Only used by browsers without the
* Popover API (where the popover insertion point cannot place the overlay host): keeping the
* container a descendant of the `appendTo` host preserves ancestor-scoped styles and focus
* containment there too. The container element carries `position: fixed`, so the connected
* position strategy's viewport coordinates stay valid, same as the default body container.
*/
class NgSelectAppendToOverlayContainer extends OverlayContainer {
constructor(_appendToHost) {
super();
this._appendToHost = _appendToHost;
}
_createContainer() {
super._createContainer();
this._appendToHost.appendChild(this._containerElement);
}
}
class NgSelectComponent {
constructor() {
this.classes = inject(new HostAttributeToken('class'), { optional: true });
this.config = inject(NgSelectConfig);
// signals
this._disabled = signal(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
// inputs: underscored input() + alias + linkedSignal() for stable public names (back compat)
this._ariaLabelDropdown = input(undefined, { ...(ngDevMode ? { debugName: "_ariaLabelDropdown" } : /* istanbul ignore next */ {}), alias: 'ariaLabelDropdown' });
this.ariaLabelDropdown = linkedSignal(() => this._ariaLabelDropdown(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ariaLabelDropdown" }] : /* istanbul ignore next */ []));
this._ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "_ariaLabel" } : /* istanbul ignore next */ {}), alias: 'ariaLabel' });
this.ariaLabel = linkedSignal(() => this._ariaLabel(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
/** Marks first item as focused when opening/filtering. */
this._markFirst = input(true, { ...(ngDevMode ? { debugName: "_markFirst" } : /* istanbul ignore next */ {}), alias: 'markFirst', transform: booleanAttribute });
this.markFirst = linkedSignal(() => this._markFirst(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "markFirst" }] : /* istanbul ignore next */ []));
/** Placeholder text. */
this._placeholder = input(this.config.placeholder, { ...(ngDevMode ? { debugName: "_placeholder" } : /* istanbul ignore next */ {}), alias: 'placeholder' });
this.placeholder = linkedSignal(() => this._placeholder(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
/** Set placeholder visible even when an item is selected */
this._fixedPlaceholder = input(this.config.fixedPlaceholder, { ...(ngDevMode ? { debugName: "_fixedPlaceholder" } : /* istanbul ignore next */ {}), alias: 'fixedPlaceholder' });
this.fixedPlaceholder = linkedSignal(() => this._fixedPlaceholder(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fixedPlaceholder" }] : /* istanbul ignore next */ []));
/** Set custom text when filter returns empty result */
this._notFoundText = input(undefined, { ...(ngDevMode ? { debugName: "_notFoundText" } : /* istanbul ignore next */ {}), alias: 'notFoundText' });
this.notFoundText = linkedSignal(() => this._notFoundText(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "notFoundText" }] : /* istanbul ignore next */ []));
/** Set custom text when using Typeahead */
this._typeToSearchText = input(undefined, { ...(ngDevMode ? { debugName: "_typeToSearchText" } : /* istanbul ignore next */ {}), alias: 'typeToSearchText' });
this.typeToSearchText = linkedSignal(() => this._typeToSearchText(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "typeToSearchText" }] : /* istanbul ignore next */ []));
/** Prevent opening of ng-select on right mouse click */
this._preventToggleOnRightClick = input(false, { ...(ngDevMode ? { debugName: "_preventToggleOnRightClick" } : /* istanbul ignore next */ {}), alias: 'preventToggleOnRightClick' });
this.preventToggleOnRightClick = linkedSignal(() => this._preventToggleOnRightClick(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "preventToggleOnRightClick" }] : /* istanbul ignore next */ []));
/** Set custom text when using tagging */
this._addTagText = input(undefined, { ...(ngDevMode ? { debugName: "_addTagText" } : /* istanbul ignore next */ {}), alias: 'addTagText' });
this.addTagText = linkedSignal(() => this._addTagText(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "addTagText" }] : /* istanbul ignore next */ []));
/** Set custom text when for loading items */
this._loadingText = input(undefined, { ...(ngDevMode ? { debugName: "_loadingText" } : /* istanbul ignore next */ {}), alias: 'loadingText' });
this.loadingText = linkedSignal(() => this._loadingText(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
/** Set custom text for clear all icon title */
this._clearAllText = input(undefined, { ...(ngDevMode ? { debugName: "_clearAllText" } : /* istanbul ignore next */ {}), alias: 'clearAllText' });
this.clearAllText = linkedSignal(() => this._clearAllText(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearAllText" }] : /* istanbul ignore next */ []));
/** Set custom text prefixed to the option label in the aria-label of the remove icon on selected values (multiple mode) */
this._removeText = input(undefined, { ...(ngDevMode ? { debugName: "_removeText" } : /* istanbul ignore next */ {}), alias: 'removeText' });
this.removeText = linkedSignal(() => this._removeText(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "removeText" }] : /* istanbul ignore next */ []));
/** Set the dropdown position on open */
this._dropdownPosition = input('auto', { ...(ngDevMode ? { debugName: "_dropdownPosition" } : /* istanbul ignore next */ {}), alias: 'dropdownPosition' });
this.dropdownPosition = linkedSignal(() => this._dropdownPosition(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dropdownPosition" }] : /* istanbul ignore next */ []));
/**
* Append the dropdown overlay to any element using a css selector, resolved against the
* select's own root (document or shadow root). The panel keeps its viewport-based
* positioning and top-layer painting; the target controls where the overlay lives in the
* DOM — ancestor-scoped styles, stacking context and focus containment. Defaults to the
* document body.
*/
this._appendTo = input(undefined, { ...(ngDevMode ? { debugName: "_appendTo" } : /* istanbul ignore next */ {}), alias: 'appendTo' });
this.appendTo = linkedSignal(() => this._appendTo(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "appendTo" }] : /* istanbul ignore next */ []));
/** Configure which DOM event type is used for outside click detection. Use `'mousedown'` to fix issues with backdrop/loading overlays that appear on dropdown open */
this._outsideClickEvent = input(this.config.outsideClickEvent ?? 'click', { ...(ngDevMode ? { debugName: "_outsideClickEvent" } : /* istanbul ignore next */ {}), alias: 'outsideClickEvent' });
this.outsideClickEvent = linkedSignal(() => this._outsideClickEvent(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "outsideClickEvent" }] : /* istanbul ignore next */ []));
/** You can set the loading state from the outside (e.g. async items loading) */
this._loading = input(false, { ...(ngDevMode ? { debugName: "_loading" } : /* istanbul ignore next */ {}), alias: 'loading', transform: booleanAttribute });
this.loading = linkedSignal(() => this._loading(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
/** Whether to close the menu when a value is selected */
this._closeOnSelect = input(true, { ...(ngDevMode ? { debugName: "_closeOnSelect" } : /* istanbul ignore next */ {}), alias: 'closeOnSelect', transform: booleanAttribute });
this.closeOnSelect = linkedSignal(() => this._closeOnSelect(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "closeOnSelect" }] : /* istanbul ignore next */ []));
/** Allows to hide selected items. */
this._hideSelected = input(false, { ...(ngDevMode ? { debugName: "_hideSelected" } : /* istanbul ignore next */ {}), alias: 'hideSelected', transform: booleanAttribute });
this.hideSelected = linkedSignal(() => this._hideSelected(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hideSelected" }] : /* istanbul ignore next */ []));
/** Select marked dropdown item using tab. Default `false` */
this._selectOnTab = input(false, { ...(ngDevMode ? { debugName: "_selectOnTab" } : /* istanbul ignore next */ {}), alias: 'selectOnTab', transform: booleanAttribute });
this.selectOnTab = linkedSignal(() => this._selectOnTab(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectOnTab" }] : /* istanbul ignore next */ []));
/** Open dropdown using enter. Default `true` */
this._openOnEnter = input(undefined, { ...(ngDevMode ? { debugName: "_openOnEnter" } : /* istanbul ignore next */ {}), alias: 'openOnEnter', transform: booleanAttribute });
this.openOnEnter = linkedSignal(() => this._openOnEnter(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "openOnEnter" }] : /* istanbul ignore next */ []));
/** When multiple = true, allows to set a limit number of selection. */
this._maxSelectedItems = input(undefined, { ...(ngDevMode ? { debugName: "_maxSelectedItems" } : /* istanbul ignore next */ {}), alias: 'maxSelectedItems', transform: numberAttribute });
this.maxSelectedItems = linkedSignal(() => this._maxSelectedItems(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "maxSelectedItems" }] : /* istanbul ignore next */ []));
/** Allow to group items by key or function expression */
this._groupBy = input(undefined, { ...(ngDevMode ? { debugName: "_groupBy" } : /* istanbul ignore next */ {}), alias: 'groupBy' });
this.groupBy = linkedSignal(() => this._groupBy(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
/** Function expression to provide group value */
this._groupValue = input(undefined, { ...(ngDevMode ? { debugName: "_groupValue" } : /* istanbul ignore next */ {}), alias: 'groupValue' });
this.groupValue = linkedSignal(() => this._groupValue(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "groupValue" }] : /* istanbul ignore next */ []));
/** Used in virtual scrolling, the `bufferAmount` property controls the number of items preloaded in the background to ensure smoother and more seamless scrolling. */
this._bufferAmount = input(4, { ...(ngDevMode ? { debugName: "_bufferAmount" } : /* istanbul ignore next */ {}), alias: 'bufferAmount', transform: numberAttribute });
this.bufferAmount = linkedSignal(() => this._bufferAmount(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "bufferAmount" }] : /* istanbul ignore next */ []));
/** Enable virtual scroll for better performance when rendering a lot of data */
this._virtualScroll = input(undefined, { ...(ngDevMode ? { debugName: "_virtualScroll" } : /* istanbul ignore next */ {}), alias: 'virtualScroll',
transform: optionalBooleanAttribute });
this.virtualScroll = linkedSignal(() => this._virtualScroll(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "virtualScroll" }] : /* istanbul ignore next */ []));
this.dropdownVirtualScroll = computed(() => {
const value = this._virtualScroll();
return isDefined(value) ? value : this.isVirtualScrollDisabled(this.config);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dropdownVirtualScroll" }] : /* istanbul ignore next */ []));
/** Allow to select group when groupBy is used */
this._selectableGroup = input(false, { ...(ngDevMode ? { debugName: "_selectableGroup" } : /* istanbul ignore next */ {}), alias: 'selectableGroup', transform: booleanAttribute });
this.selectableGroup = linkedSignal(() => this._selectableGroup(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectableGroup" }] : /* istanbul ignore next */ []));
/** Control tab navigation behavior for the clear button. Default `true` */
this._tabFocusOnClearButton = input(undefined, { ...(ngDevMode ? { debugName: "_tabFocusOnClearButton" } : /* istanbul ignore next */ {}), alias: 'tabFocusOnClearButton' });
this.tabFocusOnClearButton = linkedSignal(() => this._tabFocusOnClearButton(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "tabFocusOnClearButton" }] : /* istanbul ignore next */ []));
/** Indicates whether to select all children or group itself */
this._selectableGroupAsModel = input(true, { ...(ngDevMode ? { debugName: "_selectableGroupAsModel" } : /* istanbul ignore next */ {}), alias: 'selectableGroupAsModel', transform: booleanAttribute });
this.selectableGroupAsModel = linkedSignal(() => this._selectableGroupAsModel(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectableGroupAsModel" }] : /* istanbul ignore next */ []));
/** Allow to filter by custom search function */
this._searchFn = input(null, { ...(ngDevMode ? { debugName: "_searchFn" } : /* istanbul ignore next */ {}), alias: 'searchFn' });
this.searchFn = linkedSignal(() => this._searchFn(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "searchFn" }] : /* istanbul ignore next */ []));
/** Provide custom trackBy function */
this._trackByFn = input(null, { ...(ngDevMode ? { debugName: "_trackByFn" } : /* istanbul ignore next */ {}), alias: 'trackByFn' });
this.trackByFn = linkedSignal(() => this._trackByFn(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "trackByFn" }] : /* istanbul ignore next */ []));
/** Clear selected values one by one when clicking backspace. Default `true` */
this._clearOnBackspace = input(true, { ...(ngDevMode ? { debugName: "_clearOnBackspace" } : /* istanbul ignore next */ {}), alias: 'clearOnBackspace', transform: booleanAttribute });
this.clearOnBackspace = linkedSignal(() => this._clearOnBackspace(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearOnBackspace" }] : /* istanbul ignore next */ []));
/** Id to associate control with label. */
this._labelForId = input(null, { ...(ngDevMode ? { debugName: "_labelForId" } : /* istanbul ignore next */ {}), alias: 'labelForId' });
this.labelForId = linkedSignal(() => this._labelForId(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "labelForId" }] : /* istanbul ignore next */ []));
/** Pass custom attributes to underlying `input` element */
this._inputAttrs = input({}, { ...(ngDevMode ? { debugName: "_inputAttrs" } : /* istanbul ignore next */ {}), alias: 'inputAttrs' });
this.inputAttrs = linkedSignal(() => this._inputAttrs(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "inputAttrs" }] : /* istanbul ignore next */ []));
/** Set tabindex on ng-select */
this._tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "_tabIndex" } : /* istanbul ignore next */ {}), alias: 'tabIndex', transform: numberAttribute });
this.tabIndex = linkedSignal(() => this._tabIndex(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
/** Set ng-select as readonly. Mostly used with reactive forms. */
this._readonly = input(false, { ...(ngDevMode ? { debugName: "_readonly" } : /* istanbul ignore next */ {}), alias: 'readonly', transform: booleanAttribute });
this.readonly = linkedSignal(() => this._readonly(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
/** Whether items should be filtered while composition started */
this._searchWhileComposing = input(true, { ...(ngDevMode ? { debugName: "_searchWhileComposing" } : /* istanbul ignore next */ {}), alias: 'searchWhileComposing', transform: booleanAttribute });
this.searchWhileComposing = linkedSignal(() => this._searchWhileComposing(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "searchWhileComposing" }] : /* istanbul ignore next */ []));
/** Minimum term length to start a search. Should be used with `typeahead` */
this._minTermLength = input(0, { ...(ngDevMode ? { debugName: "_minTermLength" } : /* istanbul ignore next */ {}), alias: 'minTermLength', transform: numberAttribute });
this.minTermLength = linkedSignal(() => this._minTermLength(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "minTermLength" }] : /* istanbul ignore next */ []));
/** Allow to edit search query if option selected. Default `false`. Works only if multiple is `false`. */
this._editableSearchTerm = input(false, { ...(ngDevMode ? { debugName: "_editableSearchTerm" } : /* istanbul ignore next */ {}), alias: 'editableSearchTerm', transform: booleanAttribute });
this.editableSearchTerm = linkedSignal(() => this._editableSearchTerm(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "editableSearchTerm" }] : /* istanbul ignore next */ []));
this._ngClass = input(null, { ...(ngDevMode ? { debugName: "_ngClass" } : /* istanbul ignore next */ {}), alias: 'ngClass' });
this.ngClass = linkedSignal(() => this._ngClass(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ngClass" }] : /* istanbul ignore next */ []));
/** Custom autocomplete or advanced filter. */
this._typeahead = input(undefined, { ...(ngDevMode ? { debugName: "_typeahead" } : /* istanbul ignore next */ {}), alias: 'typeahead' });
this.typeahead = linkedSignal(() => this._typeahead(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "typeahead" }] : /* istanbul ignore next */ []));
/** Allows to select multiple items. */
this._multiple = input(false, { ...(ngDevMode ? { debugName: "_multiple" } : /* istanbul ignore next */ {}), alias: 'multiple', transform: booleanAttribute });
this.multiple = linkedSignal(() => this._multiple(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
/** Allows to create custom options. */
this._addTag = input(false, { ...(ngDevMode ? { debugName: "_addTag" } : /* istanbul ignore next */ {}), alias: 'addTag' });
this.addTag = linkedSignal(() => this._addTag(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "addTag" }] : /* istanbul ignore next */ []));
/** Allow to search for value. Default `true` */
this._searchable = input(true, { ...(ngDevMode ? { debugName: "_searchable" } : /* istanbul ignore next */ {}), alias: 'searchable', transform: booleanAttribute });
this.searchable = linkedSignal(() => this._searchable(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "searchable" }] : /* istanbul ignore next */ []));
/** Allow to clear selected value. Default `true` */
this._clearable = input(true, { ...(ngDevMode ? { debugName: "_clearable" } : /* istanbul ignore next */ {}), alias: 'clearable', transform: booleanAttribute });
this.clearable = linkedSignal(() => this._clearable(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearable" }] : /* istanbul ignore next */ []));
this._clearKeepsDisabledOptions = input(true, { ...(ngDevMode ? { debugName: "_clearKeepsDisabledOptions" } : /* istanbul ignore next */ {}), alias: 'clearKeepsDisabledOptions', transform: booleanAttribute });
this.clearKeepsDisabledOptions = linkedSignal(() => this._clearKeepsDisabledOptions(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearKeepsDisabledOptions" }] : /* istanbul ignore next */ []));
/** Deselects a selected item when it is clicked in the dropdown. Default `false`. Default `true` when **multiple** is `true` */
this._deselectOnClick = input(undefined, { ...(ngDevMode ? { debugName: "_deselectOnClick" } : /* istanbul ignore next */ {}), alias: 'deselectOnClick' });
this.deselectOnClick = linkedSignal(() => this._deselectOnClick(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "deselectOnClick" }] : /* istanbul ignore next */ []));
/** Clears search input when item is selected. Default `true`. Default `false` when **closeOnSelect** is `false` */
this._clearSearchOnAdd = input(undefined, { ...(ngDevMode ? { debugName: "_clearSearchOnAdd" } : /* istanbul ignore next */ {}), alias: 'clearSearchOnAdd' });
this.clearSearchOnAdd = linkedSignal(() => this._clearSearchOnAdd(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearSearchOnAdd" }] : /* istanbul ignore next */ []));
/** A function to compare the option values with the selected values. The first argument is a value from an option. The second is a value from the selection(model). A boolean should be returned. */
this._compareWith = input(undefined, { ...(ngDevMode ? { debugName: "_compareWith" } : /* istanbul ignore next */ {}), alias: 'compareWith',
transform: (fn) => {
if (fn !== undefined && fn !== null && !isFunction(fn)) {
throw Error('`compareWith` must be a function.');
}
return fn;
} });
this.compareWith = linkedSignal(() => this._compareWith(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "compareWith" }] : /* istanbul ignore next */ []));
/** Provide custom keyDown function. Executed before default handler. Return false to suppress execution of default key down handlers */
this._keyDownFn = input((_) => true, { ...(ngDevMode ? { debugName: "_keyDownFn" } : /* istanbul ignore next */ {}), alias: 'keyDownFn' });
this.keyDownFn = linkedSignal(() => this._keyDownFn(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "keyDownFn" }] : /* istanbul ignore next */ []));
/** @deprecated Has no effect: the CDK overlay renders in the native Popover API top layer automatically in supporting browsers. Will be removed in a future major version. */
this._popover = input(false, { ...(ngDevMode ? { debugName: "_popover" } : /* istanbul ignore next */ {}), alias: 'popover', transform: booleanAttribute });
/** @deprecated Has no effect: the CDK overlay renders in the native Popover API top layer automatically in supporting browsers. Will be removed in a future major version. */
this.popover = linkedSignal(() => this._popover(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "popover" }] : /* istanbul ignore next */ []));
// models
/** Object property to use for label. Default `label` */
this.bindLabel = model(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "bindLabel" }] : /* istanbul ignore next */ []));
/** Object property to use for selected model. By default binds to whole object. */
this.bindValue = model(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "bindValue" }] : /* istanbul ignore next */ []));
/** Allows to select dropdown appearance. Set to `outline` or `fill` for Material form-field styles (applies only to Material theme) */
this.appearance = model(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
/** Allows manual control of dropdown opening and closing. `true` - won't close. `false` - won't open. */
this.isOpen = model(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
/** Items array */
this.items = model([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
// output events
/** Fired on select blur */
this.blurEvent = output({ alias: 'blur' });
/** Fired on select focus */
this.focusEvent = output({ alias: 'focus' });
/** Fired on model change. Outputs whole model */
this.changeEvent = output({ alias: 'change' });
/** Fired on select dropdown open */
this.openEvent = output({ alias: 'open' });
/** Fired on select dropdown close */
this.closeEvent = output({ alias: 'close' });
/** Fired while typing search term. Outputs search term with filtered items */
this.searchEvent = output({ alias: 'search' });
/** Fired on clear icon click */
this.clearEvent = output({ alias: 'clear' });
/** Fired when item is added while `[multiple]="true"`. Outputs added item */
this.addEvent = output({ alias: 'add' });
/** Fired when item is removed while `[multiple]="true"` */
this.removeEvent = output({ alias: 'remove' });
/** Fired when scrolled (only when `[virtualScroll]="true"`). Provides the start and end index of the currently available items. Can be used for loading more items in chunks before the user has scrolled all the way to the bottom of the list. */
this.scroll = output({ alias: 'scroll' });
/** Fired when scrolled to the end of items. Can be used for loading more items in chunks. */
this.scrollToEnd = output({ alias: 'scrollToEnd' });
// computed
this.disabled = computed(() => this.readonly() || this._disabled(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
this.clearSearchOnAddValue = computed(() => {
if (isDefined(this.clearSearchOnAdd())) {
return this.clearSearchOnAdd();
}
if (isDefined(this.config.clearSearchOnAdd)) {
return this.config.clearSearchOnAdd;
}
return this.closeOnSelect();
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearSearchOnAddValue" }] : /* istanbul ignore next */ []));
this.deselectOnClickValue = computed(() => {
if (isDefined(this.deselectOnClick())) {
return this.deselectOnClick();
}
if (isDefined(this.config.deselectOnClick)) {
return this.config.deselectOnClick;
}
return this.multiple();
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "deselectOnClickValue" }] : /* istanbul ignore next */ []));
// content child queries
this.optionTemplate = contentChild(NgOptionTemplateDirective, { ...(ngDevMode ? { debugName: "optionTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.optgroupTemplate = contentChild(NgOptgroupTemplateDirective, { ...(ngDevMode ? { debugName: "optgroupTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.labelTemplate = contentChild(NgLabelTemplateDirective, { ...(ngDevMode ? { debugName: "labelTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.multiLabelTemplate = contentChild(NgMultiLabelTemplateDirective, { ...(ngDevMode ? { debugName: "multiLabelTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.headerTemplate = contentChild(NgHeaderTemplateDirective, { ...(ngDevMode ? { debugName: "headerTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.footerTemplate = contentChild(NgFooterTemplateDirective, { ...(ngDevMode ? { debugName: "footerTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.notFoundTemplate = contentChild(NgNotFoundTemplateDirective, { ...(ngDevMode ? { debugName: "notFoundTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.placeholderTemplate = contentChild(NgPlaceholderTemplateDirective, { ...(ngDevMode ? { debugName: "placeholderTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.typeToSearchTemplate = contentChild(NgTypeToSearchTemplateDirective, { ...(ngDevMode ? { debugName: "typeToSearchTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.loadingTextTemplate = contentChild(NgLoadingTextTemplateDirective, { ...(ngDevMode ? { debugName: "loadingTextTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.tagTemplate = contentChild(NgTagTemplateDirective, { ...(ngDevMode ? { debugName: "tagTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.loadingSpinnerTemplate = contentChild(NgLoadingSpinnerTemplateDirective, { ...(ngDevMode ? { debugName: "loadingSpinnerTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.clearButtonTemplate = contentChild(NgClearButtonTemplateDirective, { ...(ngDevMode ? { debugName: "clearButtonTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
this.ngOptions = contentChildren(NgOptionComponent, { ...(ngDevMode ? { debugName: "ngOptions" } : /* istanbul ignore next */ {}), descendants: true });
// view children queries
this.dropdownPanel = viewChild(forwardRef(() => NgDropdownPanelComponent), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dropdownPanel" }] : /* istanbul ignore next */ []));
this.searchInput = viewChild('searchInput', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
this.clearButton = viewChild('clearButton', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "clearButton" }] : /* istanbul ignore next */ []));
this._dropdownTemplate = viewChild('dropdownTemplate', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_dropdownTemplate" }] : /* istanbul ignore next */ []));
// The portal's embedded view is anchored at the template's own container so it stays
// part of this component's logical view tree (DI, view queries, detectChanges) while
// its DOM nodes live in the CDK overlay
this._dropdownOutlet = viewChild('dropdownTemplate', { ...(ngDevMode ? { debugName: "_dropdownOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
this._selectContainer = viewChild('selectContainer', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_selectContainer" }] : /* istanbul ignore next */ []));
// public variables
this.dropdownId = newId();
/** Width of the notched-outline gap for the floated label in the material outline appearance */
this.outlineNotchWidth = signal(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "outlineNotchWidth" }] : /* istanbul ignore next */ []));
this.viewPortItems = [];
this.tabFocusOnClear = signal(true, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "tabFocusOnClear" }] : /* istanbul ignore next */ []));
this._cd = inject(ChangeDetectorRef);
this._console = inject(ConsoleService);
this._destroyRef = inject(DestroyRef);
this.autoFocus = inject(new HostAttributeToken('autofocus'), { optional: true });
/** Overlay hosting the dropdown panel while open. Bound into the panel so it can request repositioning. */
this.dropdownOverlayRef = null;
// private variables
this._defaultLabel = 'label';
this._editableSearchTermActive = computed(() => this.editableSearchTerm() && !this.multiple(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_editableSearchTermActive" }] : /* istanbul ignore next */ []));
this._document = inject(DOCUMENT);
this._dropdownPositionStrategy = null;
this._dropdownPortal = null;
/** `appendTo` value the current overlay was created against; a change rebuilds the overlay. */
this._overlayAppendTo = null;
/** Overlay container placed inside the `appendTo` host for browsers without the Popover API. */
this._appendToContainer = null;
this._injector = inject(Injector);
this._isComposing = false;
this._keyPress$ = new Subject();
this._pressedKeys = [];
this._searchTerm = signal(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_searchTerm" }] : /* istanbul ignore next */ []));
this._validTerm = computed(() => {
const term = this._searchTerm()?.trim();
return term && term.length >= this.minTermLength();
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_validTerm" }] : /* istanbul ignore next */ []));
this.clearItem = (item) => {
const option = this.selectedItems.find((x) => x.value === item);
this.unselect(option);
};
this.trackByOption = (_, item) => {
if (this.trackByFn()) {
return this.trackByFn()(item.value);
}
return item;
};
this._onChange = (_) => { };
this._onTouched = () => { };
const config = this.config;
const newSelectionModel = inject(SELECTION_MODEL_FACTORY, { optional: true });
const _elementRef = inject(ElementRef);
this._mergeGlobalConfig(config);
this.itemsList = new ItemsList(this, newSelectionModel ? newSelectionModel() : DefaultSelectionModelFactory());
this.element = _elementRef.nativeElement;
this._handleSignalChanges();
afterEveryRender({
read: () => this._measureOutlineNotch(),
});
this._destroyRef.onDestroy(() => this._destroyDropdownOverlay());
}
/**
* Measures the placeholder label so the notched outline can leave a real gap in the border
* for the floated label instead of masking it with an opaque background (material theme).
* The 0.75 factor matches the `scale(0.75)` the material theme applies to the floated label.
*/
_measureOutlineNotch() {
if (this.appearance() !== 'outline') {
return;
}
const label = this.element.querySelector('.ng-select-container > .ng-value-container .ng-placeholder');
const width = label ? label.offsetWidth * 0.75 : 0;
if (width !== this.outlineNotchWidth()) {
this.outlineNotchWidth.set(width);
}
}
get focused() {
return this._focused;
}
get filtered() {
return (!!this.searchTerm && this.searchable()) || this._isComposing;
}
get searchTerm() {
return this._searchTerm();
}
get selectedItems() {
return this.itemsList.selectedItems;
}
get selectedValues() {
return this.selectedItems.map((x) => x.value);
}
get hasValue() {
return this.selectedItems.length > 0;
}
get currentPanelPosition() {
if (this.dropdownPanel()) {
return this.dropdownPanel().currentPosition;
}
return undefined;
}
get showAddTag() {
if (!this._validTerm()) {
return false;
}
const term = this.searchTerm.toLowerCase().trim();
return (this.addTag() &&
!this.itemsList.filteredItems.some((x) => x.label.toLowerCase() === term) &&
((!this.hideSelected() && this.isOpen()) || !this.selectedItems.some((x) => x.label.toLowerCase() === term)) &&
!this.loading());
}
ngOnInit() {
this._handleKeyPresses();
this._setInputAttributes();
this._warnDeprecatedInputs();
}
ngOnChanges(changes) {
const multipleChange = changes._multiple ?? changes.multiple;
const itemsChange = changes.items;
const isOpenChange = changes.isOpen;
const groupByChange = changes._groupBy ?? changes.groupBy;
if (multipleChange?.firstChange) {
this.itemsList.clearSelected(false);
}
if (itemsChange?.firstChange) {
this._itemsAreUsed = true;
this._setItems(itemsChange.currentValue || []);
}
if (isOpenChange) {
this._manualOpen = isDefined(isOpenChange.currentValue);
}
if (groupByChange?.firstChange && !itemsChange) {
this._setItems([...this.items()]);
}
this._setTabFocusOnClear();
}
ngAfterViewInit() {
if (!this._itemsAreUsed) {
this._setItemsFromNgOptions();
}
if (isDefined(this.autoFocus)) {
this.focus();
}
}
handleKeyDown($event) {
const keyName = $event.key;
if (Object.values(KeyCode).includes(keyName)) {
if (this.keyDownFn()($event) === false) {
return;
}
this.handleKeyCode($event);
}
else if (keyName && keyName.length === 1) {
this._keyPress$.next(keyName.toLocaleLowerCase());
}
}
handleKeyCode($event) {
const target = $event.target;
if (this.clearButton() && this.clearButton().nativeElement === target) {
this.handleKeyCodeClear($event);
}
else {
this.handleKeyCodeInput($event);
}
}
handleKeyCodeInput($event) {
switch ($event.key) {
case KeyCode.ArrowDown:
this._handleArrowDown($event);
break;
case KeyCode.ArrowUp:
this._handleArrowUp($event);
break;
case KeyCode.Space:
this._handleSpace($event);
break;
case KeyCode.Enter:
this._handleEnter($event);
break;
case KeyCode.Tab:
this._handleTab($event);
break;
case KeyCode.Esc:
// Only consume Escape when it closes the dropdown so parent overlays/dialogs can still handle Escape when already closed
if (!this.isOpen() || this._manualOpen) {
return;
}
this.close();
$event.preventDefault();
break;
case KeyCode.Backspace:
this._handleBackspace();
break;
}
}
handleKeyCodeClear($event) {
switch ($event.key) {
case KeyCode.Enter:
case KeyCode.Space:
this.handleClearClick();
$event.preventDefault();
break;
}
}
handleRemoveKeydown($event, item) {
if (item.disabled) {
return;
}
if ($event.key === KeyCode.Enter || $event.key === KeyCode.Space) {
$event.preventDefault();
$event.stopPropagation();
this.unselect(item);
}
}
handleMousedown($event) {
if (this.disabled()) {
return;
}
if (this.preventToggleOnRightClick() && $event.button === 2) {
return false;
}
const target = $event.target;
// Allow highlighting/copying the selected label when search is disabled.
// Skipping preventDefault + toggle lets the browser start a text selection.
// See https://github.com/ng-select/ng-select/issues/2669
const onSelectedValue = !!target.closest?.('.ng-value');
const onValueIcon = target.classList.contains('ng-value-icon') || !!target.closest?.('.ng-value-icon');
if (!this.searchable() && onSelectedValue && !onValueIcon) {
if (!this._focused) {
this.focus();
}
return;
}
// Skip during Angular event replay (SSR hydration): preventDefault has no effect
// after browser dispatch and throws — see https://github.com/ng-select/ng-select/issues/2549
if (target.tagName !== 'INPUT' && $event.eventPhase !== EventPhase.REPLAY) {
$event.preventDefault();
}
if (target.classList.contains('ng-clear-wrapper')) {
// Don't handle clear on mousedown - let click event handle it
return;
}
if (target.classList.contains('ng-arrow-wrapper')) {
this.handleArrowClick();
return;
}
if (target.classList.contains('ng-value-icon')) {
return;
}
if (!this._focused) {
this.focus();
}
if (this.searchable()) {
this.open();
}
else {
this.toggle();
}
}
handleArrowClick() {
if (this.isOpen()) {
this.close();
}
else {
this.open();
}
}
handleClearClick(_event) {
if (this.hasValue) {
this.itemsList.clearSelected(this.clearKeepsDisabledOptions());
this._updateNgModel();
}
this._clearSearch();
this.focus();
this.clearEvent.emit();
this._onSelectionChanged();
}
clearModel() {
if (!this.clearable()) {
return;
}
this.itemsList.clearSelected(false);
this._updateNgModel();
}
writeValue(value) {
this.itemsList.clearSelected(false);
this._handleWriteValue(value);
if (this._editableSearchTermActive()) {
this._setSearchTermFromItems();
}
this._cd.markForCheck();
}
registerOnChange(fn) {
this._onChange = fn;
}
registerOnTouched(fn) {
this._onTouched = fn;
}
setDisabledState(state) {
this._disabled.set(state);
this._cd.markForCheck();
}
toggle() {
if (!this.isOpen()) {
this.open();
}
else {
this.close();
}
}
/** Opens the select dropdown panel */
open() {
if (this.disabled() || this.isOpen() || this._manualOpen) {
return;
}
if (!this.typeahead()?.observed && !this.addTag() && this.itemsList.noItemsToSelect) {
return;
}
this.isOpen.set(true);
this.itemsList.markSelectedOrDefault(this.markFirst());
this.openEvent.emit();
if (!this.searchTerm) {
this.focus();
}
// Attach synchronously (effects only flush on the next tick): consumers expect the
// panel to be in the DOM right after open() returns
this._syncDropdownOverlay();
this.detectChanges();
}
/** Closes the select dropdown panel */
close() {
if (!this.isOpen() || this._manualOpen) {
return;
}
this.isOpen.set(false);
this._isComposing = false;
if (!this._editableSearchTermActive()) {
this._clearSearch();
}
else {
this.itemsList.resetFilteredItems();
}
this.itemsList.unmarkItem();
this._onTouched();
this.closeEvent.emit();
// Detach synchronously (effects only flush on the next tick): the panel must leave
// the DOM without relying on zone-triggered change detection (#2765)
this._syncDropdownOverlay();
this.detectChanges();
}
toggleItem(item) {
if (!item || item.disabled || this.disabled()) {
return;
}
if (this.deselectOnClickValue() && item.selected) {
this.unselect(item);
}
else {
this.select(item);
}
if (this._editableSearchTermActive()) {
this._setSearchTermFromItems();
}
}
select(item) {
if (!item.selected) {
this.itemsList.select(item);
if (this.clearSearchOnAddValue() && !this._editableSearchTermActive()) {
this._clearSearch();
}
this._updateNgModel();
if (this.multiple()) {
this.addEvent.emit(item.value);
}
}
if (this.closeOnSelect() || this.itemsList.noItemsToSelect) {
this.close();
}
this._onSelectionChanged();
}
/** Focuses the select element */
focus() {
this.searchInput().nativeElement.focus();
}
/** Blurs the select element */
blur() {
this.searchInput().nativeElement.blur();
}
unselect(item) {
if (!item || this.disabled() || item.disabled) {
return;
}
this.itemsList.unselect(item);
this.focus();
this._updateNgModel();
this.removeEvent.emit(item.value);
this._onSelectionChanged();
}
selectTag() {
let tag;
if (isFunction(this.addTag())) {
tag = this.addTag()(this.searchTerm);
}
else {
tag = this._primitive ? this.searchTerm : { [this.bindLabel()]: this.searchTerm };
}
const handleTag = (item) => (this.typeahead()?.observed || !this.isOpen() ? this.itemsList.mapItem(item, null) : this.itemsList.addItem(item));
if (isPromise(tag)) {
tag.then((item) => this.select(handleTag(item))).catch(() => { });
}
else if (tag) {
this.select(handleTag(tag));
}
}
showClear() {
return this.clearable() && (this.hasValue || this.searchTerm) && !this.disabled();
}
focusOnClear() {
this.blur();
if (this.clearButton()) {
this.clearButton().nativeElement.focus();
}
}
showNoItemsFound() {
const empty = this.itemsList.filteredItems.length === 0;
return (((empty && !this.typeahead()?.observed && !this.loading()) || (empty && this.typeahead()?.observed && this._validTerm() && !this.loading())) &&
!this.showAddTag);
}
showTypeToSearch() {
const empty = this.itemsList.filteredItems.length === 0;
return empty && this.typeahead()?.observed && !this._validTerm() && !this.loading();
}
onCompositionStart() {
this._isComposing = true;
}
onCompositionEnd(term) {
this._isComposing = false;
if (this.searchWhileComposing()) {
return;
}
this.filter(term);
}
filter(term) {
if (this._isComposing && !this.searchWhileComposing()) {
return;
}
this._searchTerm.set(term);
if (this.typeahead()?.observed && (this._validTerm() || this.minTermLength() === 0)) {
this.typeahead().next(term);
}
if (!this.typeahead()?.observed) {
this.itemsList.filter(term);
if (this.isOpen()) {
this.itemsList.markSelectedOrDefault(this.markFirst());
}
}
this.searchEvent.emit({ term, items: this.itemsList.filteredItems.map((x) => x.value) });
this.open();
}
onInputFocus($event) {
if (this._focused) {
return;
}
if (this._editableSearchTermActive()) {
this._setSearchTermFromItems();
}
this.element.classList.add('ng-select-focused');
this.focusEvent.emit($event);
this._focused = true;
}
onInputBlur($event) {
this.element.classList.remove('ng-select-focused');
this.blurEvent.emit($event);
// When `selectOnTab` is enabled, commit the marked item on any focus loss, not just the literal Tab
// key handled in `_handleTab` (e.g. mouse click-away or assistive-technology navigation). The Tab key
// path calls `preventDefault()` and keeps focus, so this cannot double-select for that case.
if (this.selectOnTab() && this.isOpen() && !this.disabled() && this.itemsList.markedItem && !this._isComposing) {
this.toggleItem(this.itemsList.markedItem);
}
if (!this.isOpen() && !this.disabled()) {
this._onTouched();
}
if (this._editableSearchTermActive()) {
this._setSearchTermFromItems();
}
this._focused = false;
}
onItemHover(item) {
if (item.disabled) {
return;
}
this.itemsList.markItem(item);
}
detectChanges() {
if (!this._cd.destroyed) {
this._cd.detectChanges();
}
}
_handleSignalChanges() {
let itemsInitialized = false;
effect(() => {
const items = this.items();
if (!itemsInitialized) {
itemsInitialized = true;
return;
}
untracked(() => {
this._itemsAreUsed = true;
this._setItems(items || []);
});
}, { injector: this._injector });
let multipleInitialized = false;
effect(() => {
this.multiple();
if (!multipleInitialized) {
multipleInitialized = true;
return;
}
untracked(() => this.itemsList.clearSelected(false));
}, { injector: this._injector });
let groupByInitialized = false;
effect(() => {
this.groupBy();
if (!groupByInitialized) {
groupByInitialized = true;
return;
}
untracked(() => this._setItems([...this.items()]));
}, { injector: this._injector });
effect(() => {
this.inputAttrs();
const input = this.searchInput();
if (!input) {
return;
}
untracked(() => this._setInputAttributes());
}, { injector: this._injector });
// open()/close() sync the overlay imperatively; this effect covers the paths that
// bypass them — an [isOpen] binding (manual mode), programmatic isOpen.set(), and
// dropdownPosition changes while open
effect(() => {
this.isOpen();
this.dropdownPosition();
// Track the template query too: with [isOpen] pre-set, the first run can happen
// before view children resolve, and this re-runs the effect once they do
this._dropdownTemplate();
untracked(() => this._syncDropdownOverlay());
}, { injector: this._injector });
}
_setSearchTermFromItems() {
const selected = this.selectedItems?.[0];
this._searchTerm.set(selected?.label ?? null);
}
_setItems(items) {
const firstItem = items[0];
this.bindLabel.set(this.bindLabel() || this._defaultLabel);
this._primitive = isDefined(firstItem) ? !isObject(firstItem) : this._primitive || this.bindLabel() === this._defaultLabel;
this.itemsList.setItems(items);
if (items.length > 0 && this.hasValue) {
this.itemsList.mapSelectedItems();
}
if (this.isOpen() && isDefined(this.searchTerm) && !this.typeahead()?.observed) {
this.itemsList.filter(this.searchTerm);
}
if (this.typeahead()?.observed || this.isOpen()) {
this.itemsList.markSelectedOrDefault(this.markFirst());
}
}
_setItemsFromNgOptions() {
effect(() => {
const options = this.ngOptions();
// Wait until all ng-option inputs are initialized (avoids _groupBy crash when values load async)
if (options.length > 0 && !options.every((opt) => opt.isInitialized())) {
return;
}
this.bindLabel.set(this._defaultLabel);
const items = options.map((option) => ({
$ngOptionValue: option.value(),
$ngOptionLabel: option.label(),
$ngOptionClasses: option.classes(),
disabled: option.disabled(),
}));
this.items.set(items);
this.itemsList.setItems(items);
if (this.hasValue) {
this.itemsList.mapSelectedItems();
}
this._cd.detectChanges();
options
// find item for each option
.map((option) => ({
option,
item: this.itemsList.findItem(option.value()),
}))
// filter non found items
.filter(({ item }) => isDefined(item))
// process to update disabled and label
.forEach(({ option, item }) => {
item.disabled = option.disabled();
item.label = option.label() || item.label;
item.classes = option.classes();
});
}, { injector: this._injector });
}
_isValidWriteValue(value) {
if (!isDefined(value) || (this.multiple() && value === '') || (Array.isArray(value) && value.length === 0)) {
return false;
}
const validateBinding = (item) => {
if (!isDefined(this.compareWith()) && isObject(item) && this.bindValue()) {
this._console.warn(`Setting object(${JSON.stringify(item)}) as your model with bindValue is not allowed unless [compareWith] is used.`);
return false;
}
return true;
};
if (this.multiple()) {
if (!Array.isArray(value)) {
this._console.warn('Multiple select ngModel should be array.');
return false;
}
return value.every((item) => validateBinding(item));
}
else {
return validateBinding(value);
}
}
_handleWriteValue(ngModel) {
if (!this._isValidWriteValue(ngModel)) {
return;
}
const select = (val) => {
let item = this.itemsList.findItem(val);
if (item) {
this.itemsList.select(item);
}
else {
const isValObject = isObject(val);
const isPrimitive = !isValObject && !this.bindValue();
if (isValObject || isPrimitive) {
this.itemsList.select(this.itemsList.mapItem(val, null));
}
else {
item = {
[this.bindLabel()]: null,
[this.bindValue()]: val,
};
this.itemsList.select(this.itemsList.mapItem(item, null));
}
}
};
if (this.multiple()) {
ngModel.forEach((item) => select(item));
}
else {
select(ngModel);
}
}
_handleKeyPresses() {
if (this.searchable()) {
return;
}
this._keyPress$
.pipe(takeUntilDestroyed(this._destroyRef), tap((letter) => this._pressedKeys.push(letter)), debounceTime(200), filter(() => this._pressedKeys.length > 0), map(() => this._pressedKeys.join('')))
.subscribe((term) => {
const item = this.itemsList.findByLabel(term);
if (item) {
if (this.isOpen()) {
if (!item.disabled) {
this.itemsList.markItem(item);
this._scrollToMarked();
// Required under zoneless CD: this subscription fires from a debounce
// timer, which schedules nothing by itself
this._cd.markForCheck();
}
}
else {
this.select(item);
}
}
this._pressedKeys = [];
});
}
_setInputAttributes() {
const input = this.searchInput().nativeElement;
const attributes = {
type: 'text',
autocorrect: 'off',
autocapitalize: 'off',
autocomplete: 'off',
...this.inputAttrs(),
};
for (const key of Object.keys(attributes)) {
input.setAttribute(key, attributes[key]);
}
}
_setTabFocusOnClear() {
this.tabFocusOnClear.set(isDefined(this.tabFocusOnClearButton()) ? !!this.tabFocusOnClearButton() : this.config.tabFocusOnClear);
}
_updateNgModel() {
const model = [];
for (const item of this.selectedItems) {
if (this.bindValue()) {
let value;
if (item.children) {
const groupKey = this.groupValue() ? this.bindValue() : this.groupBy();
value = item.value[groupKey || this.groupBy()];
}
else {
value = this.itemsList.resolveNested(item.value, this.bindValue());
}
model.push(value);
}
else {
model.push(item.value);
}
}
const selected = this.selectedItems.map((x) => x.value);
if (this.multiple()) {
this._onChange(model);
this.changeEvent.emit(selected);
}
else {
this._onChange(isDefined(model[0]) ? model[0] : null);
this.changeEvent.emit(selected[0]);
}
// Required under zoneless CD: sole notifier for the programmatic selection
// APIs (select/unselect/clearModel/clearItem) called from non-Angular contexts
this._cd.markForCheck();
}
_clearSearch() {
if (!this.searchTerm) {
return;
}
this._changeSearch(null);
this.itemsList.resetFilteredItems();
}
_changeSearch(searchTerm) {
this._searchTerm.set(searchTerm);
}
_scrollToMarked() {
if (!this.isOpen() || !this.dropdownPanel()) {
return;
}
this.dropdownPanel().scrollTo(this.itemsList.markedItem);
}
_scrollToTag() {
if (!this.isOpen() || !this.dropdownPanel()) {
return;
}
this.dropdownPanel().scrollToTag();
}
_onSelectionChanged() {
if (this.isOpen() && this.deselectOnClickValue()) {
// Make sure items are rendered.
this._cd.detectChanges();
this.dropdownPanel()?.adjustPosition();
}
}
/** Attaches or detaches the dropdown overlay to match the current open state. Idempotent. */
_syncDropdownOverlay() {
if (!this._dropdownTemplate()) {
return;
}
if (this.isOpen()) {
this._openDropdownOverlay(this.dropdownPosition());
}
else {
this.dropdownOverlayRef?.detach();
}
}
/**
* Creates the overlay on first open and (re)attaches the dropdown template to it.
* The panel always renders in the CDK overlay; `dropdownPosition` maps onto connected
* positions, with `auto` falling back to the opposite side when space runs out.
*/
_openDropdownOverlay(position) {
const overlayRef = this._ensureDropdownOverlay();
this._dropdownPositionStrategy.withPositions(DROPDOWN_POSITIONS[position] ?? DROPDOWN_POSITIONS.auto);
// Direction is snapshotted per open, matching how the previous implementation read
// `document.documentElement.dir` when positioning an appended panel
overlayRef.setDirection(this._document?.documentElement?.dir === 'rtl' ? 'rtl' : 'ltr');
// The panel tracks the width of the select; the panel keeps it in sync on host resize
overlayRef.updateSize({ width: this._dropdownOrigin().getBoundingClientRect().width });
if (overlayRef.hasAttached()) {
// Only `dropdownPosition` changed while open — re-evaluate with the new positions
overlayRef.updatePosition();
return;
}
this._dropdownPortal ??= new TemplatePortal(this._dropdownTemplate(), this._dropdownOutlet());
overlayRef.attach(this._dropdownPortal);
}
/**
* The dropdown anchors to the `.ng-select-container` box, not the host: themes may pad
* the host below the container (material reserves 1.25em of subscript space under the
* underline), and the panel must sit flush against the visible field — the same anchor
* the pre-overlay `appendTo` positioning used.
*/
_dropdownOrigin() {
return this._selectContainer()?.nativeElement ?? this.element;
}
_ensureDropdownOverlay() {
const appendTo = this.appendTo() ?? this.config.appendTo ?? null;
if (this.dropdownOverlayRef && appendTo !== this._overlayAppendTo) {
// `appendTo` changed since the overlay was created — rebuild against the new host
this._destroyDropdownOverlay();
}
if (!this.dropdownOverlayRef) {
let injector = this._injector;
let appendToHost = null;
if (appendTo) {
appendToHost = this._resolveAppendToHost(appendTo);
this._appendToContainer = runInInjectionContext(this._injector, () => new NgSelectAppendToOverlayContainer(appendToHost));
injector = Injector.create({
parent: this._injector,
providers: [{ provide: OverlayContainer, useValue: this._appendToContainer }],
});
}
this._dropdownPositionStrategy = createFlexibleConnectedPositionStrategy(injector, this._dropdownOrigin()).withFlexibleDimensions(false).withPush(false);
if (appendToHost) {
// Popover-capable browsers paint the panel in the top layer regardless of where it
// lives in the DOM, so the `appendTo` host only determines DOM containment —
// ancestor-scoped styles and focus enclosure. Browsers without the Popover API fall
// back to the NgSelectAppendToOverlayContainer provided above instead.
this._dropdownPositionStrategy.withPopoverLocation({ type: 'parent', element: appendToHost });
}
this._overlayAppendTo = appendTo;
this.dropdownOverlayRef = createOverlayRef(injector, {
positionStrategy: this._dropdownPositionStrategy,
// Ancestor-scroll repositioning is handled by the panel's capture-phase document
// listener, which also covers plain scroll containers that CDK's ScrollDispatcher
// cannot see without a `cdkScrollable` marker (#2788)
scrollStrategy: createNoopScrollStrategy(),
// Detach must remove the panel from the DOM synchronously (#2765); the panel has
// no CDK-driven animations to wait for
disableAnimations: true,
});
}
return this.dropdownOverlayRef;
}
_destroyDropdownOverlay() {
this.dropdownOverlayRef?.dispose();
this.dropdownOverlayRef = null;
this._dropdownPositionStrategy = null;
this._appendToContainer?.ngOnDestroy();
this._appendToContainer = null;
}
/** Resolves the `appendTo` selector against the select's own root, so a select inside a shadow root finds hosts in that same root. */
_resolveAppendToHost(selector) {
const root = this.element.getRootNode();
const scope = typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root : this._document;
const host = scope.querySelector(selector);
if (!host) {
throw new Error(`appendTo selector ${selector} did not found any parent element`);
}
return host;
}
_warnDeprecatedInputs() {
if (!isDevMode()) {
return;
}
if (this.popover()) {
this._console.warn('[ng-select] `popover` is deprecated and has no effect: the dropdown panel now renders in an Angular CDK overlay, which uses the native Popover API top layer automatically in supporting browsers.');
}
}
_handleTab($event) {
if (this.isOpen() === false) {
if (this.showClear() && !$event.shiftKey && this.tabFocusOnClear()) {
this.focusOnClear();
$event.preventDefault();
}
else if (!this.addTag()) {
return;
}
}
if (this.selectOnTab()) {
if (this.itemsList.markedItem) {
this.toggleItem(this.itemsList.markedItem);
$event.preventDefault();
}
else if (this.showAddTag) {
this.selectTag();
$event.preventDefault();
}
else {
this.close();
}
}
else {
this.close();
}
}
_handleEnter($event) {
const openOnEnter = this.openOnEnter() ?? this.config.openOnEnter;
if (this.isOpen() || this._manualOpen) {
if (this.itemsList.markedItem) {
this.toggleItem(this.itemsList.markedItem);
}
else if (this.showAddTag) {
this.selectTag();
}
}
else if (openOnEnter) {
this.open();
}
else {
return;
}
$event.preventDefault();
}
_handleSpace($event) {
if (this.isOpen() || this._manualOpen) {
return;
}
this.open();
$event.preventDefault();
}
_handleArrowDown($event) {
if (this._nextItemIsTag(+1)) {
this.itemsList.unmarkItem();
this._scrollToTag();
}
else {
this.itemsList.markNextItem();
this._scrollToMarked();
}
this.open();
$event.preventDefault();
}
_handleArrowUp($event) {
if (!this.isOpen()) {
return;
}
if (this._nextItemIsTag(-1)) {
this.itemsList.unmarkItem();
this._scrollToTag();
}
else {
this.itemsList.markPreviousItem();
this._scrollToMarked();
}
$event.preventDefault();
}
_nextItemIsTag(nextStep) {
const nextIndex = this.itemsList.markedIndex + nextStep;
return this.addTag() && this.searchTerm && this.itemsList.markedItem && (nextIndex < 0 || nextIndex === this.itemsList.filteredItems.length);
}
_handleBackspace() {
if (this.searchTerm || !this.clearable() || !this.clearOnBackspace() || !this.hasValue) {
return;
}
if (this.multiple()) {
this.unselect(this.itemsList.lastSelectedItem);
}
else {
this.clearModel();
}
}
_mergeGlobalConfig(config) {
this.bindValue.set(this.bindValue() || config.bindValue);
this.bindLabel.set(this.bindLabel() || config.bindLabel);
this.appearance.set(this.appearance() || config.appearance);
this._setTabFocusOnClear();
}
/**
* Gets virtual scroll value from input or from config
*
* @param config NgSelectConfig object
*
* @returns `true` if virtual scroll is enabled, `false` otherwise
*/
getVirtualScroll(config) {
return isDefined(this._virtualScroll()) ? this._virtualScroll() : this.isVirtualScrollDisabled(config);
}
/**
* Gets disableVirtualScroll value from input or from config
*
* @param config NgSelectConfig object
*
* @returns `true` if disableVirtualScroll is enabled, `false` otherwise
*/
isVirtualScrollDisabled(config) {
return isDefined(config.disableVirtualScroll) ? !config.disableVirtualScroll : false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: NgSelectComponent, isStandalone: true, selector: "ng-select", inputs: { _ariaLabelDropdown: { classPropertyName: "_ariaLabelDropdown", publicName: "ariaLabelDropdown", isSignal: true, isRequired: false, transformFunction: null }, _ariaLabel: { classPropertyName: "_ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, _markFirst: { classPropertyName: "_markFirst", publicName: "markFirst", isSignal: true, isRequired: false, transformFunction: null }, _placeholder: { classPropertyName: "_placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, _fixedPlaceholder: { classPropertyName: "_fixedPlaceholder", publicName: "fixedPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, _notFoundText: { classPropertyName: "_notFoundText", publicName: "notFoundText", isSignal: true, isRequired: false, transformFunction: null }, _typeToSearchText: { classPropertyName: "_typeToSearchText", publicName: "typeToSearchText", isSignal: true, isRequired: false, transformFunction: null }, _preventToggleOnRightClick: { classPropertyName: "_preventToggleOnRightClick", publicName: "preventToggleOnRightClick", isSignal: true, isRequired: false, transformFunction: null }, _addTagText: { classPropertyName: "_addTagText", publicName: "addTagText", isSignal: true, isRequired: false, transformFunction: null }, _loadingText: { classPropertyName: "_loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, _clearAllText: { classPropertyName: "_clearAllText", publicName: "clearAllText", isSignal: true, isRequired: false, transformFunction: null }, _removeText: { classPropertyName: "_removeText", publicName: "removeText", isSignal: true, isRequired: false, transformFunction: null }, _dropdownPosition: { classPropertyName: "_dropdownPosition", publicName: "dropdownPosition", isSignal: true, isRequired: false, transformFunction: null }, _appendTo: { classPropertyName: "_appendTo", publicName: "appendTo", isSignal: true, isRequired: false, transformFunction: null }, _outsideClickEvent: { classPropertyName: "_outsideClickEvent", publicName: "outsideClickEvent", isSignal: true, isRequired: false, transformFunction: null }, _loading: { classPropertyName: "_loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, _closeOnSelect: { classPropertyName: "_closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, _hideSelected: { classPropertyName: "_hideSelected", publicName: "hideSelected", isSignal: true, isRequired: false, transformFunction: null }, _selectOnTab: { classPropertyName: "_selectOnTab", publicName: "selectOnTab", isSignal: true, isRequired: false, transformFunction: null }, _openOnEnter: { classPropertyName: "_openOnEnter", publicName: "openOnEnter", isSignal: true, isRequired: false, transformFunction: null }, _maxSelectedItems: { classPropertyName: "_maxSelectedItems", publicName: "maxSelectedItems", isSignal: true, isRequired: false, transformFunction: null }, _groupBy: { classPropertyName: "_groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, _groupValue: { classPropertyName: "_groupValue", publicName: "groupValue", isSignal: true, isRequired: false, transformFunction: null }, _bufferAmount: { classPropertyName: "_bufferAmount", publicName: "bufferAmount", isSignal: true, isRequired: false, transformFunction: null }, _virtualScroll: { classPropertyName: "_virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, _selectableGroup: { classPropertyName: "_selectableGroup", publicName: "selectableGroup", isSignal: true, isRequired: false, transformFunction: null }, _tabFocusOnClearButton: { classPropertyName: "_tabFocusOnClearButton", publicName: "tabFocusOnClearButton", isSignal: true, isRequired: false, transformFunction: null }, _selectableGroupAsModel: { classPropertyName: "_selectableGroupAsModel", publicName: "selectableGroupAsModel", isSignal: true, isRequired: false, transformFunction: null }, _searchFn: { classPropertyName: "_searchFn", publicName: "searchFn", isSignal: true, isRequired: false, transformFunction: null }, _trackByFn: { classPropertyName: "_trackByFn", publicName: "trackByFn", isSignal: true, isRequired: false, transformFunction: null }, _clearOnBackspace: { classPropertyName: "_clearOnBackspace", publicName: "clearOnBackspace", isSignal: true, isRequired: false, transformFunction: null }, _labelForId: { classPropertyName: "_labelForId", publicName: "labelForId", isSignal: true, isRequired: false, transformFunction: null }, _inputAttrs: { classPropertyName: "_inputAttrs", publicName: "inputAttrs", isSignal: true, isRequired: false, transformFunction: null }, _tabIndex: { classPropertyName: "_tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, _readonly: { classPropertyName: "_readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, _searchWhileComposing: { classPropertyName: "_searchWhileComposing", publicName: "searchWhileComposing", isSignal: true, isRequired: false, transformFunction: null }, _minTermLength: { classPropertyName: "_minTermLength", publicName: "minTermLength", isSignal: true, isRequired: false, transformFunction: null }, _editableSearchTerm: { classPropertyName: "_editableSearchTerm", publicName: "editableSearchTerm", isSignal: true, isRequired: false, transformFunction: null }, _ngClass: { classPropertyName: "_ngClass", publicName: "ngClass", isSignal: true, isRequired: false, transformFunction: null }, _typeahead: { classPropertyName: "_typeahead", publicName: "typeahead", isSignal: true, isRequired: false, transformFunction: null }, _multiple: { classPropertyName: "_multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, _addTag: { classPropertyName: "_addTag", publicName: "addTag", isSignal: true, isRequired: false, transformFunction: null }, _searchable: { classPropertyName: "_searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, _clearable: { classPropertyName: "_clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, _clearKeepsDisabledOptions: { classPropertyName: "_clearKeepsDisabledOptions", publicName: "clearKeepsDisabledOptions", isSignal: true, isRequired: false, transformFunction: null }, _deselectOnClick: { classPropertyName: "_deselectOnClick", publicName: "deselectOnClick", isSignal: true, isRequired: false, transformFunction: null }, _clearSearchOnAdd: { classPropertyName: "_clearSearchOnAdd", publicName: "clearSearchOnAdd", isSignal: true, isRequired: false, transformFunction: null }, _compareWith: { classPropertyName: "_compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null }, _keyDownFn: { classPropertyName: "_keyDownFn", publicName: "keyDownFn", isSignal: true, isRequired: false, transformFunction: null }, _popover: { classPropertyName: "_popover", publicName: "popover", isSignal: true, isRequired: false, transformFunction: null }, bindLabel: { classPropertyName: "bindLabel", publicName: "bindLabel", isSignal: true, isRequired: false, transformFunction: null }, bindValue: { classPropertyName: "bindValue", publicName: "bindValue", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { bindLabel: "bindLabelChange", bindValue: "bindValueChange", appearance: "appearanceChange", isOpen: "isOpenChange", items: "itemsChange", blurEvent: "blur", focusEvent: "focus", changeEvent: "change", openEvent: "open", closeEvent: "close", searchEvent: "search", clearEvent: "clear", addEvent: "add", removeEvent: "remove", scroll: "scroll", scrollToEnd: "scrollToEnd" }, host: { listeners: { "keydown": "handleKeyDown($event)" }, properties: { "class.ng-select": "true", "class.ng-select-single": "!multiple()", "class.ng-select-typeahead": "typeahead()", "class.ng-select-multiple": "multiple()", "class.ng-select-taggable": "addTag()", "class.ng-select-searchable": "searchable()", "class.ng-select-clearable": "clearable()", "class.ng-select-opened": "isOpen()", "class.ng-select-filtered": "filtered", "class.ng-select-disabled": "disabled()" } }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NgSelectComponent),
multi: true,
},
NgDropdownPanelService,
], queries: [{ propertyName: "optionTemplate", first: true, predicate: NgOptionTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "optgroupTemplate", first: true, predicate: NgOptgroupTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "labelTemplate", first: true, predicate: NgLabelTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "multiLabelTemplate", first: true, predicate: NgMultiLabelTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "headerTemplate", first: true, predicate: NgHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "footerTemplate", first: true, predicate: NgFooterTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "notFoundTemplate", first: true, predicate: NgNotFoundTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "placeholderTemplate", first: true, predicate: NgPlaceholderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "typeToSearchTemplate", first: true, predicate: NgTypeToSearchTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "loadingTextTemplate", first: true, predicate: NgLoadingTextTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "tagTemplate", first: true, predicate: NgTagTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "loadingSpinnerTemplate", first: true, predicate: NgLoadingSpinnerTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "clearButtonTemplate", first: true, predicate: NgClearButtonTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "ngOptions", predicate: NgOptionComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "dropdownPanel", first: true, predicate: i0.forwardRef(() => NgDropdownPanelComponent), descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "clearButton", first: true, predicate: ["clearButton"], descendants: true, isSignal: true }, { propertyName: "_dropdownTemplate", first: true, predicate: ["dropdownTemplate"], descendants: true, isSignal: true }, { propertyName: "_dropdownOutlet", first: true, predicate: ["dropdownTemplate"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "_selectContainer", first: true, predicate: ["selectContainer"], descendants: true, isSignal: true }], exportAs: ["ngSelect"], usesOnChanges: true, ngImport: i0, template: "<div\n\t#selectContainer\n\t(mousedown)=\"handleMousedown($event)\"\n\t[class.ng-appearance-outline]=\"appearance() === 'outline'\"\n\t[class.ng-appearance-fill]=\"appearance() === 'fill'\"\n\t[class.ng-has-value]=\"hasValue\"\n\tclass=\"ng-select-container\">\n\t<div class=\"ng-value-container\">\n\t\t@if ((selectedItems.length === 0 && !searchTerm) || (fixedPlaceholder() ?? config.fixedPlaceholder)) {\n\t\t\t<ng-template #defaultPlaceholderTemplate>\n\t\t\t\t<div class=\"ng-placeholder\">{{ placeholder() ?? config.placeholder }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"placeholderTemplate() || defaultPlaceholderTemplate\"> </ng-template>\n\t\t}\n\n\t\t@if ((!multiLabelTemplate() || !multiple()) && selectedItems.length > 0) {\n\t\t\t@for (item of selectedItems; track trackByOption($index, item)) {\n\t\t\t\t<div [class.ng-value-disabled]=\"item.disabled\" class=\"ng-value\">\n\t\t\t\t\t<ng-template #defaultLabelTemplate>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"ng-value-icon left\"\n\t\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\t\ttabindex=\"0\"\n\t\t\t\t\t\t\t[attr.aria-label]=\"(removeText() ?? config.removeText) + ' ' + item.label\"\n\t\t\t\t\t\t\t(click)=\"unselect(item)\"\n\t\t\t\t\t\t\t(keydown)=\"handleRemoveKeydown($event, item)\"\n\t\t\t\t\t\t\t>\u00D7</span\n\t\t\t\t\t\t>\n\t\t\t\t\t\t<span class=\"ng-value-label\">{{ item.label }}</span>\n\t\t\t\t\t</ng-template>\n\t\t\t\t\t<ng-template\n\t\t\t\t\t\t[ngTemplateOutlet]=\"labelTemplate() || defaultLabelTemplate\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ item: item.value, clear: clearItem, label: item.label }\">\n\t\t\t\t\t</ng-template>\n\t\t\t\t</div>\n\t\t\t}\n\t\t}\n\n\t\t@if (multiple() && multiLabelTemplate() && selectedValues.length > 0) {\n\t\t\t<ng-template [ngTemplateOutlet]=\"multiLabelTemplate()\" [ngTemplateOutletContext]=\"{ items: selectedValues, clear: clearItem }\"> </ng-template>\n\t\t}\n\n\t\t<div class=\"ng-input\">\n\t\t\t<input\n\t\t\t\t#searchInput\n\t\t\t\t(blur)=\"onInputBlur($event)\"\n\t\t\t\t(change)=\"$event.stopPropagation()\"\n\t\t\t\t(compositionend)=\"onCompositionEnd(searchInput.value)\"\n\t\t\t\t(compositionstart)=\"onCompositionStart()\"\n\t\t\t\t(focus)=\"onInputFocus($event)\"\n\t\t\t\t(input)=\"filter(searchInput.value)\"\n\t\t\t\t[attr.aria-activedescendant]=\"isOpen() ? itemsList?.markedItem?.htmlId : null\"\n\t\t\t\t[attr.aria-controls]=\"isOpen() ? dropdownId : null\"\n\t\t\t\t[attr.aria-expanded]=\"isOpen()\"\n\t\t\t\t[attr.aria-label]=\"ariaLabel()\"\n\t\t\t\t[attr.id]=\"labelForId()\"\n\t\t\t\t[attr.tabindex]=\"tabIndex()\"\n\t\t\t\t[disabled]=\"disabled()\"\n\t\t\t\t[readOnly]=\"!searchable() || itemsList.maxItemsSelected\"\n\t\t\t\t[value]=\"searchTerm ?? ''\"\n\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\trole=\"combobox\" />\n\t\t</div>\n\t</div>\n\n\t@if (loading()) {\n\t\t<ng-template #defaultLoadingSpinnerTemplate>\n\t\t\t<div class=\"ng-spinner-loader\"></div>\n\t\t</ng-template>\n\t\t<ng-template [ngTemplateOutlet]=\"loadingSpinnerTemplate() || defaultLoadingSpinnerTemplate\"></ng-template>\n\t}\n\n\t@if (showClear()) {\n\t\t<span\n\t\t\tclass=\"ng-clear-wrapper\"\n\t\t\trole=\"button\"\n\t\t\ttabindex=\"0\"\n\t\t\t[attr.tabindex]=\"tabFocusOnClear() ? 0 : -1\"\n\t\t\t[attr.aria-label]=\"clearAllText() || config.clearAllText\"\n\t\t\ttitle=\"{{ clearAllText() || config.clearAllText }}\"\n\t\t\t#clearButton\n\t\t\t(click)=\"handleClearClick($event)\">\n\t\t\t@if (clearButtonTemplate()) {\n\t\t\t\t<ng-container [ngTemplateOutlet]=\"clearButtonTemplate()\"></ng-container>\n\t\t\t} @else {\n\t\t\t\t<span class=\"ng-clear\" aria-hidden=\"true\">\u00D7</span>\n\t\t\t}\n\t\t</span>\n\t}\n\n\t<span class=\"ng-arrow-wrapper\">\n\t\t<span class=\"ng-arrow\"></span>\n\t</span>\n\n\t@if (appearance() === 'outline') {\n\t\t<div class=\"ng-notched-outline\" aria-hidden=\"true\">\n\t\t\t<div class=\"ng-notched-outline-leading\"></div>\n\t\t\t<div class=\"ng-notched-outline-notch\" [style.width.px]=\"outlineNotchWidth()\"></div>\n\t\t\t<div class=\"ng-notched-outline-trailing\"></div>\n\t\t</div>\n\t}\n</div>\n\n<!-- Rendered through a TemplatePortal into the CDK overlay while the select is open;\n the embedded view stays part of this component's view tree, only the DOM moves. -->\n<ng-template #dropdownTemplate>\n\t<ng-dropdown-panel\n\t\tclass=\"ng-dropdown-panel\"\n\t\t[virtualScroll]=\"dropdownVirtualScroll()\"\n\t\t[bufferAmount]=\"bufferAmount()\"\n\t\t[position]=\"dropdownPosition()\"\n\t\t[outsideClickEvent]=\"outsideClickEvent()\"\n\t\t[headerTemplate]=\"headerTemplate()\"\n\t\t[footerTemplate]=\"footerTemplate()\"\n\t\t[filterValue]=\"searchTerm\"\n\t\t[items]=\"itemsList.filteredItems\"\n\t\t[showAddTag]=\"showAddTag\"\n\t\t[markedItem]=\"itemsList.markedItem\"\n\t\t(update)=\"viewPortItems = $event\"\n\t\t(scroll)=\"scroll.emit($event)\"\n\t\t(scrollToEnd)=\"scrollToEnd.emit($event)\"\n\t\t(outsideClick)=\"close()\"\n\t\t[class.ng-select-multiple]=\"multiple()\"\n\t\t[class]=\"ngClass() ? ngClass() : classes\"\n\t\t[listboxId]=\"dropdownId\"\n\t\t[ariaLabelDropdown]=\"ariaLabelDropdown() ?? config.ariaLabelDropdown\"\n\t\t[overlayRef]=\"dropdownOverlayRef\"\n\t\t[selectElement]=\"element\">\n\t\t<ng-container>\n\t\t\t@for (item of viewPortItems; track trackByOption($index, item)) {\n\t\t\t\t<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus, @angular-eslint/template/mouse-events-have-key-events -->\n\t\t\t\t<div\n\t\t\t\t\t[attr.role]=\"item.children ? 'group' : 'option'\"\n\t\t\t\t\t(click)=\"toggleItem(item)\"\n\t\t\t\t\t(mouseover)=\"onItemHover(item)\"\n\t\t\t\t\t[ngClass]=\"item.classes\"\n\t\t\t\t\t[class.ng-option-disabled]=\"item.disabled\"\n\t\t\t\t\t[class.ng-option-selected]=\"item.selected\"\n\t\t\t\t\t[class.ng-optgroup]=\"item.children\"\n\t\t\t\t\t[class.ng-option]=\"!item.children\"\n\t\t\t\t\t[class.ng-option-child]=\"!!item.parent\"\n\t\t\t\t\t[class.ng-option-marked]=\"item === itemsList.markedItem\"\n\t\t\t\t\t[attr.aria-selected]=\"item.selected\"\n\t\t\t\t\t[attr.id]=\"item?.htmlId\"\n\t\t\t\t\t[attr.aria-setsize]=\"itemsList.filteredItems.length\"\n\t\t\t\t\t[attr.aria-posinset]=\"itemsList.filteredItems.indexOf(item) + 1 || null\">\n\t\t\t\t\t<ng-template #defaultOptionTemplate>\n\t\t\t\t\t\t<span class=\"ng-option-label\">{{ item.label }}</span>\n\t\t\t\t\t</ng-template>\n\t\t\t\t\t<ng-template\n\t\t\t\t\t\t[ngTemplateOutlet]=\"item.children ? optgroupTemplate() || defaultOptionTemplate : optionTemplate() || defaultOptionTemplate\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ item: item.value, item$: item, index: item.index, searchTerm: searchTerm }\">\n\t\t\t\t\t</ng-template>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@if (showAddTag) {\n\t\t\t\t<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus, @angular-eslint/template/mouse-events-have-key-events -->\n\t\t\t\t<div\n\t\t\t\t\tclass=\"ng-option\"\n\t\t\t\t\t[class.ng-option-marked]=\"!itemsList.markedItem\"\n\t\t\t\t\t(mouseover)=\"itemsList.unmarkItem()\"\n\t\t\t\t\trole=\"option\"\n\t\t\t\t\t[attr.aria-selected]=\"false\"\n\t\t\t\t\t(click)=\"selectTag()\">\n\t\t\t\t\t<ng-template #defaultTagTemplate>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t><span class=\"ng-tag-label\">{{ addTagText() || config.addTagText }}</span\n\t\t\t\t\t\t\t>\"{{ searchTerm }}\"</span\n\t\t\t\t\t\t>\n\t\t\t\t\t</ng-template>\n\t\t\t\t\t<ng-template [ngTemplateOutlet]=\"tagTemplate() || defaultTagTemplate\" [ngTemplateOutletContext]=\"{ searchTerm: searchTerm }\"> </ng-template>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</ng-container>\n\t\t@if (showNoItemsFound()) {\n\t\t\t<ng-template #defaultNotFoundTemplate>\n\t\t\t\t<div class=\"ng-option ng-option-disabled\">{{ notFoundText() ?? config.notFoundText }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"notFoundTemplate() || defaultNotFoundTemplate\" [ngTemplateOutletContext]=\"{ searchTerm: searchTerm }\">\n\t\t\t</ng-template>\n\t\t}\n\t\t@if (showTypeToSearch()) {\n\t\t\t<ng-template #defaultTypeToSearchTemplate>\n\t\t\t\t<div class=\"ng-option ng-option-disabled\">{{ typeToSearchText() || config.typeToSearchText }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"typeToSearchTemplate() || defaultTypeToSearchTemplate\"></ng-template>\n\t\t}\n\t\t@if (loading() && itemsList.filteredItems.length === 0) {\n\t\t\t<ng-template #defaultLoadingTextTemplate>\n\t\t\t\t<div class=\"ng-option ng-option-disabled\">{{ loadingText() || config.loadingText }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"loadingTextTemplate() || defaultLoadingTextTemplate\" [ngTemplateOutletContext]=\"{ searchTerm: searchTerm }\">\n\t\t\t</ng-template>\n\t\t}\n\t</ng-dropdown-panel>\n</ng-template>\n\n<!-- Always present aria-live region -->\n<div aria-atomic=\"true\" aria-live=\"polite\" class=\"ng-visually-hidden\" role=\"status\">\n\t@if (isOpen()) {\n\t\t@if (showNoItemsFound()) {\n\t\t\t{{ notFoundText() ?? config.notFoundText }}\n\t\t} @else if (showTypeToSearch()) {\n\t\t\t{{ typeToSearchText() || config.typeToSearchText }}\n\t\t} @else if (loading() && itemsList.filteredItems.length === 0) {\n\t\t\t{{ loadingText() || config.loadingText }}\n\t\t} @else if (itemsList.markedItem) {\n\t\t\t{{ itemsList.markedItem.label }}\n\t\t}\n\t}\n</div>\n", styles: ["@charset \"UTF-8\";.ng-select{position:relative;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ng-select div,.ng-select input,.ng-select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ng-select [hidden]{display:none}.ng-select.ng-select-searchable .ng-select-container .ng-value-container .ng-input{opacity:1}.ng-select:not(.ng-select-searchable) .ng-select-container .ng-value-container .ng-input{pointer-events:none}.ng-select.ng-select-opened .ng-select-container{z-index:1001}.ng-select.ng-select-disabled .ng-select-container .ng-value-container .ng-placeholder,.ng-select.ng-select-disabled .ng-select-container .ng-value-container .ng-value{-webkit-user-select:none;user-select:none;cursor:default}.ng-select.ng-select-disabled .ng-arrow-wrapper{cursor:default}.ng-select.ng-select-filtered .ng-placeholder{display:none}.ng-select .ng-select-container{cursor:default;display:flex;outline:none;overflow:hidden;position:relative;width:100%}.ng-select .ng-select-container .ng-value-container{display:flex;flex:1}.ng-select .ng-select-container .ng-value-container .ng-input{opacity:0}.ng-select .ng-select-container .ng-value-container .ng-input>input{box-sizing:content-box;background:none transparent;border:0 none;box-shadow:none;outline:none;padding:0;cursor:default;width:100%}.ng-select .ng-select-container .ng-value-container .ng-input>input::-ms-clear{display:none}.ng-select .ng-select-container .ng-value-container .ng-input>input[readonly]{-webkit-user-select:unset;user-select:unset;width:0;padding:0}.ng-select.ng-select-single.ng-select-filtered .ng-select-container .ng-value-container .ng-value{visibility:hidden}.ng-select.ng-select-single .ng-select-container .ng-value-container,.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-value .ng-value-icon{display:none}.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{position:absolute;left:0;width:100%}.ng-select.ng-select-multiple.ng-select-disabled>.ng-select-container .ng-value-container .ng-value .ng-value-icon{display:none}.ng-select.ng-select-multiple .ng-select-container .ng-value-container{flex-wrap:wrap}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder{position:absolute}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value{white-space:nowrap}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value.ng-value-disabled .ng-value-icon{display:none}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon{cursor:pointer}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-input{flex:1;z-index:2}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder{z-index:1}.ng-select .ng-clear-wrapper{cursor:pointer;position:relative;width:17px;-webkit-user-select:none;user-select:none}.ng-select .ng-clear-wrapper .ng-clear{display:inline-block;font-size:18px;line-height:1;pointer-events:none}.ng-select .ng-spinner-loader{border-radius:50%;width:17px;height:17px;margin-right:5px;font-size:10px;position:relative;text-indent:-9999em;border-top:2px solid rgba(66,66,66,.2);border-right:2px solid rgba(66,66,66,.2);border-bottom:2px solid rgba(66,66,66,.2);border-left:2px solid #424242;transform:translateZ(0);animation:load8 .8s infinite linear}.ng-select .ng-spinner-loader:after{border-radius:50%;width:17px;height:17px}@-webkit-keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.ng-select .ng-arrow-wrapper{cursor:pointer;position:relative;text-align:center;-webkit-user-select:none;user-select:none}.ng-select .ng-arrow-wrapper .ng-arrow{pointer-events:none;display:inline-block;height:0;width:0;position:relative}.ng-dropdown-panel{box-sizing:border-box;position:relative;opacity:0;width:100%;-webkit-overflow-scrolling:touch}.ng-dropdown-panel .ng-dropdown-panel-items{display:block;height:auto;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;max-height:240px;overflow-y:auto}.ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option{box-sizing:border-box;cursor:pointer;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option .ng-option-label:empty:before{content:\"\\200b\"}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option .highlighted{font-weight:700;text-decoration:underline}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option.disabled{cursor:default}.ng-dropdown-panel .scroll-host{overflow:hidden;overflow-y:auto;position:relative;display:block;-webkit-overflow-scrolling:touch}.ng-dropdown-panel .scrollable-content{top:0;left:0;width:100%;height:100%;position:absolute}.ng-dropdown-panel .total-padding{width:1px;opacity:0}.cdk-overlay-pane .ng-dropdown-panel{position:relative;inset:auto}.ng-visually-hidden{position:absolute!important;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);border:0;white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgDropdownPanelComponent, selector: "ng-dropdown-panel", inputs: ["items", "showAddTag", "markedItem", "position", "bufferAmount", "virtualScroll", "headerTemplate", "footerTemplate", "filterValue", "ariaLabelDropdown", "listboxId", "outsideClickEvent", "popover", "overlayRef", "selectElement"], outputs: ["update", "scroll", "scrollToEnd", "outsideClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectComponent, decorators: [{
type: Component,
args: [{ selector: 'ng-select', exportAs: 'ngSelect', providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NgSelectComponent),
multi: true,
},
NgDropdownPanelService,
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, NgTemplateOutlet, NgDropdownPanelComponent], host: {
'[class.ng-select]': 'true',
'[class.ng-select-single]': '!multiple()',
'[class.ng-select-typeahead]': 'typeahead()',
'[class.ng-select-multiple]': 'multiple()',
'[class.ng-select-taggable]': 'addTag()',
'[class.ng-select-searchable]': 'searchable()',
'[class.ng-select-clearable]': 'clearable()',
'[class.ng-select-opened]': 'isOpen()',
'[class.ng-select-filtered]': 'filtered',
'[class.ng-select-disabled]': 'disabled()',
}, template: "<div\n\t#selectContainer\n\t(mousedown)=\"handleMousedown($event)\"\n\t[class.ng-appearance-outline]=\"appearance() === 'outline'\"\n\t[class.ng-appearance-fill]=\"appearance() === 'fill'\"\n\t[class.ng-has-value]=\"hasValue\"\n\tclass=\"ng-select-container\">\n\t<div class=\"ng-value-container\">\n\t\t@if ((selectedItems.length === 0 && !searchTerm) || (fixedPlaceholder() ?? config.fixedPlaceholder)) {\n\t\t\t<ng-template #defaultPlaceholderTemplate>\n\t\t\t\t<div class=\"ng-placeholder\">{{ placeholder() ?? config.placeholder }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"placeholderTemplate() || defaultPlaceholderTemplate\"> </ng-template>\n\t\t}\n\n\t\t@if ((!multiLabelTemplate() || !multiple()) && selectedItems.length > 0) {\n\t\t\t@for (item of selectedItems; track trackByOption($index, item)) {\n\t\t\t\t<div [class.ng-value-disabled]=\"item.disabled\" class=\"ng-value\">\n\t\t\t\t\t<ng-template #defaultLabelTemplate>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"ng-value-icon left\"\n\t\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\t\ttabindex=\"0\"\n\t\t\t\t\t\t\t[attr.aria-label]=\"(removeText() ?? config.removeText) + ' ' + item.label\"\n\t\t\t\t\t\t\t(click)=\"unselect(item)\"\n\t\t\t\t\t\t\t(keydown)=\"handleRemoveKeydown($event, item)\"\n\t\t\t\t\t\t\t>\u00D7</span\n\t\t\t\t\t\t>\n\t\t\t\t\t\t<span class=\"ng-value-label\">{{ item.label }}</span>\n\t\t\t\t\t</ng-template>\n\t\t\t\t\t<ng-template\n\t\t\t\t\t\t[ngTemplateOutlet]=\"labelTemplate() || defaultLabelTemplate\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ item: item.value, clear: clearItem, label: item.label }\">\n\t\t\t\t\t</ng-template>\n\t\t\t\t</div>\n\t\t\t}\n\t\t}\n\n\t\t@if (multiple() && multiLabelTemplate() && selectedValues.length > 0) {\n\t\t\t<ng-template [ngTemplateOutlet]=\"multiLabelTemplate()\" [ngTemplateOutletContext]=\"{ items: selectedValues, clear: clearItem }\"> </ng-template>\n\t\t}\n\n\t\t<div class=\"ng-input\">\n\t\t\t<input\n\t\t\t\t#searchInput\n\t\t\t\t(blur)=\"onInputBlur($event)\"\n\t\t\t\t(change)=\"$event.stopPropagation()\"\n\t\t\t\t(compositionend)=\"onCompositionEnd(searchInput.value)\"\n\t\t\t\t(compositionstart)=\"onCompositionStart()\"\n\t\t\t\t(focus)=\"onInputFocus($event)\"\n\t\t\t\t(input)=\"filter(searchInput.value)\"\n\t\t\t\t[attr.aria-activedescendant]=\"isOpen() ? itemsList?.markedItem?.htmlId : null\"\n\t\t\t\t[attr.aria-controls]=\"isOpen() ? dropdownId : null\"\n\t\t\t\t[attr.aria-expanded]=\"isOpen()\"\n\t\t\t\t[attr.aria-label]=\"ariaLabel()\"\n\t\t\t\t[attr.id]=\"labelForId()\"\n\t\t\t\t[attr.tabindex]=\"tabIndex()\"\n\t\t\t\t[disabled]=\"disabled()\"\n\t\t\t\t[readOnly]=\"!searchable() || itemsList.maxItemsSelected\"\n\t\t\t\t[value]=\"searchTerm ?? ''\"\n\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\trole=\"combobox\" />\n\t\t</div>\n\t</div>\n\n\t@if (loading()) {\n\t\t<ng-template #defaultLoadingSpinnerTemplate>\n\t\t\t<div class=\"ng-spinner-loader\"></div>\n\t\t</ng-template>\n\t\t<ng-template [ngTemplateOutlet]=\"loadingSpinnerTemplate() || defaultLoadingSpinnerTemplate\"></ng-template>\n\t}\n\n\t@if (showClear()) {\n\t\t<span\n\t\t\tclass=\"ng-clear-wrapper\"\n\t\t\trole=\"button\"\n\t\t\ttabindex=\"0\"\n\t\t\t[attr.tabindex]=\"tabFocusOnClear() ? 0 : -1\"\n\t\t\t[attr.aria-label]=\"clearAllText() || config.clearAllText\"\n\t\t\ttitle=\"{{ clearAllText() || config.clearAllText }}\"\n\t\t\t#clearButton\n\t\t\t(click)=\"handleClearClick($event)\">\n\t\t\t@if (clearButtonTemplate()) {\n\t\t\t\t<ng-container [ngTemplateOutlet]=\"clearButtonTemplate()\"></ng-container>\n\t\t\t} @else {\n\t\t\t\t<span class=\"ng-clear\" aria-hidden=\"true\">\u00D7</span>\n\t\t\t}\n\t\t</span>\n\t}\n\n\t<span class=\"ng-arrow-wrapper\">\n\t\t<span class=\"ng-arrow\"></span>\n\t</span>\n\n\t@if (appearance() === 'outline') {\n\t\t<div class=\"ng-notched-outline\" aria-hidden=\"true\">\n\t\t\t<div class=\"ng-notched-outline-leading\"></div>\n\t\t\t<div class=\"ng-notched-outline-notch\" [style.width.px]=\"outlineNotchWidth()\"></div>\n\t\t\t<div class=\"ng-notched-outline-trailing\"></div>\n\t\t</div>\n\t}\n</div>\n\n<!-- Rendered through a TemplatePortal into the CDK overlay while the select is open;\n the embedded view stays part of this component's view tree, only the DOM moves. -->\n<ng-template #dropdownTemplate>\n\t<ng-dropdown-panel\n\t\tclass=\"ng-dropdown-panel\"\n\t\t[virtualScroll]=\"dropdownVirtualScroll()\"\n\t\t[bufferAmount]=\"bufferAmount()\"\n\t\t[position]=\"dropdownPosition()\"\n\t\t[outsideClickEvent]=\"outsideClickEvent()\"\n\t\t[headerTemplate]=\"headerTemplate()\"\n\t\t[footerTemplate]=\"footerTemplate()\"\n\t\t[filterValue]=\"searchTerm\"\n\t\t[items]=\"itemsList.filteredItems\"\n\t\t[showAddTag]=\"showAddTag\"\n\t\t[markedItem]=\"itemsList.markedItem\"\n\t\t(update)=\"viewPortItems = $event\"\n\t\t(scroll)=\"scroll.emit($event)\"\n\t\t(scrollToEnd)=\"scrollToEnd.emit($event)\"\n\t\t(outsideClick)=\"close()\"\n\t\t[class.ng-select-multiple]=\"multiple()\"\n\t\t[class]=\"ngClass() ? ngClass() : classes\"\n\t\t[listboxId]=\"dropdownId\"\n\t\t[ariaLabelDropdown]=\"ariaLabelDropdown() ?? config.ariaLabelDropdown\"\n\t\t[overlayRef]=\"dropdownOverlayRef\"\n\t\t[selectElement]=\"element\">\n\t\t<ng-container>\n\t\t\t@for (item of viewPortItems; track trackByOption($index, item)) {\n\t\t\t\t<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus, @angular-eslint/template/mouse-events-have-key-events -->\n\t\t\t\t<div\n\t\t\t\t\t[attr.role]=\"item.children ? 'group' : 'option'\"\n\t\t\t\t\t(click)=\"toggleItem(item)\"\n\t\t\t\t\t(mouseover)=\"onItemHover(item)\"\n\t\t\t\t\t[ngClass]=\"item.classes\"\n\t\t\t\t\t[class.ng-option-disabled]=\"item.disabled\"\n\t\t\t\t\t[class.ng-option-selected]=\"item.selected\"\n\t\t\t\t\t[class.ng-optgroup]=\"item.children\"\n\t\t\t\t\t[class.ng-option]=\"!item.children\"\n\t\t\t\t\t[class.ng-option-child]=\"!!item.parent\"\n\t\t\t\t\t[class.ng-option-marked]=\"item === itemsList.markedItem\"\n\t\t\t\t\t[attr.aria-selected]=\"item.selected\"\n\t\t\t\t\t[attr.id]=\"item?.htmlId\"\n\t\t\t\t\t[attr.aria-setsize]=\"itemsList.filteredItems.length\"\n\t\t\t\t\t[attr.aria-posinset]=\"itemsList.filteredItems.indexOf(item) + 1 || null\">\n\t\t\t\t\t<ng-template #defaultOptionTemplate>\n\t\t\t\t\t\t<span class=\"ng-option-label\">{{ item.label }}</span>\n\t\t\t\t\t</ng-template>\n\t\t\t\t\t<ng-template\n\t\t\t\t\t\t[ngTemplateOutlet]=\"item.children ? optgroupTemplate() || defaultOptionTemplate : optionTemplate() || defaultOptionTemplate\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ item: item.value, item$: item, index: item.index, searchTerm: searchTerm }\">\n\t\t\t\t\t</ng-template>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@if (showAddTag) {\n\t\t\t\t<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus, @angular-eslint/template/mouse-events-have-key-events -->\n\t\t\t\t<div\n\t\t\t\t\tclass=\"ng-option\"\n\t\t\t\t\t[class.ng-option-marked]=\"!itemsList.markedItem\"\n\t\t\t\t\t(mouseover)=\"itemsList.unmarkItem()\"\n\t\t\t\t\trole=\"option\"\n\t\t\t\t\t[attr.aria-selected]=\"false\"\n\t\t\t\t\t(click)=\"selectTag()\">\n\t\t\t\t\t<ng-template #defaultTagTemplate>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t><span class=\"ng-tag-label\">{{ addTagText() || config.addTagText }}</span\n\t\t\t\t\t\t\t>\"{{ searchTerm }}\"</span\n\t\t\t\t\t\t>\n\t\t\t\t\t</ng-template>\n\t\t\t\t\t<ng-template [ngTemplateOutlet]=\"tagTemplate() || defaultTagTemplate\" [ngTemplateOutletContext]=\"{ searchTerm: searchTerm }\"> </ng-template>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</ng-container>\n\t\t@if (showNoItemsFound()) {\n\t\t\t<ng-template #defaultNotFoundTemplate>\n\t\t\t\t<div class=\"ng-option ng-option-disabled\">{{ notFoundText() ?? config.notFoundText }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"notFoundTemplate() || defaultNotFoundTemplate\" [ngTemplateOutletContext]=\"{ searchTerm: searchTerm }\">\n\t\t\t</ng-template>\n\t\t}\n\t\t@if (showTypeToSearch()) {\n\t\t\t<ng-template #defaultTypeToSearchTemplate>\n\t\t\t\t<div class=\"ng-option ng-option-disabled\">{{ typeToSearchText() || config.typeToSearchText }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"typeToSearchTemplate() || defaultTypeToSearchTemplate\"></ng-template>\n\t\t}\n\t\t@if (loading() && itemsList.filteredItems.length === 0) {\n\t\t\t<ng-template #defaultLoadingTextTemplate>\n\t\t\t\t<div class=\"ng-option ng-option-disabled\">{{ loadingText() || config.loadingText }}</div>\n\t\t\t</ng-template>\n\t\t\t<ng-template [ngTemplateOutlet]=\"loadingTextTemplate() || defaultLoadingTextTemplate\" [ngTemplateOutletContext]=\"{ searchTerm: searchTerm }\">\n\t\t\t</ng-template>\n\t\t}\n\t</ng-dropdown-panel>\n</ng-template>\n\n<!-- Always present aria-live region -->\n<div aria-atomic=\"true\" aria-live=\"polite\" class=\"ng-visually-hidden\" role=\"status\">\n\t@if (isOpen()) {\n\t\t@if (showNoItemsFound()) {\n\t\t\t{{ notFoundText() ?? config.notFoundText }}\n\t\t} @else if (showTypeToSearch()) {\n\t\t\t{{ typeToSearchText() || config.typeToSearchText }}\n\t\t} @else if (loading() && itemsList.filteredItems.length === 0) {\n\t\t\t{{ loadingText() || config.loadingText }}\n\t\t} @else if (itemsList.markedItem) {\n\t\t\t{{ itemsList.markedItem.label }}\n\t\t}\n\t}\n</div>\n", styles: ["@charset \"UTF-8\";.ng-select{position:relative;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ng-select div,.ng-select input,.ng-select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.ng-select [hidden]{display:none}.ng-select.ng-select-searchable .ng-select-container .ng-value-container .ng-input{opacity:1}.ng-select:not(.ng-select-searchable) .ng-select-container .ng-value-container .ng-input{pointer-events:none}.ng-select.ng-select-opened .ng-select-container{z-index:1001}.ng-select.ng-select-disabled .ng-select-container .ng-value-container .ng-placeholder,.ng-select.ng-select-disabled .ng-select-container .ng-value-container .ng-value{-webkit-user-select:none;user-select:none;cursor:default}.ng-select.ng-select-disabled .ng-arrow-wrapper{cursor:default}.ng-select.ng-select-filtered .ng-placeholder{display:none}.ng-select .ng-select-container{cursor:default;display:flex;outline:none;overflow:hidden;position:relative;width:100%}.ng-select .ng-select-container .ng-value-container{display:flex;flex:1}.ng-select .ng-select-container .ng-value-container .ng-input{opacity:0}.ng-select .ng-select-container .ng-value-container .ng-input>input{box-sizing:content-box;background:none transparent;border:0 none;box-shadow:none;outline:none;padding:0;cursor:default;width:100%}.ng-select .ng-select-container .ng-value-container .ng-input>input::-ms-clear{display:none}.ng-select .ng-select-container .ng-value-container .ng-input>input[readonly]{-webkit-user-select:unset;user-select:unset;width:0;padding:0}.ng-select.ng-select-single.ng-select-filtered .ng-select-container .ng-value-container .ng-value{visibility:hidden}.ng-select.ng-select-single .ng-select-container .ng-value-container,.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-value .ng-value-icon{display:none}.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{position:absolute;left:0;width:100%}.ng-select.ng-select-multiple.ng-select-disabled>.ng-select-container .ng-value-container .ng-value .ng-value-icon{display:none}.ng-select.ng-select-multiple .ng-select-container .ng-value-container{flex-wrap:wrap}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder{position:absolute}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value{white-space:nowrap}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value.ng-value-disabled .ng-value-icon{display:none}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon{cursor:pointer}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-input{flex:1;z-index:2}.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder{z-index:1}.ng-select .ng-clear-wrapper{cursor:pointer;position:relative;width:17px;-webkit-user-select:none;user-select:none}.ng-select .ng-clear-wrapper .ng-clear{display:inline-block;font-size:18px;line-height:1;pointer-events:none}.ng-select .ng-spinner-loader{border-radius:50%;width:17px;height:17px;margin-right:5px;font-size:10px;position:relative;text-indent:-9999em;border-top:2px solid rgba(66,66,66,.2);border-right:2px solid rgba(66,66,66,.2);border-bottom:2px solid rgba(66,66,66,.2);border-left:2px solid #424242;transform:translateZ(0);animation:load8 .8s infinite linear}.ng-select .ng-spinner-loader:after{border-radius:50%;width:17px;height:17px}@-webkit-keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.ng-select .ng-arrow-wrapper{cursor:pointer;position:relative;text-align:center;-webkit-user-select:none;user-select:none}.ng-select .ng-arrow-wrapper .ng-arrow{pointer-events:none;display:inline-block;height:0;width:0;position:relative}.ng-dropdown-panel{box-sizing:border-box;position:relative;opacity:0;width:100%;-webkit-overflow-scrolling:touch}.ng-dropdown-panel .ng-dropdown-panel-items{display:block;height:auto;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;max-height:240px;overflow-y:auto}.ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option{box-sizing:border-box;cursor:pointer;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option .ng-option-label:empty:before{content:\"\\200b\"}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option .highlighted{font-weight:700;text-decoration:underline}.ng-dropdown-panel .ng-dropdown-panel-items .ng-option.disabled{cursor:default}.ng-dropdown-panel .scroll-host{overflow:hidden;overflow-y:auto;position:relative;display:block;-webkit-overflow-scrolling:touch}.ng-dropdown-panel .scrollable-content{top:0;left:0;width:100%;height:100%;position:absolute}.ng-dropdown-panel .total-padding{width:1px;opacity:0}.cdk-overlay-pane .ng-dropdown-panel{position:relative;inset:auto}.ng-visually-hidden{position:absolute!important;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);border:0;white-space:nowrap}\n"] }]
}], ctorParameters: () => [], propDecorators: { _ariaLabelDropdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelDropdown", required: false }] }], _ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], _markFirst: [{ type: i0.Input, args: [{ isSignal: true, alias: "markFirst", required: false }] }], _placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], _fixedPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "fixedPlaceholder", required: false }] }], _notFoundText: [{ type: i0.Input, args: [{ isSignal: true, alias: "notFoundText", required: false }] }], _typeToSearchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeToSearchText", required: false }] }], _preventToggleOnRightClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventToggleOnRightClick", required: false }] }], _addTagText: [{ type: i0.Input, args: [{ isSignal: true, alias: "addTagText", required: false }] }], _loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], _clearAllText: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAllText", required: false }] }], _removeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeText", required: false }] }], _dropdownPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownPosition", required: false }] }], _appendTo: [{ type: i0.Input, args: [{ isSignal: true, alias: "appendTo", required: false }] }], _outsideClickEvent: [{ type: i0.Input, args: [{ isSignal: true, alias: "outsideClickEvent", required: false }] }], _loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], _closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], _hideSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideSelected", required: false }] }], _selectOnTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectOnTab", required: false }] }], _openOnEnter: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOnEnter", required: false }] }], _maxSelectedItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectedItems", required: false }] }], _groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], _groupValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupValue", required: false }] }], _bufferAmount: [{ type: i0.Input, args: [{ isSignal: true, alias: "bufferAmount", required: false }] }], _virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], _selectableGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectableGroup", required: false }] }], _tabFocusOnClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabFocusOnClearButton", required: false }] }], _selectableGroupAsModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectableGroupAsModel", required: false }] }], _searchFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchFn", required: false }] }], _trackByFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByFn", required: false }] }], _clearOnBackspace: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearOnBackspace", required: false }] }], _labelForId: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelForId", required: false }] }], _inputAttrs: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputAttrs", required: false }] }], _tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], _readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], _searchWhileComposing: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchWhileComposing", required: false }] }], _minTermLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minTermLength", required: false }] }], _editableSearchTerm: [{ type: i0.Input, args: [{ isSignal: true, alias: "editableSearchTerm", required: false }] }], _ngClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngClass", required: false }] }], _typeahead: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeahead", required: false }] }], _multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], _addTag: [{ type: i0.Input, args: [{ isSignal: true, alias: "addTag", required: false }] }], _searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], _clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], _clearKeepsDisabledOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearKeepsDisabledOptions", required: false }] }], _deselectOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "deselectOnClick", required: false }] }], _clearSearchOnAdd: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearSearchOnAdd", required: false }] }], _compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], _keyDownFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyDownFn", required: false }] }], _popover: [{ type: i0.Input, args: [{ isSignal: true, alias: "popover", required: false }] }], bindLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindLabel", required: false }] }, { type: i0.Output, args: ["bindLabelChange"] }], bindValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindValue", required: false }] }, { type: i0.Output, args: ["bindValueChange"] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }, { type: i0.Output, args: ["appearanceChange"] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }, { type: i0.Output, args: ["itemsChange"] }], blurEvent: [{ type: i0.Output, args: ["blur"] }], focusEvent: [{ type: i0.Output, args: ["focus"] }], changeEvent: [{ type: i0.Output, args: ["change"] }], openEvent: [{ type: i0.Output, args: ["open"] }], closeEvent: [{ type: i0.Output, args: ["close"] }], searchEvent: [{ type: i0.Output, args: ["search"] }], clearEvent: [{ type: i0.Output, args: ["clear"] }], addEvent: [{ type: i0.Output, args: ["add"] }], removeEvent: [{ type: i0.Output, args: ["remove"] }], scroll: [{ type: i0.Output, args: ["scroll"] }], scrollToEnd: [{ type: i0.Output, args: ["scrollToEnd"] }], optionTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgOptionTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], optgroupTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgOptgroupTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], labelTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgLabelTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], multiLabelTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgMultiLabelTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], headerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgHeaderTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], footerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgFooterTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], notFoundTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgNotFoundTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], placeholderTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgPlaceholderTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], typeToSearchTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgTypeToSearchTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], loadingTextTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgLoadingTextTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], tagTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgTagTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], loadingSpinnerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgLoadingSpinnerTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], clearButtonTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgClearButtonTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], ngOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NgOptionComponent), { ...{ descendants: true }, isSignal: true }] }], dropdownPanel: [{ type: i0.ViewChild, args: [forwardRef(() => NgDropdownPanelComponent), { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], clearButton: [{ type: i0.ViewChild, args: ['clearButton', { isSignal: true }] }], _dropdownTemplate: [{ type: i0.ViewChild, args: ['dropdownTemplate', { isSignal: true }] }], _dropdownOutlet: [{ type: i0.ViewChild, args: ['dropdownTemplate', { ...{ read: ViewContainerRef }, isSignal: true }] }], _selectContainer: [{ type: i0.ViewChild, args: ['selectContainer', { isSignal: true }] }], handleKeyDown: [{
type: HostListener,
args: ['keydown', ['$event']]
}] } });
class NgSelectModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: NgSelectModule, imports: [NgDropdownPanelComponent,
NgOptionComponent,
NgSelectComponent,
NgOptgroupTemplateDirective,
NgOptionTemplateDirective,
NgLabelTemplateDirective,
NgMultiLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgPlaceholderTemplateDirective,
NgClearButtonTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective,
NgTagTemplateDirective,
NgLoadingSpinnerTemplateDirective,
NgItemLabelDirective], exports: [NgSelectComponent,
NgOptionComponent,
NgOptgroupTemplateDirective,
NgOptionTemplateDirective,
NgLabelTemplateDirective,
NgMultiLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgPlaceholderTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective,
NgTagTemplateDirective,
NgLoadingSpinnerTemplateDirective,
NgClearButtonTemplateDirective] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectModule, providers: provideNgSelect() }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: NgSelectModule, decorators: [{
type: NgModule,
args: [{
imports: [
NgDropdownPanelComponent,
NgOptionComponent,
NgSelectComponent,
NgOptgroupTemplateDirective,
NgOptionTemplateDirective,
NgLabelTemplateDirective,
NgMultiLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgPlaceholderTemplateDirective,
NgClearButtonTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective,
NgTagTemplateDirective,
NgLoadingSpinnerTemplateDirective,
NgItemLabelDirective,
],
exports: [
NgSelectComponent,
NgOptionComponent,
NgOptgroupTemplateDirective,
NgOptionTemplateDirective,
NgLabelTemplateDirective,
NgMultiLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgPlaceholderTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective,
NgTagTemplateDirective,
NgLoadingSpinnerTemplateDirective,
NgClearButtonTemplateDirective,
],
providers: provideNgSelect(),
}]
}] });
function provideNgSelect() {
return [
{
provide: SELECTION_MODEL_FACTORY,
useValue: DefaultSelectionModelFactory,
},
];
}
/*
* Public API Surface of ng-select
*/
/**
* Generated bundle index. Do not edit.
*/
export { ConsoleService, DefaultSelectionModel, DefaultSelectionModelFactory, NgClearButtonTemplateDirective, NgDropdownPanelComponent, NgDropdownPanelService, NgFooterTemplateDirective, NgHeaderTemplateDirective, NgItemLabelDirective, NgLabelTemplateDirective, NgLoadingSpinnerTemplateDirective, NgLoadingTextTemplateDirective, NgMultiLabelTemplateDirective, NgNotFoundTemplateDirective, NgOptgroupTemplateDirective, NgOptionComponent, NgOptionTemplateDirective, NgPlaceholderTemplateDirective, NgSelectComponent, NgSelectConfig, NgSelectModule, NgTagTemplateDirective, NgTypeToSearchTemplateDirective, SELECTION_MODEL_FACTORY };
//# sourceMappingURL=ng-select-ng-select.mjs.map