ng-select2-component
Version:
An Angular select2 component.
955 lines (946 loc) • 188 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, ElementRef, input, signal, Directive, booleanAttribute, contentChildren, Pipe, ChangeDetectorRef, numberAttribute, computed, output, viewChild, viewChildren, effect, untracked, TemplateRef, Component } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { moveItemInArray, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
import { CdkConnectedOverlay, ConnectionPositionPair, CdkOverlayOrigin } from '@angular/cdk/overlay';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { NgTemplateOutlet } from '@angular/common';
import { toObservable } from '@angular/core/rxjs-interop';
import { NgForm, FormGroupDirective, NgControl } from '@angular/forms';
import { Subject, Subscription, fromEvent, debounceTime } from 'rxjs';
const timeout = 200;
/**
* Latin - `[\u0300-\u036F]` matches combining diacritical marks:
* - `\u0300-\u036F`: grave, acute, circumflex, tilde, macron, breve, dot above, diaeresis, ring above, etc.
*/
const latinDiacritical = { tmp: '\uE000', pattern: '[\\u0300-\\u036F]*' };
/**
* Arabic - `[\u064B-\u0652\u0670]` matches Arabic diacritics (harakat):
* - `\u064B-\u0652`: fatha, damma, kasra, sukun, shadda, tanwin, etc.
* - `\u0670`: alif khanjariyah (superscript alif)
*/
const arabicDiacritical = { tmp: '\uE001', pattern: '[\\u064B-\\u0652\\u0670]*' };
/**
* Hebrew - `[\u05B0-\u05BC\u05C1\u05C2]` matches Hebrew diacritics (niqqud):
* - `\u05B0-\u05BC`: vowel points (sheva, hataf, hiriq, tsere, segol, patah, qamats, holam, qubuts, dagesh, etc.)
* - `\u05C1-\u05C2`: shin dot (right) and sin dot (left)
*/
const hebrewDiacritical = { tmp: '\uE002', pattern: '[\\u05B0-\\u05BC\\u05C1\\u05C2]*' };
const unicodePatterns = [
// Latin
{ l: 'a', s: /[ⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ]/gi, e: 'a(?![aeouvy])', d: latinDiacritical },
{ l: 'aa', s: /ꜳ/gi, d: latinDiacritical },
{ l: 'ae', s: /[æǽǣ]/gi, d: latinDiacritical },
{ l: 'ao', s: /ꜵ/gi, d: latinDiacritical },
{ l: 'au', s: /ꜷ/gi, d: latinDiacritical },
{ l: 'av', s: /[ꜹꜻ]/gi, d: latinDiacritical },
{ l: 'ay', s: /ꜽ/gi, d: latinDiacritical },
{ l: 'b', s: /[ⓑbḃḅḇƀƃɓ]/gi, d: latinDiacritical },
{ l: 'c', s: /[ⓒcćĉċčçḉƈȼꜿↄ]/gi, d: latinDiacritical },
{ l: 'd', s: /[ⓓdḋďḍḑḓḏđƌɖɗꝺ]/gi, e: 'd(?!z)', d: latinDiacritical },
{ l: 'dz', s: /[dzdž]/gi, d: latinDiacritical },
{ l: 'e', s: /(?<!o)[eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ]/gi, e: '(?<!o)e', d: latinDiacritical },
{ l: 'f', s: /[ⓕfḟƒꝼ]/gi, d: latinDiacritical },
{ l: 'g', s: /[ⓖgǵĝḡğġǧģǥɠꞡᵹꝿ]/gi, d: latinDiacritical },
{ l: 'h', s: /[ⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ]/gi, e: 'h(?!v)', d: latinDiacritical },
{ l: 'hv', s: /ƕ/gi, d: latinDiacritical },
{ l: 'i', s: /[ⓘiìíîĩīĭİïḯỉǐȉȋịįḭɨı]/gi, d: latinDiacritical },
{ l: 'j', s: /[ⓙjĵǰɉ]/gi, e: '(?<![ln])j', d: latinDiacritical },
{ l: 'k', s: /[ⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ]/gi, d: latinDiacritical },
{ l: 'l', s: /[ⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇꝆ]/gi, e: 'l(?!j)', d: latinDiacritical },
{ l: 'lj', s: /lj/gi, d: latinDiacritical },
{ l: 'm', s: /[ⓜmḿṁṃɱɯ]/gi, d: latinDiacritical },
{ l: 'n', s: /[ⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ]/gi, e: 'n(?!j)', d: latinDiacritical },
{ l: 'nj', s: /nj/gi, d: latinDiacritical },
{ l: 'o', s: /[ⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔƟꝋꝍɵ]/gi, e: 'o(?![ieou])', d: latinDiacritical },
{ l: 'oi', s: /ƣ/gi, d: latinDiacritical },
{ l: 'oe', s: /œ/gi, d: latinDiacritical },
{ l: 'oo', s: /ꝏ/gi, d: latinDiacritical },
{ l: 'ou', s: /ȣ/gi, d: latinDiacritical },
{ l: 'p', s: /[ⓟpṕṗƥᵽꝑꝓꝕ]/gi, d: latinDiacritical },
{ l: 'q', s: /[ⓠqɋꝗꝙ]/gi, d: latinDiacritical },
{ l: 'r', s: /[ⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ]/gi, d: latinDiacritical },
{ l: 's', s: /[ⓢsßẞśṥŝṡšṧṣṩșşȿꞩꞅẛ]/gi, d: latinDiacritical },
{ l: 't', s: /[ⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ]/gi, e: 't(?!z)', d: latinDiacritical },
{ l: 'tz', s: /ꜩ/gi, d: latinDiacritical },
{ l: 'u', s: /[ⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ]/gi, d: latinDiacritical },
{ l: 'v', s: /[ⓥvṽṿʋꝟʌ]/gi, e: 'v(?!y)', d: latinDiacritical },
{ l: 'vy', s: /ꝡ/gi, d: latinDiacritical },
{ l: 'w', s: /[ⓦwẁẃŵẇẅẘẉⱳ]/gi, d: latinDiacritical },
{ l: 'x', s: /[ⓧxẋẍ]/gi, d: latinDiacritical },
{ l: 'y', s: /[ⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ]/gi, d: latinDiacritical },
{ l: 'z', s: /[ⓩzźẑżžẓẕƶȥɀⱬꝣ]/gi, d: latinDiacritical },
// Japanese - Hiragana/Katakana
{ l: 'あ', s: /[あアぁァ]゙?/gi },
{ l: 'い', s: /[いイぃィ]゙?/gi },
{ l: 'う', s: /[うウぅゥゔヴ]゙?/gi },
{ l: 'え', s: /[えエぇェ]゙?/gi },
{ l: 'お', s: /[おオぉォ]゙?/gi },
{ l: 'か', s: /[かカがガゕヵ]゙?/gi },
{ l: 'き', s: /[きキぎギ]゙?/gi },
{ l: 'く', s: /[くクぐグ]゙?/gi },
{ l: 'け', s: /[けケげゲゖヶ]゙?/gi },
{ l: 'こ', s: /[こコごゴ]゙?/gi },
{ l: 'さ', s: /[さサざザ]゙?/gi },
{ l: 'し', s: /[しシじジ]゙?/gi },
{ l: 'す', s: /[すスずズ]゙?/gi },
{ l: 'せ', s: /[せセぜゼ]゙?/gi },
{ l: 'そ', s: /[そソぞゾ]゙?/gi },
{ l: 'た', s: /[たタだダ]゙?/gi },
{ l: 'ち', s: /[ちチぢヂ]゙?/gi },
{ l: 'つ', s: /[つツっッづヅ]゙?/gi },
{ l: 'て', s: /[てテでデ]゙?/gi },
{ l: 'と', s: /[とトどド]゙?/gi },
{ l: 'な', s: /ナ/gi },
{ l: 'に', s: /ニ/gi },
{ l: 'ぬ', s: /ヌ/gi },
{ l: 'ね', s: /ネ/gi },
{ l: 'の', s: /ノ/gi },
{ l: 'は', s: /[はハばバぱパ][゙゚]?/gi },
{ l: 'ひ', s: /[ひヒびビぴピ][゙゚]?/gi },
{ l: 'ふ', s: /[ふフぶブぷプ][゙゚]?/gi },
{ l: 'へ', s: /[へヘべベぺペ][゙゚]?/gi },
{ l: 'ほ', s: /[ほホぼボぽポ][゙゚]?/gi },
{ l: 'ま', s: /マ/gi },
{ l: 'み', s: /ミ/gi },
{ l: 'む', s: /ム/gi },
{ l: 'め', s: /メ/gi },
{ l: 'も', s: /モ/gi },
{ l: 'や', s: /[ヤゃャ]/gi },
{ l: 'ゆ', s: /[ユゅュ]/gi },
{ l: 'よ', s: /[ヨょョ]/gi },
{ l: 'ら', s: /ラ/gi },
{ l: 'り', s: /リ/gi },
{ l: 'る', s: /ル/gi },
{ l: 'れ', s: /レ/gi },
{ l: 'ろ', s: /ロ/gi },
{ l: 'わ', s: /[わワゎヮヷ]゙?/gi },
{ l: 'ゐ', s: /[ゐヰヸ]゙?/gi },
{ l: 'ゑ', s: /[ゑヱヹ]゙?/gi },
{ l: 'を', s: /[をヲヺ]゙?/gi },
{ l: 'ん', s: /[ン]/gi },
// Cyrillic
{ l: 'а', s: /[аӐӑӒӓӔӕ]/gi },
{ l: 'г', s: /[гѓґҐғҒҕҔ]/gi },
{ l: 'д', s: /[дђѐ]/gi },
{ l: 'е', s: /[еѐёЁӖӗ]/gi },
{ l: 'ж', s: /[жӁӂӜӝ]/gi },
{ l: 'з', s: /[зӞӟ]/gi },
{ l: 'и', s: /[иѝӢӣӤӥ]/gi },
{ l: 'й', s: /[йЙ]/gi },
{ l: 'к', s: /[кќҚқҜҝҞҟҠҡ]/gi },
{ l: 'л', s: /[лљЉ]/gi },
{ l: 'н', s: /[нњЊҢңӉӊ]/gi },
{ l: 'о', s: /[оӦӧӨӫ]/gi },
{ l: 'с', s: /[сҪҫ]/gi },
{ l: 'т', s: /[тћЋ]/gi },
{ l: 'у', s: /[уўЎӮӯӰӱӲӳ]/gi },
{ l: 'х', s: /[хҲҳӇӈ]/gi },
{ l: 'ч', s: /[чҴҵӴӵ]/gi },
{ l: 'ы', s: /[ыӸӹ]/gi },
// Greek
{ l: 'α', s: /[αάἀἁἂἃἄἅἆἇὰάᾀᾁᾂᾃᾄᾅᾆᾇᾰᾱᾲᾳᾴᾶᾷ]/gi },
{ l: 'β', s: /[βϐ]/gi },
{ l: 'η', s: /[ηήἠἡἢἣἤἥἦἧὴήᾐᾑᾒᾓᾔᾕᾖᾗῂῃῄῆῇ]/gi },
{ l: 'θ', s: /[θϑ]/gi },
{ l: 'ι', s: /[ιίϊΐἰἱἲἳἴἵἶἷὶίῐῑῒΐῖῗ]/gi },
{ l: 'κ', s: /[κϰ]/gi },
{ l: 'ο', s: /[οόὀὁὂὃὄὅὸό]/gi },
{ l: 'π', s: /[πϖ]/gi },
{ l: 'ρ', s: /[ρϱῤῥ]/gi },
{ l: 'σ', s: /[σςϲ]/gi },
{ l: 'υ', s: /[υύϋΰὐὑὒὓὔὕὖὗὺύῠῡῢΰῦῧ]/gi },
{ l: 'φ', s: /[φϕ]/gi },
{ l: 'ω', s: /[ωώὠὡὢὣὤὥὦὧὼώᾠᾡᾢᾣᾤᾥᾦᾧῲῳῴῶῷ]/gi },
// Arabic
{ l: 'ا', s: /[اأإآٱ]/gi, d: arabicDiacritical },
{ l: 'ب', s: /ب/gi, d: arabicDiacritical },
{ l: 'ت', s: /[تة]/gi, d: arabicDiacritical },
{ l: 'ث', s: /ث/gi, d: arabicDiacritical },
{ l: 'ج', s: /ج/gi, d: arabicDiacritical },
{ l: 'ح', s: /ح/gi, d: arabicDiacritical },
{ l: 'خ', s: /خ/gi, d: arabicDiacritical },
{ l: 'د', s: /د/gi, d: arabicDiacritical },
{ l: 'ذ', s: /ذ/gi, d: arabicDiacritical },
{ l: 'ر', s: /ر/gi, d: arabicDiacritical },
{ l: 'ز', s: /ز/gi, d: arabicDiacritical },
{ l: 'س', s: /س/gi, d: arabicDiacritical },
{ l: 'ش', s: /ش/gi, d: arabicDiacritical },
{ l: 'ص', s: /ص/gi, d: arabicDiacritical },
{ l: 'ض', s: /ض/gi, d: arabicDiacritical },
{ l: 'ط', s: /ط/gi, d: arabicDiacritical },
{ l: 'ظ', s: /ظ/gi, d: arabicDiacritical },
{ l: 'ع', s: /ع/gi, d: arabicDiacritical },
{ l: 'غ', s: /غ/gi, d: arabicDiacritical },
{ l: 'ف', s: /ف/gi, d: arabicDiacritical },
{ l: 'ق', s: /ق/gi, d: arabicDiacritical },
{ l: 'ك', s: /[كک]/gi, d: arabicDiacritical },
{ l: 'ل', s: /ل/gi, d: arabicDiacritical },
{ l: 'م', s: /م/gi, d: arabicDiacritical },
{ l: 'ن', s: /ن/gi, d: arabicDiacritical },
{ l: 'ه', s: /[هە]/gi, d: arabicDiacritical },
{ l: 'و', s: /[وؤ]/gi, d: arabicDiacritical },
{ l: 'ي', s: /[يىئ]/gi, d: arabicDiacritical },
// Hebrew
{ l: 'א', s: /א/gi, d: hebrewDiacritical },
{ l: 'ב', s: /ב/gi, d: hebrewDiacritical },
{ l: 'ג', s: /ג/gi, d: hebrewDiacritical },
{ l: 'ד', s: /ד/gi, d: hebrewDiacritical },
{ l: 'ה', s: /ה/gi, d: hebrewDiacritical },
{ l: 'ו', s: /ו/gi, d: hebrewDiacritical },
{ l: 'ז', s: /ז/gi, d: hebrewDiacritical },
{ l: 'ח', s: /ח/gi, d: hebrewDiacritical },
{ l: 'ט', s: /ט/gi, d: hebrewDiacritical },
{ l: 'י', s: /י/gi, d: hebrewDiacritical },
{ l: 'כ', s: /[כך]/gi, d: hebrewDiacritical },
{ l: 'ל', s: /ל/gi, d: hebrewDiacritical },
{ l: 'מ', s: /[םמ]/gi, d: hebrewDiacritical },
{ l: 'נ', s: /[ןנ]/gi, d: hebrewDiacritical },
{ l: 'ס', s: /ס/gi, d: hebrewDiacritical },
{ l: 'ע', s: /ע/gi, d: hebrewDiacritical },
{ l: 'פ', s: /[פף]/gi, d: hebrewDiacritical },
{ l: 'צ', s: /[ץצ]/gi, d: hebrewDiacritical },
{ l: 'ק', s: /ק/gi, d: hebrewDiacritical },
{ l: 'ר', s: /ר/gi, d: hebrewDiacritical },
{ l: 'ש', s: /ש/gi, d: hebrewDiacritical },
{ l: 'ת', s: /ת/gi, d: hebrewDiacritical },
];
const defaultMinCountForSearch = 6;
const protectRegexp = new RegExp('[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]', 'g');
/**
* Shared base for the <ng-option> and <ng-group> directives.
*
* Holds the inputs common to both (classes, templateId, data, dir) and the projected-content
* reactivity: plain text content (interpolation in the element body) is not a tracked signal, so
* the host component dirty-checks it in ngDoCheck and pushes changes into {@link _projectedContent},
* which the rebuild effect depends on through {@link _resolveLabel}.
*/
class Select2ContentDirective {
constructor() {
this._elementRef = inject((ElementRef));
/** Additional CSS classes */
this.classes = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
/** Template id */
this.templateId = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "templateId" }] : /* istanbul ignore next */ []));
/** Arbitrary data attached to the option/group */
this.data = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
/** Force text direction */
this.dir = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
/**
* Reactive trigger for the projected text content (interpolation in the element body).
* The host component dirty-checks the DOM in its ngDoCheck and updates this signal when the
* rendered text changes, so the component's rebuild effect re-runs even though plain text
* content is not otherwise a tracked dependency.
*/
this._projectedContent = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_projectedContent" }] : /* istanbul ignore next */ []));
}
/** Read the host element's rendered text content (innerHTML, then textContent, then ''). */
_readContent() {
const el = this._elementRef.nativeElement;
return el.innerHTML?.trim() || el.textContent?.trim() || '';
}
/**
* Re-read the host element's rendered content and update {@link _projectedContent} when it
* changed. Returns true if a change was detected. Called from the host component's ngDoCheck.
*/
_refreshProjectedContent() {
const content = this._readContent();
if (content !== this._projectedContent()) {
this._projectedContent.set(content);
return true;
}
return false;
}
/**
* Resolve the label: prefer the explicit [label] input, then the cached projected content,
* then a one-off DOM read. Reading _projectedContent() registers it as a dependency of the
* rebuild effect so interpolation/content changes propagate; the cached value avoids a second
* DOM read, and _readContent() only runs on the initial pass.
*/
_resolveLabel(label) {
return label ?? this._projectedContent() ?? this._readContent();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2ContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: Select2ContentDirective, isStandalone: true, inputs: { classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, templateId: { classPropertyName: "templateId", publicName: "templateId", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2ContentDirective, decorators: [{
type: Directive
}], propDecorators: { classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], templateId: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateId", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }] } });
/**
* Directive representing a single option inside a <ng-select2> or <ng-group>.
*
* Usage:
* ```html
* <ng-select2>
* <ng-option value="foo">Foo</ng-option>
* <ng-option value="bar" [disabled]="true">Bar</ng-option>
* </ng-select2>
* ```
*/
class Select2OptionDirective extends Select2ContentDirective {
constructor() {
super(...arguments);
/** The option value */
this.value = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
/** Explicit label — falls back to the element's text content if omitted */
this.label = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
/** Whether the option is disabled */
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Template selection id */
this.templateSelectionId = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "templateSelectionId" }] : /* istanbul ignore next */ []));
/** Hide this option */
this.hide = input(false, { ...(ngDevMode ? { debugName: "hide" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
}
/** Build a plain Select2Option object from the current input values */
toOption() {
return {
value: this.value(),
label: this._resolveLabel(this.label()),
disabled: this.disabled() || undefined,
classes: this.classes(),
templateId: this.templateId(),
templateSelectionId: this.templateSelectionId(),
data: this.data(),
hide: this.hide() || undefined,
dir: this.dir(),
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2OptionDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: Select2OptionDirective, isStandalone: true, selector: "ng-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, templateSelectionId: { classPropertyName: "templateSelectionId", publicName: "templateSelectionId", isSignal: true, isRequired: false, transformFunction: null }, hide: { classPropertyName: "hide", publicName: "hide", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2OptionDirective, decorators: [{
type: Directive,
args: [{ selector: 'ng-option' }]
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], templateSelectionId: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateSelectionId", required: false }] }], hide: [{ type: i0.Input, args: [{ isSignal: true, alias: "hide", required: false }] }] } });
/**
* Directive representing an option group inside a <ng-select2>.
*
* Usage:
* ```html
* <ng-select2>
* <ng-group label="Fruits">
* <ng-option value="apple">Apple</ng-option>
* <ng-option value="banana">Banana</ng-option>
* </ng-group>
* </ng-select2>
* ```
*/
class Select2GroupDirective extends Select2ContentDirective {
constructor() {
super(...arguments);
/** The group label (required) */
this.label = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
/** Child <ng-option> directives nested inside this group */
this._ngOptions = contentChildren(Select2OptionDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_ngOptions" }] : /* istanbul ignore next */ []));
}
/** Build a plain Select2Group object from the current input values */
toGroup() {
return {
label: this._resolveLabel(this.label()),
options: this._ngOptions().map(o => o.toOption()),
classes: this.classes(),
templateId: this.templateId(),
data: this.data(),
dir: this.dir(),
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2GroupDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.1", type: Select2GroupDirective, isStandalone: true, selector: "ng-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, queries: [{ propertyName: "_ngOptions", predicate: Select2OptionDirective, isSignal: true }], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2GroupDirective, decorators: [{
type: Directive,
args: [{ selector: 'ng-group' }]
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], _ngOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => Select2OptionDirective), { isSignal: true }] }] } });
class Select2Utils {
static getOptionByValue(data, value) {
for (const groupOrOption of data) {
const options = groupOrOption.options;
if (options) {
for (const option of options) {
if (option.value === value) {
return option;
}
}
}
else if (groupOrOption.value === value) {
return groupOrOption;
}
}
return null;
}
static getOptionsByValue(data, value, multiple) {
if (multiple) {
const values = Array.isArray(value) ? value : [];
const result = [];
for (const v of values) {
const option = Select2Utils.getOptionByValue(data, v);
if (option) {
result.push(option);
}
}
return result;
}
return Select2Utils.getOptionByValue(data, value);
}
static getFirstAvailableOption(data) {
for (const groupOrOption of data) {
const options = groupOrOption.options;
if (options) {
for (const option of options) {
if (!option.disabled) {
return option;
}
}
}
else {
const option = groupOrOption;
if (!option.disabled) {
return option;
}
}
}
return null;
}
static optionIsNotInFilteredData(filteredData, option) {
if (Select2Utils.isNullOrUndefined(option)) {
return true;
}
for (const groupOrOption of filteredData) {
const options = groupOrOption.options;
if (options && options.includes(option)) {
return false;
}
else if (groupOrOption === option) {
return false;
}
}
return true;
}
static getPreviousOption(filteredData, hoveringOption) {
let findIt = Select2Utils.isNullOrUndefined(hoveringOption);
for (let i = filteredData.length - 1; i >= 0; i--) {
const groupOrOption = filteredData[i];
const options = groupOrOption.options;
if (options) {
for (let j = options.length - 1; j >= 0; j--) {
const option = options[j];
if (findIt && !option.disabled && !option.hide) {
return option;
}
if (!findIt) {
findIt = option === hoveringOption;
}
}
}
else {
const option = groupOrOption;
if (findIt && !option.disabled && !option.hide) {
return option;
}
if (!findIt) {
findIt = option === hoveringOption;
}
}
}
return null;
}
static getNextOption(filteredData, hoveringOption) {
let findIt = Select2Utils.isNullOrUndefined(hoveringOption);
if (filteredData) {
for (const groupOrOption of filteredData) {
const options = groupOrOption.options;
if (options) {
for (const option of options) {
if (findIt) {
if (!option.disabled && !option.hide) {
return option;
}
}
else {
findIt = option === hoveringOption;
}
}
}
else {
const option = groupOrOption;
if (findIt) {
if (!option.disabled && !option.hide) {
return option;
}
}
else {
findIt = option === hoveringOption;
}
}
}
}
return null;
}
static getFirstOption(filteredData) {
const firstElement = filteredData[0];
if (this.isOption(firstElement)) {
return firstElement;
}
else {
return firstElement.options[0] ?? null;
}
}
static getLastOption(filteredData) {
const lastElement = filteredData.at(-1);
if (!lastElement) {
return null;
}
if (this.isOption(lastElement)) {
return lastElement;
}
else {
return lastElement.options.at(-1) ?? null;
}
}
static isGroup(element) {
return !!element.options;
}
static isOption(element) {
return !this.isGroup(element);
}
static getReduceData(data, maxResults = 0) {
if (maxResults > 0) {
let counter = 0;
const result = [];
// debugger;
for (const groupOrOption of data) {
const options = groupOrOption.options;
if (options) {
const group = {
...groupOrOption,
options: [],
};
result.push(group);
for (const item of options) {
group.options.push(item);
counter++;
if (counter === maxResults) {
return { result, reduce: true };
}
}
}
else {
result.push(groupOrOption);
counter++;
}
if (counter === maxResults) {
return { result, reduce: true };
}
}
return { result, reduce: false };
}
else {
return { result: data, reduce: false };
}
}
static getFilteredData(data, searchText, editPattern) {
if (searchText) {
const result = [];
for (const groupOrOption of data) {
const options = groupOrOption.options;
if (options) {
if (options.some(group => Select2Utils.containSearchText(group.label, searchText, editPattern))) {
const filteredOptions = options.filter(group => Select2Utils.containSearchText(group.label, searchText, editPattern));
result.push({
...groupOrOption,
options: filteredOptions,
});
}
}
else if (Select2Utils.containSearchText(groupOrOption.label, searchText, editPattern)) {
result.push(groupOrOption);
}
}
return result;
}
else {
return data;
}
}
static getFilteredSelectedData(data, selectedOptions) {
const result = [];
for (const groupOrOption of data) {
const options = groupOrOption.options;
if (options) {
const filteredOptions = options.filter(group => Select2Utils.isSelected(selectedOptions, group, true) === 'false');
if (filteredOptions.length) {
result.push({
...groupOrOption,
options: filteredOptions,
});
}
}
else if (Select2Utils.isSelected(selectedOptions, groupOrOption, true) === 'false') {
result.push(groupOrOption);
}
}
return result;
}
static isSearchboxHidden(data, minCountForSearch) {
if (minCountForSearch === undefined || minCountForSearch === null || isNaN(+minCountForSearch)) {
minCountForSearch = defaultMinCountForSearch;
}
const optionCount = Select2Utils.getOptionsCount(data);
return optionCount < +minCountForSearch;
}
static isSelected(options, option, multiple) {
return multiple
? options && options.some(op => op.value === option.value)
? 'true'
: 'false'
: options && option.value === options.value
? 'true'
: 'false';
}
static removeSelection(options, option) {
for (let i = 0; i < options.length; i++) {
if (options[i].value === option.value) {
options.splice(i, 1);
return;
}
}
}
static getOptionsCount(data) {
let count = 0;
if (Array.isArray(data)) {
for (const groupOrOption of data) {
const options = groupOrOption.options;
count += options ? options.length : 1;
}
}
return count;
}
static isNullOrUndefined(value) {
return value === null || value === undefined;
}
static containSearchText(label, searchText, editPattern) {
return (Select2Utils.formatSansUnicode(label).match(new RegExp(Select2Utils.formatPattern(searchText, editPattern), 'i')) !== null);
}
static protectPattern(str) {
return str.replace(protectRegexp, '\\$&');
}
static formatSansUnicode(str) {
for (const unicodePattern of unicodePatterns) {
str = str.replace(unicodePattern.s, unicodePattern.l);
}
return str;
}
static patternUnicode(str) {
for (const unicodePattern of unicodePatterns) {
const pattern = unicodePattern.s.toString().replace('/gi', '').substring(1);
str = str.replace(new RegExp(`((${unicodePattern.e ?? unicodePattern.l}|${pattern})${unicodePattern.d?.pattern ?? ''})`, 'gi'), `((${unicodePattern.l}|${pattern})${unicodePattern.d?.tmp ?? ''})`);
}
str = str
.replaceAll(latinDiacritical.tmp, latinDiacritical.pattern)
.replaceAll(arabicDiacritical.tmp, arabicDiacritical.pattern)
.replaceAll(hebrewDiacritical.tmp, hebrewDiacritical.pattern);
return str;
}
static formatPattern(str, editPattern) {
str = Select2Utils.formatSansUnicode(Select2Utils.protectPattern(str));
if (editPattern && typeof editPattern === 'function') {
str = editPattern(str);
}
return str;
}
}
class Select2HighlightPipe {
constructor() {
this.sanitizer = inject(DomSanitizer);
}
transform(value, search, disabled = false) {
if (!value || !search || disabled) {
return value ?? '';
}
const escapedSearch = Select2Utils.patternUnicode(Select2Utils.protectPattern(search));
const result = value.replace(new RegExp(`(${escapedSearch})`, 'gi'), '<span class="select2-highlight-text">$1</span>');
return this.sanitizer.bypassSecurityTrustHtml(result);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2HighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
/** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: Select2HighlightPipe, isStandalone: true, name: "highlightText" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2HighlightPipe, decorators: [{
type: Pipe,
args: [{ name: 'highlightText' }]
}] });
class Select2Hint {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2Hint, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: Select2Hint, isStandalone: true, selector: "select2-hint, ng-select2-hint", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2Hint, decorators: [{
type: Directive,
args: [{ selector: 'select2-hint, ng-select2-hint' }]
}] });
class Select2Label {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2Label, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: Select2Label, isStandalone: true, selector: "select2-label, ng-select2-label", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: Select2Label, decorators: [{
type: Directive,
args: [{ selector: 'select2-label, ng-select2-label' }]
}] });
let nextUniqueId = 0;
const OPEN_KEYS_NATIVE = ['Enter', ' '];
const CLOSE_KEYS_NATIVE = ['ArrowDown', 'ArrowUp', 'Home', 'End', 'PageUp', 'PageDown'];
const OPEN_KEYS = ['ArrowDown', 'ArrowUp', 'Enter', ' ', 'Home', 'End', 'PageUp', 'PageDown'];
const ON_OPEN_KEYS = ['Home', 'End', 'PageUp', 'PageDown'];
const CLOSE_KEYS = ['Escape', 'Tab', { key: 'ArrowUp', altKey: true }];
class Select2 {
get select2Options() {
return this.multiple() ? (this.selectedOption ?? []) : [];
}
get select2Option() {
return this.multiple() ? null : this.selectedOption;
}
get searchText() {
return this.innerSearchText;
}
set searchText(text) {
this.innerSearchText = text;
}
get disabledState() {
return this._control?.disabled ?? this._disabled;
}
get resultsElement() {
const container = this.resultContainer();
return container ? container.nativeElement : undefined;
}
/** Tab index for the element. */
get _tabIndex() {
return this.disabledState ? -1 : this.tabIndex();
}
constructor() {
this._viewportRuler = inject(ViewportRuler);
this._changeDetectorRef = inject(ChangeDetectorRef);
this._parentForm = inject(NgForm, { optional: true });
this._parentFormGroup = inject(FormGroupDirective, { optional: true });
this._control = inject(NgControl, { optional: true, self: true });
this._uid = `select2-${nextUniqueId++}`;
// ----------------------- signal-input
/** data of options & option groups */
this.data = input([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
/** minimum characters to start filter search */
this.minCharForSearch = input(0, { ...(ngDevMode ? { debugName: "minCharForSearch" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** text placeholder */
this.displaySearchStatus = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "displaySearchStatus" }] : /* istanbul ignore next */ []));
/** text placeholder */
this.placeholder = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
/** in multiple: maximum selection element (0 = no limit) */
this.limitSelection = input(0, { ...(ngDevMode ? { debugName: "limitSelection" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** dropdown position */
this.listPosition = input('below', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "listPosition" }] : /* istanbul ignore next */ []));
/** overlay with CDK Angular position */
this.overlay = input(false, { ...(ngDevMode ? { debugName: "overlay" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** select one or more item */
this.multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** drag'n drop list of items in multiple */
this.multipleDrag = input(false, { ...(ngDevMode ? { debugName: "multipleDrag" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** use the material style */
this.styleMode = input('default', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "styleMode" }] : /* istanbul ignore next */ []));
/** message when no result */
this.noResultMessage = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "noResultMessage" }] : /* istanbul ignore next */ []));
/** maximum results limit (0 = no limit) */
this.maxResults = input(0, { ...(ngDevMode ? { debugName: "maxResults" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** message when maximum results */
this.maxResultsMessage = input('Too many results…', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "maxResultsMessage" }] : /* istanbul ignore next */ []));
/** infinite scroll distance */
this.infiniteScrollDistance = input(1.5, { ...(ngDevMode ? { debugName: "infiniteScrollDistance" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** infinite scroll distance */
this.infiniteScrollThrottle = input(150, { ...(ngDevMode ? { debugName: "infiniteScrollThrottle" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** infinite scroll activated */
this.infiniteScroll = input(false, { ...(ngDevMode ? { debugName: "infiniteScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** auto create if not exist */
this.autoCreate = input(false, { ...(ngDevMode ? { debugName: "autoCreate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** no template for label selection */
this.noLabelTemplate = input(false, { ...(ngDevMode ? { debugName: "noLabelTemplate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** use it for change the pattern of the filter search */
this.editPattern = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "editPattern" }] : /* istanbul ignore next */ []));
/** template(s) for formatting */
this.templates = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "templates" }] : /* istanbul ignore next */ []));
/** template for formatting selected option */
this.templateSelection = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "templateSelection" }] : /* istanbul ignore next */ []));
/** the max height of the results container when opening the select */
this.resultMaxHeight = input('200px', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resultMaxHeight" }] : /* istanbul ignore next */ []));
/** Active Search event */
this.customSearchEnabled = input(false, { ...(ngDevMode ? { debugName: "customSearchEnabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** minimal data of show the search field */
this.minCountForSearch = input(undefined, { ...(ngDevMode ? { debugName: "minCountForSearch" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** Unique id of the element. */
this.id = input(this._uid, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
/** Unique id of label element. */
this.idLabel = computed(() => `${this.id()}-label`, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "idLabel" }] : /* istanbul ignore next */ []));
/** Unique id of combo element. */
this.idCombo = computed(() => `${this.id()}-combo`, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "idCombo" }] : /* istanbul ignore next */ []));
/** Unique id of options element. */
this.idOptions = computed(() => `${this.id()}-options`, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "idOptions" }] : /* istanbul ignore next */ []));
/** Unique id of overlay element. */
this.idOverlay = computed(() => `${this.id()}-overlay`, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "idOverlay" }] : /* istanbul ignore next */ []));
/** Whether the element is required. */
this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Whether selected items should be hidden. */
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Whether items are hidden when has. */
this.hideSelectedItems = input(false, { ...(ngDevMode ? { debugName: "hideSelectedItems" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Whether the element is readonly. */
this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** The input element's value. */
this.value = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
/** Tab index for the select2 element. */
this.tabIndex = input(0, { ...(ngDevMode ? { debugName: "tabIndex" } : /* istanbul ignore next */ {}), transform: numberAttribute });
/** reset with no selected value */
this.resettable = input(false, { ...(ngDevMode ? { debugName: "resettable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** selected value when × is clicked */
this.resetSelectedValue = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resetSelectedValue" }] : /* istanbul ignore next */ []));
/** like native select keyboard navigation (only single mode) */
this.nativeKeyboard = input(false, { ...(ngDevMode ? { debugName: "nativeKeyboard" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** highlight search text */
this.highlightText = input(false, { ...(ngDevMode ? { debugName: "highlightText" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** grid: item by line
* * 0 = no grid
* * number = item by line (4)
* * string = minimal size item (100px)
*/
this.grid = input('', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []));
/**
* Replace selection by a text
* * if string: `%size%` = total selected options
* * if function: juste show the string
*/
this.selectionOverride = input(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectionOverride" }] : /* istanbul ignore next */ []));
/** force selection on one line */
this.selectionNoWrap = input(false, { ...(ngDevMode ? { debugName: "selectionNoWrap" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Add an option to select or remove all (if all is selected) */
this.showSelectAll = input(false, { ...(ngDevMode ? { debugName: "showSelectAll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Show a checkbox next to each option */
this.showOptionCheckbox = input(false, { ...(ngDevMode ? { debugName: "showOptionCheckbox" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** Text for remove all options */
this.removeAllText = input('Remove all', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "removeAllText" }] : /* istanbul ignore next */ []));
/** Text for select all options */
this.selectAllText = input('Select all', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "selectAllText" }] : /* istanbul ignore next */ []));
// -- WAI related inputs ---
/** title attribute applied to the input */
this.title = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
/** aria-labelledby attribute applied to the input, to specify en external label */
this.ariaLabelledby = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "ariaLabelledby" }] : /* istanbul ignore next */ []));
/** aria-describedby attribute applied to the input */
this.ariaDescribedby = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "ariaDescribedby" }] : /* istanbul ignore next */ []));
/** aria-invalid attribute applied to the input, to force error state */
this.ariaInvalid = input(false, { ...(ngDevMode ? { debugName: "ariaInvalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
/** description of the reset button when using 'resettable'. Default value : 'Reset' */
this.ariaResetButtonDescription = input('Reset', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ariaResetButtonDescription" }] : /* istanbul ignore next */ []));
// ----------------------- output
this.update = output();
this.autoCreateItem = output();
this.open = output();
this.close = output();
this.focus = output();
this.blur = output();
this.search = output();
this.scroll = output();
this.removeOption = output();
// ----------------------- signal viewChild
this.cdkConnectedOverlay = viewChild.required(CdkConnectedOverlay);
this.selection = viewChild.required('selection');
this.resultContainer = viewChild('results', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resultContainer" }] : /* istanbul ignore next */ []));
this.results = viewChildren('result', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "results" }] : /* istanbul ignore next */ []));
this.searchInput = viewChild('searchInput', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
this.dropdown = viewChild('dropdown', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dropdown" }] : /* istanbul ignore next */ []));
// ----------------------- content children (ng-option / ng-group template mode)
/** Top-level <ng-option> elements (not inside a <ng-group>) */
this._ngOptions = contentChildren(Select2OptionDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_ngOptions" }] : /* istanbul ignore next */ []));
/** <ng-group> elements */
this._ngGroups = contentChildren(Select2GroupDirective, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_ngGroups" }] : /* istanbul ignore next */ []));
// ----------------------- internal var
this.classMaterial = computed(() => this.styleMode() === 'material', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "classMaterial" }] : /* istanbul ignore next */ []));
this.classNostyle = computed(() => this.styleMode() === 'noStyle', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "classNostyle" }] : /* istanbul ignore next */ []));
this.classBorderless = computed(() => this.styleMode() === 'borderless', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "classBorderless" }] : /* istanbul ignore next */ []));
this.select2above = computed(() => !this.overlay() ? this.listPosition() === 'above' : this._isAbobeOverlay(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "select2above" }] : /* istanbul ignore next */ []));
this.selectedOption = null;
this.isOpen = false;
/** Whether the element is focused or not. */
this.focused = false;
this.filteredData = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "filteredData" }] : /* istanbul ignore next */ []));
this.overlayWidth = '';
this.overlayHeight = '';
this._positions = computed(() => {
switch (this.listPosition()) {
case 'above':
return [
new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }),
];
case 'auto':
return [
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }),
new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }),
];
default:
return [
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }),
];
}
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_positions" }] : /* istanbul ignore next */ []));
this.hoveringOption = signal(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hoveringOption" }] : /* istanbul ignore next */ []));
this.hoveringOptionId = computed(() => this.getElementId(this.hoveringOption()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hoveringOptionId" }] : /* istanbul ignore next */ []));
this.innerSearchText = '';
this._stateChanges = new Subject();
this._data = [];
this._disabled = false;
this._destroyed = false;
this._value = null;
this._overlayPosition = signal(undefined, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_overlayPosition" }] : /*