angular-perfect-select 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, HostListener, Output, Directive, signal, computed, ViewChild, Input, Component } from '@angular/core';
3
- import * as i3 from '@angular/common';
2
+ import { EventEmitter, HostListener, Output, Directive, signal, computed, TemplateRef, forwardRef, ContentChild, ViewChild, Input, Component } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import * as i2 from '@angular/forms';
5
+ import * as i3 from '@angular/forms';
6
6
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import * as i4 from '@angular/cdk/scrolling';
8
+ import { ScrollingModule, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
7
9
  import { trigger, transition, style, animate, query, stagger } from '@angular/animations';
8
10
  import * as i1 from '@angular/platform-browser';
9
11
 
@@ -178,6 +180,34 @@ class PerfectSelectComponent {
178
180
  debounceTime = 300;
179
181
  minSearchLength = 0;
180
182
  minSearchMessage = 'Type to search...';
183
+ // v1.2.0 Features - Virtual Scrolling
184
+ enableVirtualScroll = false;
185
+ virtualScrollItemSize = 40;
186
+ virtualScrollMinBufferPx = 200;
187
+ virtualScrollMaxBufferPx = 400;
188
+ // v1.2.0 Features - Validation States
189
+ validationState = 'default';
190
+ validationMessage = '';
191
+ showValidationIcon = true;
192
+ // v1.2.0 Features - Tooltips
193
+ showTooltips = false;
194
+ tooltipDelay = 300;
195
+ getOptionTooltip = (option) => option.tooltip || '';
196
+ // v1.2.0 Features - Recent Selections
197
+ showRecentSelections = false;
198
+ recentSelectionsLimit = 5;
199
+ recentSelectionsLabel = 'Recent';
200
+ enableRecentSelectionsPersistence = false;
201
+ // v1.2.0 Features - Infinite Scroll / Pagination
202
+ enableInfiniteScroll = false;
203
+ infiniteScrollThreshold = 80;
204
+ totalOptionsCount = null;
205
+ // v1.2.0 Features - Advanced Keyboard
206
+ enableAdvancedKeyboard = true;
207
+ typeAheadDelay = 500;
208
+ enableCopyPaste = true;
209
+ copyDelimiter = ', ';
210
+ pasteDelimiter = ',';
181
211
  // Behavior
182
212
  name = 'angular-perfect-select';
183
213
  id = 'angular-perfect-select';
@@ -200,10 +230,18 @@ class PerfectSelectComponent {
200
230
  createOption = new EventEmitter();
201
231
  optionsLoaded = new EventEmitter();
202
232
  loadError = new EventEmitter();
233
+ // v1.2.0 Events
234
+ copy = new EventEmitter();
235
+ paste = new EventEmitter();
236
+ scrollEnd = new EventEmitter();
203
237
  // ViewChildren
204
238
  selectContainerRef;
205
239
  searchInputRef;
206
240
  menuElementRef;
241
+ virtualScrollViewport;
242
+ // ContentChildren - Custom Templates
243
+ optionTemplate;
244
+ selectedOptionTemplate;
207
245
  // Signals for reactive state
208
246
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
209
247
  searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
@@ -213,6 +251,16 @@ class PerfectSelectComponent {
213
251
  isLoadingAsync = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingAsync" }] : []));
214
252
  optionsCache = new Map();
215
253
  debounceTimeout = null;
254
+ // v1.2.0 Signals
255
+ recentSelections = signal([], ...(ngDevMode ? [{ debugName: "recentSelections" }] : []));
256
+ typeAheadBuffer = signal('', ...(ngDevMode ? [{ debugName: "typeAheadBuffer" }] : []));
257
+ hoveredOptionIndex = signal(-1, ...(ngDevMode ? [{ debugName: "hoveredOptionIndex" }] : []));
258
+ isScrolling = signal(false, ...(ngDevMode ? [{ debugName: "isScrolling" }] : []));
259
+ // v1.2.0 Private state
260
+ typeAheadTimeout = null;
261
+ tooltipTimeout = null;
262
+ recentSelectionsStorageKey = '';
263
+ scrollEndTimeout = null;
216
264
  // Computed signals
217
265
  currentTheme = computed(() => THEMES[this.theme] || THEMES.blue, ...(ngDevMode ? [{ debugName: "currentTheme" }] : []));
218
266
  filteredOptions = computed(() => {
@@ -324,6 +372,29 @@ class PerfectSelectComponent {
324
372
  const term = this.searchTerm();
325
373
  return this.isOpen() && term.length > 0 && term.length < this.minSearchLength;
326
374
  }, ...(ngDevMode ? [{ debugName: "showMinSearchMessage" }] : []));
375
+ // v1.2.0 Computed signals
376
+ displayOptionsWithRecent = computed(() => {
377
+ if (!this.showRecentSelections || this.searchTerm()) {
378
+ return this.displayOptions();
379
+ }
380
+ const recent = this.recentSelections();
381
+ const display = this.displayOptions();
382
+ // Filter out recent options from display list to avoid duplicates
383
+ const displayWithoutRecent = display.filter(opt => !recent.some(r => this.getOptionValue(r) === this.getOptionValue(opt)));
384
+ // Combine recent (marked) and regular options
385
+ const markedRecent = recent.map(opt => ({ ...opt, __isRecent__: true }));
386
+ return [...markedRecent, ...displayWithoutRecent];
387
+ }, ...(ngDevMode ? [{ debugName: "displayOptionsWithRecent" }] : []));
388
+ validationIconName = computed(() => {
389
+ const state = this.validationState;
390
+ switch (state) {
391
+ case 'error': return 'error';
392
+ case 'warning': return 'warning';
393
+ case 'success': return 'check_circle';
394
+ case 'info': return 'info';
395
+ default: return '';
396
+ }
397
+ }, ...(ngDevMode ? [{ debugName: "validationIconName" }] : []));
327
398
  // Helper method for template
328
399
  getEnabledOptionsCount() {
329
400
  return this.filteredOptions().filter(opt => !this.isOptionDisabled(opt)).length;
@@ -369,6 +440,11 @@ class PerfectSelectComponent {
369
440
  if (this.loadOptions && this.defaultOptions) {
370
441
  this.handleLoadOptions('');
371
442
  }
443
+ // v1.2.0: Initialize recent selections
444
+ if (this.showRecentSelections) {
445
+ this.recentSelectionsStorageKey = `ps-recent-${this.name || this.id}`;
446
+ this.loadRecentSelections();
447
+ }
372
448
  // Auto-focus if needed
373
449
  if (this.autoFocus) {
374
450
  setTimeout(() => {
@@ -383,11 +459,62 @@ class PerfectSelectComponent {
383
459
  if (this.debounceTimeout) {
384
460
  clearTimeout(this.debounceTimeout);
385
461
  }
462
+ // v1.2.0: Clear new timeouts
463
+ if (this.typeAheadTimeout) {
464
+ clearTimeout(this.typeAheadTimeout);
465
+ }
466
+ if (this.tooltipTimeout) {
467
+ clearTimeout(this.tooltipTimeout);
468
+ }
469
+ if (this.scrollEndTimeout) {
470
+ clearTimeout(this.scrollEndTimeout);
471
+ }
386
472
  }
387
473
  // Keyboard Navigation
388
474
  handleKeydown(event) {
389
475
  if (this.isDisabled)
390
476
  return;
477
+ // v1.2.0: Advanced keyboard shortcuts
478
+ if (this.enableAdvancedKeyboard) {
479
+ // Ctrl/Cmd + A: Select All
480
+ if ((event.ctrlKey || event.metaKey) && event.key === 'a' && this.isMulti && this.isOpen()) {
481
+ event.preventDefault();
482
+ this.selectAll();
483
+ return;
484
+ }
485
+ // Ctrl/Cmd + C: Copy
486
+ if ((event.ctrlKey || event.metaKey) && event.key === 'c' && this.enableCopyPaste) {
487
+ if (this.selectedOptions().length > 0) {
488
+ event.preventDefault();
489
+ this.copySelectedValues();
490
+ }
491
+ return;
492
+ }
493
+ // Ctrl/Cmd + V: Paste
494
+ if ((event.ctrlKey || event.metaKey) && event.key === 'v' && this.enableCopyPaste && this.isMulti) {
495
+ // Let browser handle paste, we'll catch it in the paste event
496
+ return;
497
+ }
498
+ // Home: Jump to first option
499
+ if (event.key === 'Home' && this.isOpen()) {
500
+ event.preventDefault();
501
+ this.highlightedIndex.set(0);
502
+ this.scrollHighlightedIntoView();
503
+ return;
504
+ }
505
+ // End: Jump to last option
506
+ if (event.key === 'End' && this.isOpen()) {
507
+ event.preventDefault();
508
+ const opts = this.displayOptions();
509
+ this.highlightedIndex.set(Math.max(0, opts.length - 1));
510
+ this.scrollHighlightedIntoView();
511
+ return;
512
+ }
513
+ // Type-ahead: Single character typing
514
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey && this.isOpen()) {
515
+ this.handleTypeAhead(event.key);
516
+ }
517
+ }
391
518
  switch (event.key) {
392
519
  case 'ArrowDown':
393
520
  event.preventDefault();
@@ -406,7 +533,7 @@ class PerfectSelectComponent {
406
533
  event.preventDefault();
407
534
  if (this.isOpen()) {
408
535
  const index = this.highlightedIndex();
409
- const opts = this.displayOptions();
536
+ const opts = this.showRecentSelections ? this.displayOptionsWithRecent() : this.displayOptions();
410
537
  if (index >= 0 && index < opts.length) {
411
538
  this.selectOption(opts[index]);
412
539
  }
@@ -448,6 +575,16 @@ class PerfectSelectComponent {
448
575
  break;
449
576
  }
450
577
  }
578
+ // v1.2.0: Paste event handler
579
+ handlePaste(event) {
580
+ if (!this.enableCopyPaste || !this.isMulti || this.isDisabled)
581
+ return;
582
+ const pastedText = event.clipboardData?.getData('text');
583
+ if (!pastedText)
584
+ return;
585
+ event.preventDefault();
586
+ this.pasteValues(pastedText);
587
+ }
451
588
  // Toggle dropdown
452
589
  toggleDropdown() {
453
590
  if (this.isDisabled)
@@ -514,6 +651,10 @@ class PerfectSelectComponent {
514
651
  option,
515
652
  action: exists ? 'remove-value' : 'select-option'
516
653
  });
654
+ // v1.2.0: Track recent selection
655
+ if (!exists && this.showRecentSelections) {
656
+ this.addToRecentSelections(option);
657
+ }
517
658
  }
518
659
  else {
519
660
  this.internalValue.set(optionValue);
@@ -523,6 +664,10 @@ class PerfectSelectComponent {
523
664
  option,
524
665
  action: 'select-option'
525
666
  });
667
+ // v1.2.0: Track recent selection
668
+ if (this.showRecentSelections) {
669
+ this.addToRecentSelections(option);
670
+ }
526
671
  }
527
672
  if (this.closeMenuOnSelect) {
528
673
  this.closeDropdown();
@@ -680,20 +825,179 @@ class PerfectSelectComponent {
680
825
  trackByGroup(index, item) {
681
826
  return item[0];
682
827
  }
828
+ // ==================== v1.2.0 NEW METHODS ====================
829
+ // Copy selected values to clipboard
830
+ copySelectedValues() {
831
+ const selected = this.selectedOptions();
832
+ if (selected.length === 0)
833
+ return;
834
+ const values = selected.map(opt => this.getOptionLabel(opt));
835
+ const formattedText = values.join(this.copyDelimiter);
836
+ // Copy to clipboard using modern API
837
+ if (navigator.clipboard && navigator.clipboard.writeText) {
838
+ navigator.clipboard.writeText(formattedText).then(() => {
839
+ this.copy.emit({ values: selected.map(opt => this.getOptionValue(opt)), formattedText });
840
+ });
841
+ }
842
+ else {
843
+ // Fallback for older browsers
844
+ const textarea = document.createElement('textarea');
845
+ textarea.value = formattedText;
846
+ textarea.style.position = 'fixed';
847
+ textarea.style.opacity = '0';
848
+ document.body.appendChild(textarea);
849
+ textarea.select();
850
+ document.execCommand('copy');
851
+ document.body.removeChild(textarea);
852
+ this.copy.emit({ values: selected.map(opt => this.getOptionValue(opt)), formattedText });
853
+ }
854
+ }
855
+ // Paste values from clipboard
856
+ pasteValues(pastedText) {
857
+ const values = pastedText
858
+ .split(this.pasteDelimiter)
859
+ .map(v => v.trim())
860
+ .filter(v => v.length > 0);
861
+ if (values.length === 0)
862
+ return;
863
+ this.paste.emit({ values, pastedText });
864
+ // Find matching options and select them
865
+ const allOptions = this.internalOptions();
866
+ const currentValue = Array.isArray(this.internalValue()) ? [...this.internalValue()] : [];
867
+ values.forEach(value => {
868
+ const option = allOptions.find(opt => this.getOptionLabel(opt).toLowerCase() === value.toLowerCase() ||
869
+ String(this.getOptionValue(opt)).toLowerCase() === value.toLowerCase());
870
+ if (option && !currentValue.includes(this.getOptionValue(option))) {
871
+ // Check max selection limit
872
+ if (this.maxSelectedOptions === null || currentValue.length < this.maxSelectedOptions) {
873
+ currentValue.push(this.getOptionValue(option));
874
+ }
875
+ }
876
+ });
877
+ this.internalValue.set(currentValue);
878
+ this.onChange(currentValue);
879
+ this.change.emit({
880
+ value: currentValue,
881
+ action: 'set-value'
882
+ });
883
+ }
884
+ // Type-ahead functionality
885
+ handleTypeAhead(key) {
886
+ // Clear previous timeout
887
+ if (this.typeAheadTimeout) {
888
+ clearTimeout(this.typeAheadTimeout);
889
+ }
890
+ // Append key to buffer
891
+ const newBuffer = this.typeAheadBuffer() + key.toLowerCase();
892
+ this.typeAheadBuffer.set(newBuffer);
893
+ // Find matching option
894
+ const opts = this.displayOptions();
895
+ const matchIndex = opts.findIndex(opt => this.getOptionLabel(opt).toLowerCase().startsWith(newBuffer));
896
+ if (matchIndex !== -1) {
897
+ this.highlightedIndex.set(matchIndex);
898
+ this.scrollHighlightedIntoView();
899
+ }
900
+ // Clear buffer after delay
901
+ this.typeAheadTimeout = setTimeout(() => {
902
+ this.typeAheadBuffer.set('');
903
+ }, this.typeAheadDelay);
904
+ }
905
+ // Recent selections management
906
+ addToRecentSelections(option) {
907
+ const recent = this.recentSelections();
908
+ const optionValue = this.getOptionValue(option);
909
+ // Remove if already exists
910
+ const filtered = recent.filter(r => this.getOptionValue(r) !== optionValue);
911
+ // Add to beginning
912
+ const updated = [option, ...filtered].slice(0, this.recentSelectionsLimit);
913
+ this.recentSelections.set(updated);
914
+ // Save to storage if enabled
915
+ if (this.enableRecentSelectionsPersistence) {
916
+ this.saveRecentSelections();
917
+ }
918
+ }
919
+ loadRecentSelections() {
920
+ if (!this.enableRecentSelectionsPersistence)
921
+ return;
922
+ try {
923
+ const stored = localStorage.getItem(this.recentSelectionsStorageKey);
924
+ if (stored) {
925
+ const parsed = JSON.parse(stored);
926
+ this.recentSelections.set(parsed.slice(0, this.recentSelectionsLimit));
927
+ }
928
+ }
929
+ catch (error) {
930
+ console.warn('Failed to load recent selections:', error);
931
+ }
932
+ }
933
+ saveRecentSelections() {
934
+ if (!this.enableRecentSelectionsPersistence)
935
+ return;
936
+ try {
937
+ const recent = this.recentSelections();
938
+ localStorage.setItem(this.recentSelectionsStorageKey, JSON.stringify(recent));
939
+ }
940
+ catch (error) {
941
+ console.warn('Failed to save recent selections:', error);
942
+ }
943
+ }
944
+ // Infinite scroll handler
945
+ onOptionsScroll(event) {
946
+ if (!this.enableInfiniteScroll)
947
+ return;
948
+ const target = event.target;
949
+ const scrollTop = target.scrollTop;
950
+ const scrollHeight = target.scrollHeight;
951
+ const clientHeight = target.clientHeight;
952
+ const scrollPercentage = ((scrollTop + clientHeight) / scrollHeight) * 100;
953
+ // Clear previous timeout
954
+ if (this.scrollEndTimeout) {
955
+ clearTimeout(this.scrollEndTimeout);
956
+ }
957
+ // Debounce scroll end detection
958
+ this.scrollEndTimeout = setTimeout(() => {
959
+ if (scrollPercentage >= this.infiniteScrollThreshold) {
960
+ this.scrollEnd.emit({ scrollTop, scrollHeight, clientHeight });
961
+ }
962
+ }, 100);
963
+ }
964
+ // Virtual scroll index tracking
965
+ onVirtualScrollIndexChange(index) {
966
+ // Update highlighted index for virtual scroll
967
+ if (this.enableVirtualScroll) {
968
+ this.highlightedIndex.set(index);
969
+ }
970
+ }
971
+ // Tooltip methods
972
+ showTooltip(option, index) {
973
+ if (!this.showTooltips)
974
+ return;
975
+ this.hoveredOptionIndex.set(index);
976
+ }
977
+ hideTooltip() {
978
+ this.hoveredOptionIndex.set(-1);
979
+ }
980
+ getTooltipContent(option) {
981
+ return this.getOptionTooltip(option) || option.tooltip || '';
982
+ }
983
+ // Validation state CSS class
984
+ getValidationClass() {
985
+ return `validation-${this.validationState}`;
986
+ }
683
987
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PerfectSelectComponent, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
684
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PerfectSelectComponent, isStandalone: true, selector: "ng-perfect-select", inputs: { options: "options", placeholder: "placeholder", isMulti: "isMulti", multiple: "multiple", isSearchable: "isSearchable", searchable: "searchable", isClearable: "isClearable", clearable: "clearable", isDisabled: "isDisabled", disabled: "disabled", isLoading: "isLoading", loading: "loading", isRtl: "isRtl", closeMenuOnSelect: "closeMenuOnSelect", hideSelectedOptions: "hideSelectedOptions", isCreatable: "isCreatable", allowCreateWhileLoading: "allowCreateWhileLoading", createOptionPosition: "createOptionPosition", formatCreateLabel: "formatCreateLabel", loadOptions: "loadOptions", cacheOptions: "cacheOptions", defaultOptions: "defaultOptions", selectSize: "selectSize", containerSize: "containerSize", theme: "theme", borderRadius: "borderRadius", customStyles: "customStyles", maxHeight: "maxHeight", menuPlacement: "menuPlacement", menuPosition: "menuPosition", getOptionLabel: "getOptionLabel", getOptionValue: "getOptionValue", isOptionDisabled: "isOptionDisabled", filterOption: "filterOption", isGrouped: "isGrouped", groupBy: "groupBy", showSelectAll: "showSelectAll", selectAllText: "selectAllText", deselectAllText: "deselectAllText", showOptionIcons: "showOptionIcons", showOptionBadges: "showOptionBadges", maxOptionsDisplay: "maxOptionsDisplay", optionHeight: "optionHeight", emptyStateText: "emptyStateText", emptySearchText: "emptySearchText", maxSelectedOptions: "maxSelectedOptions", maxSelectedMessage: "maxSelectedMessage", debounceTime: "debounceTime", minSearchLength: "minSearchLength", minSearchMessage: "minSearchMessage", name: "name", id: "id", autoFocus: "autoFocus", openMenuOnFocus: "openMenuOnFocus", openMenuOnClick: "openMenuOnClick", tabSelectsValue: "tabSelectsValue", backspaceRemovesValue: "backspaceRemovesValue", escapeClearsValue: "escapeClearsValue", noOptionsMessage: "noOptionsMessage", loadingMessage: "loadingMessage" }, outputs: { change: "change", clear: "clear", focus: "focus", blur: "blur", menuOpen: "menuOpen", menuClose: "menuClose", inputChange: "inputChange", createOption: "createOption", optionsLoaded: "optionsLoaded", loadError: "loadError" }, host: { listeners: { "keydown": "handleKeydown($event)" } }, providers: [{
988
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PerfectSelectComponent, isStandalone: true, selector: "ng-perfect-select", inputs: { options: "options", placeholder: "placeholder", isMulti: "isMulti", multiple: "multiple", isSearchable: "isSearchable", searchable: "searchable", isClearable: "isClearable", clearable: "clearable", isDisabled: "isDisabled", disabled: "disabled", isLoading: "isLoading", loading: "loading", isRtl: "isRtl", closeMenuOnSelect: "closeMenuOnSelect", hideSelectedOptions: "hideSelectedOptions", isCreatable: "isCreatable", allowCreateWhileLoading: "allowCreateWhileLoading", createOptionPosition: "createOptionPosition", formatCreateLabel: "formatCreateLabel", loadOptions: "loadOptions", cacheOptions: "cacheOptions", defaultOptions: "defaultOptions", selectSize: "selectSize", containerSize: "containerSize", theme: "theme", borderRadius: "borderRadius", customStyles: "customStyles", maxHeight: "maxHeight", menuPlacement: "menuPlacement", menuPosition: "menuPosition", getOptionLabel: "getOptionLabel", getOptionValue: "getOptionValue", isOptionDisabled: "isOptionDisabled", filterOption: "filterOption", isGrouped: "isGrouped", groupBy: "groupBy", showSelectAll: "showSelectAll", selectAllText: "selectAllText", deselectAllText: "deselectAllText", showOptionIcons: "showOptionIcons", showOptionBadges: "showOptionBadges", maxOptionsDisplay: "maxOptionsDisplay", optionHeight: "optionHeight", emptyStateText: "emptyStateText", emptySearchText: "emptySearchText", maxSelectedOptions: "maxSelectedOptions", maxSelectedMessage: "maxSelectedMessage", debounceTime: "debounceTime", minSearchLength: "minSearchLength", minSearchMessage: "minSearchMessage", enableVirtualScroll: "enableVirtualScroll", virtualScrollItemSize: "virtualScrollItemSize", virtualScrollMinBufferPx: "virtualScrollMinBufferPx", virtualScrollMaxBufferPx: "virtualScrollMaxBufferPx", validationState: "validationState", validationMessage: "validationMessage", showValidationIcon: "showValidationIcon", showTooltips: "showTooltips", tooltipDelay: "tooltipDelay", getOptionTooltip: "getOptionTooltip", showRecentSelections: "showRecentSelections", recentSelectionsLimit: "recentSelectionsLimit", recentSelectionsLabel: "recentSelectionsLabel", enableRecentSelectionsPersistence: "enableRecentSelectionsPersistence", enableInfiniteScroll: "enableInfiniteScroll", infiniteScrollThreshold: "infiniteScrollThreshold", totalOptionsCount: "totalOptionsCount", enableAdvancedKeyboard: "enableAdvancedKeyboard", typeAheadDelay: "typeAheadDelay", enableCopyPaste: "enableCopyPaste", copyDelimiter: "copyDelimiter", pasteDelimiter: "pasteDelimiter", name: "name", id: "id", autoFocus: "autoFocus", openMenuOnFocus: "openMenuOnFocus", openMenuOnClick: "openMenuOnClick", tabSelectsValue: "tabSelectsValue", backspaceRemovesValue: "backspaceRemovesValue", escapeClearsValue: "escapeClearsValue", noOptionsMessage: "noOptionsMessage", loadingMessage: "loadingMessage" }, outputs: { change: "change", clear: "clear", focus: "focus", blur: "blur", menuOpen: "menuOpen", menuClose: "menuClose", inputChange: "inputChange", createOption: "createOption", optionsLoaded: "optionsLoaded", loadError: "loadError", copy: "copy", paste: "paste", scrollEnd: "scrollEnd" }, host: { listeners: { "keydown": "handleKeydown($event)", "paste": "handlePaste($event)" } }, providers: [{
685
989
  provide: NG_VALUE_ACCESSOR,
686
- useExisting: PerfectSelectComponent,
990
+ useExisting: forwardRef(() => PerfectSelectComponent),
687
991
  multi: true
688
- }], viewQueries: [{ propertyName: "selectContainerRef", first: true, predicate: ["selectContainer"], descendants: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "menuElementRef", first: true, predicate: ["menuRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #selectContainer\n class=\"select-container {{selectSize}} {{containerSize}} theme-{{theme}}\"\n [class.disabled]=\"isDisabled\"\n [class.rtl]=\"isRtl\"\n (clickOutside)=\"onClickOutside()\"\n role=\"combobox\"\n tabindex=\"0\"\n [attr.aria-controls]=\"'options-list'\"\n [attr.aria-expanded]=\"isOpen()\"\n (focus)=\"openMenuOnFocus && !isOpen() && toggleDropdown(); focus.emit()\"\n [style]=\"customStyles.container || ''\"\n>\n <!-- Main Select Trigger -->\n <div\n class=\"select-trigger\"\n [class.open]=\"isOpen()\"\n [class.focused]=\"isOpen()\"\n (click)=\"openMenuOnClick && toggleDropdown()\"\n [attr.tabindex]=\"isDisabled ? -1 : 0\"\n role=\"button\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-label]=\"placeholder\"\n >\n <!-- Selected Value Display -->\n <div class=\"select-value\">\n <!-- Multi-select Tags -->\n @if (isMulti && selectedOptions().length > 0) {\n <div class=\"tags\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" [@tag]>\n <span class=\"tag-label\">{{getOptionLabel(option)}}</span>\n @if (!isDisabled) {\n <button\n class=\"tag-remove\"\n (click)=\"removeOption(option, $event)\"\n [attr.aria-label]=\"'Remove ' + getOptionLabel(option)\"\n type=\"button\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n </span>\n }\n </div>\n } @else {\n <!-- Single Select or Placeholder -->\n <span class=\"placeholder\" [class.has-value]=\"selectedOptions().length > 0\">\n {{displayText()}}\n </span>\n }\n </div>\n\n <!-- Actions (Clear, Loading, Arrow) -->\n <div class=\"select-actions\">\n @if (isLoading || isLoadingAsync()) {\n <div class=\"spinner\"></div>\n }\n @if (isClearable && selectedOptions().length > 0 && !isDisabled && !isLoading && !isLoadingAsync()) {\n <button\n class=\"clear-button\"\n (click)=\"clearSelection($event)\"\n aria-label=\"Clear selection\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n <span class=\"separator\"></span>\n <span class=\"arrow\" [class.open]=\"isOpen()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\"></path>\n </svg>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n @if (isOpen()) {\n <div\n #menuRef\n class=\"dropdown {{menuPlacement}}\"\n [class.fixed]=\"menuPosition === 'fixed'\"\n [style.max-height]=\"maxHeight\"\n role=\"listbox\"\n [attr.aria-multiselectable]=\"isMulti\"\n [@dropdown]\n >\n <!-- Search Input -->\n @if (isSearchable) {\n <div class=\"search-container\">\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search...\"\n [value]=\"searchTerm()\"\n (input)=\"onSearchChange($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n aria-label=\"Search options\"\n aria-autocomplete=\"list\"\n />\n </div>\n }\n\n <!-- Options List -->\n <div class=\"options-list\" id=\"options-list\">\n <!-- Max Selection Message -->\n @if (isMaxSelectionReached()) {\n <div class=\"info-message max-selection\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{maxSelectedMessage}}\n </div>\n }\n\n <!-- Min Search Length Message -->\n @if (showMinSearchMessage()) {\n <div class=\"info-message min-search\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{minSearchMessage}} ({{searchTerm().length}}/{{minSearchLength}})\n </div>\n }\n\n @if (isLoadingAsync()) {\n <div class=\"loading-message\">{{loadingMessage()}}</div>\n } @else if (displayOptions().length === 0 && !showMinSearchMessage()) {\n <div class=\"no-options\">\n {{searchTerm() ? emptySearchText : emptyStateText}}\n </div>\n } @else if (!showMinSearchMessage()) {\n <!-- Select All (Multi-select only) -->\n @if (isMulti && showSelectAll && !searchTerm()) {\n <div class=\"select-all-container\">\n <button\n class=\"select-all-button\"\n (click)=\"allOptionsSelected() ? deselectAll() : selectAll()\"\n type=\"button\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"allOptionsSelected()\"\n [indeterminate]=\"someOptionsSelected()\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n <span class=\"select-all-text\">\n {{allOptionsSelected() ? deselectAllText : selectAllText}}\n </span>\n <span class=\"select-all-count\">\n ({{selectedOptions().length}}/{{getEnabledOptionsCount()}})\n </span>\n </button>\n </div>\n }\n\n <!-- Grouped Options -->\n @if (isGrouped && groupedOptions()) {\n @for (group of groupedOptions() | keyvalue; track trackByGroup($index, [$any(group.key), $any(group.value)])) {\n <div class=\"option-group\">\n <div class=\"option-group-label\">{{group.key}}</div>\n @for (option of $any(group.value); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n </div>\n }\n } @else {\n <!-- Regular (Ungrouped) Options -->\n @for (option of displayOptions(); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n</div>\n\n<!-- Hidden native select for form compatibility -->\n@if (isMulti) {\n <select [name]=\"name\" [id]=\"id\" multiple style=\"display: none;\" [attr.aria-hidden]=\"true\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <option [value]=\"getOptionValue(option)\" selected>{{getOptionLabel(option)}}</option>\n }\n </select>\n} @else {\n @if (selectedOptions().length > 0) {\n <select [name]=\"name\" [id]=\"id\" style=\"display: none;\" [attr.aria-hidden]=\"true\">\n <option [value]=\"getOptionValue(selectedOptions()[0])\" selected>{{getOptionLabel(selectedOptions()[0])}}</option>\n </select>\n }\n}\n", styles: [":host{display:block;width:100%}.select-container{position:relative;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.smaller{font-size:11px}.small{font-size:13px}.medium{font-size:14px}.large{font-size:16px}.larger{font-size:18px}.xs .select-trigger{min-height:28px;padding:4px 8px}.sm .select-trigger{min-height:32px;padding:6px 10px}.md .select-trigger{min-height:40px;padding:8px 12px}.lg .select-trigger{min-height:48px;padding:10px 14px}.xl .select-trigger{min-height:56px;padding:12px 16px}.select-container.disabled{opacity:.6;cursor:not-allowed}.select-container.rtl{direction:rtl}.select-trigger{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1.5px solid #D1D5DB;border-radius:10px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);min-height:38px;gap:8px;box-shadow:0 1px 2px #0000000d}.select-trigger:hover:not(.disabled){border-color:#9ca3af;box-shadow:0 2px 4px #00000014}.select-trigger:focus,.select-trigger.focused{outline:none;border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-value{flex:1;display:flex;align-items:center;min-width:0;gap:4px}.placeholder{color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.placeholder.has-value{color:#333}.tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}.tag{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#e6f2ff;color:#0052cc;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #CCE0FF;box-shadow:0 1px 2px #0000000d}.tag.disabled{background:#f0f0f0;color:#666;border-color:#d9d9d9}.tag-label{line-height:1.2}.tag-remove{background:none;border:none;color:inherit;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;border-radius:2px;transition:all .15s}.tag-remove:hover{background:#0052cc26}.tag-remove svg{fill:currentColor}.select-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}.clear-button{background:none;border:none;color:#999;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s}.clear-button:hover{color:#333;background:#f0f0f0}.clear-button svg{fill:currentColor}.separator{width:1px;height:24px;background:#ccc;align-self:stretch}.arrow{color:#999;transition:transform .2s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;padding:4px}.arrow svg{fill:currentColor}.arrow.open{transform:rotate(180deg)}.spinner{width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #2684FF;border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.dropdown{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fffffffa;border:1px solid #E5E7EB;border-radius:12px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d;z-index:1000;overflow:hidden;backdrop-filter:blur(12px) saturate(180%);-webkit-backdrop-filter:blur(12px) saturate(180%)}.dropdown.fixed{position:fixed}.dropdown.top{top:auto;bottom:calc(100% + 4px)}.search-container{padding:8px;border-bottom:1px solid #f0f0f0;background:#fff}.search-input{width:100%;padding:6px 8px;border:1px solid #cccccc;border-radius:3px;font-size:inherit;outline:none;transition:border-color .15s}.search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.options-list{max-height:inherit;overflow-y:auto;overflow-x:hidden;padding:4px 0}.options-list::-webkit-scrollbar{width:8px}.options-list::-webkit-scrollbar-track{background:transparent}.options-list::-webkit-scrollbar-thumb{background:#d9d9d9;border-radius:4px}.options-list::-webkit-scrollbar-thumb:hover{background:#b3b3b3}.select-all-container{padding:8px;border-bottom:1px solid #E5E7EB;background:#f9fafb}.select-all-button{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;background:#fff;border:1px solid #D1D5DB;border-radius:6px;cursor:pointer;transition:all .15s;font-size:inherit;color:inherit;font-family:inherit}.select-all-button:hover{background:#f3f4f6;border-color:#9ca3af}.select-all-button input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.select-all-text{flex:1;text-align:left;font-weight:500}.select-all-count{color:#6b7280;font-size:.9em}.option-group{margin:4px 0}.option-group-label{padding:8px 12px 4px;font-size:.75em;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:1}.option-group .option{padding-left:20px}.option{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;transition:all .15s cubic-bezier(.4,0,.2,1);position:relative;min-height:40px}.option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.option-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6}.option-icon img{width:100%;height:100%;object-fit:cover}.option-badge{padding:2px 8px;border-radius:12px;font-size:.75em;font-weight:500;color:#374151;background:#e5e7eb;flex-shrink:0;white-space:nowrap}.option:hover:not(.disabled):not(.create-option){background:#deebff}.option.highlighted{background:#deebff}.option.selected:not(.create-option){background:#e6f2ff;font-weight:500}.option.disabled{opacity:.5;cursor:not-allowed;background:transparent!important}.option.hidden{display:none}.option.create-option{color:#2684ff;font-weight:500;background:#f0f6ff}.option.create-option:hover{background:#e6f2ff}.option input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.option-label{font-weight:500;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.option-description{font-size:.875em;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.check-icon{color:#2684ff;display:flex;align-items:center;margin-left:auto}.check-icon svg{fill:currentColor}.no-options,.loading-message{padding:16px 12px;text-align:center;color:#999;font-size:.95em}.info-message{padding:12px;margin:8px;border-radius:6px;font-size:.9em;display:flex;align-items:center;gap:8px}.info-message.max-selection{background:#fff4e5;color:#f57c00;border:1px solid #FFE0B2}.info-message.min-search{background:#e3f2fd;color:#1976d2;border:1px solid #BBDEFB}.info-message svg{flex-shrink:0}.rtl .select-actions,.rtl .tags,.rtl .option{flex-direction:row-reverse}.theme-blue .select-trigger:focus,.theme-blue .select-trigger.focused,.theme-blue .select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-blue .option:hover:not(.disabled):not(.create-option),.theme-blue .option.highlighted{background:#deebff}.theme-blue .option.selected:not(.create-option){background:#e6f2ff}.theme-blue .tag{background:#e6f2ff;color:#0052cc;border-color:#cce0ff}.theme-blue .check-icon,.theme-blue .option.create-option{color:#2684ff}.theme-blue .spinner{border-top-color:#2684ff}.theme-blue .search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-purple .select-trigger:focus,.theme-purple .select-trigger.focused,.theme-purple .select-trigger.open{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-purple .option:hover:not(.disabled):not(.create-option),.theme-purple .option.highlighted{background:#f3e8ff}.theme-purple .option.selected:not(.create-option){background:#faf5ff}.theme-purple .tag{background:#faf5ff;color:#7e22ce;border-color:#e9d5ff}.theme-purple .check-icon,.theme-purple .option.create-option{color:#9333ea}.theme-purple .spinner{border-top-color:#9333ea}.theme-purple .search-input:focus{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-green .select-trigger:focus,.theme-green .select-trigger.focused,.theme-green .select-trigger.open{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-green .option:hover:not(.disabled):not(.create-option),.theme-green .option.highlighted{background:#d1fae5}.theme-green .option.selected:not(.create-option){background:#ecfdf5}.theme-green .tag{background:#ecfdf5;color:#059669;border-color:#a7f3d0}.theme-green .check-icon,.theme-green .option.create-option{color:#10b981}.theme-green .spinner{border-top-color:#10b981}.theme-green .search-input:focus{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-red .select-trigger:focus,.theme-red .select-trigger.focused,.theme-red .select-trigger.open{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-red .option:hover:not(.disabled):not(.create-option),.theme-red .option.highlighted{background:#fee2e2}.theme-red .option.selected:not(.create-option){background:#fef2f2}.theme-red .tag{background:#fef2f2;color:#dc2626;border-color:#fecaca}.theme-red .check-icon,.theme-red .option.create-option{color:#ef4444}.theme-red .spinner{border-top-color:#ef4444}.theme-red .search-input:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-orange .select-trigger:focus,.theme-orange .select-trigger.focused,.theme-orange .select-trigger.open{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-orange .option:hover:not(.disabled):not(.create-option),.theme-orange .option.highlighted{background:#ffedd5}.theme-orange .option.selected:not(.create-option){background:#fff7ed}.theme-orange .tag{background:#fff7ed;color:#ea580c;border-color:#fed7aa}.theme-orange .check-icon,.theme-orange .option.create-option{color:#f97316}.theme-orange .spinner{border-top-color:#f97316}.theme-orange .search-input:focus{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-pink .select-trigger:focus,.theme-pink .select-trigger.focused,.theme-pink .select-trigger.open{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-pink .option:hover:not(.disabled):not(.create-option),.theme-pink .option.highlighted{background:#fce7f3}.theme-pink .option.selected:not(.create-option){background:#fdf2f8}.theme-pink .tag{background:#fdf2f8;color:#db2777;border-color:#fbcfe8}.theme-pink .check-icon,.theme-pink .option.create-option{color:#ec4899}.theme-pink .spinner{border-top-color:#ec4899}.theme-pink .search-input:focus{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-dark .select-trigger:focus,.theme-dark .select-trigger.focused,.theme-dark .select-trigger.open{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.theme-dark .option:hover:not(.disabled):not(.create-option),.theme-dark .option.highlighted{background:#e5e7eb}.theme-dark .option.selected:not(.create-option){background:#f3f4f6}.theme-dark .tag{background:#f3f4f6;color:#111827;border-color:#d1d5db}.theme-dark .check-icon,.theme-dark .option.create-option{color:#1f2937}.theme-dark .spinner{border-top-color:#1f2937}.theme-dark .search-input:focus{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", outputs: ["clickOutside"] }, { kind: "pipe", type: i3.KeyValuePipe, name: "keyvalue" }], animations: selectAnimations });
992
+ }], queries: [{ propertyName: "optionTemplate", first: true, predicate: ["optionTemplate"], descendants: true, read: TemplateRef }, { propertyName: "selectedOptionTemplate", first: true, predicate: ["selectedOptionTemplate"], descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "selectContainerRef", first: true, predicate: ["selectContainer"], descendants: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "menuElementRef", first: true, predicate: ["menuRef"], descendants: true }, { propertyName: "virtualScrollViewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #selectContainer\n class=\"select-container {{selectSize}} {{containerSize}} theme-{{theme}} {{getValidationClass()}}\"\n [class.disabled]=\"isDisabled\"\n [class.rtl]=\"isRtl\"\n [class.has-validation]=\"validationState !== 'default'\"\n (clickOutside)=\"onClickOutside()\"\n role=\"combobox\"\n tabindex=\"0\"\n [attr.aria-controls]=\"'options-list'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"validationState === 'error'\"\n (focus)=\"openMenuOnFocus && !isOpen() && toggleDropdown(); focus.emit()\"\n [style]=\"customStyles.container || ''\"\n>\n <!-- Main Select Trigger -->\n <div\n class=\"select-trigger\"\n [class.open]=\"isOpen()\"\n [class.focused]=\"isOpen()\"\n (click)=\"openMenuOnClick && toggleDropdown()\"\n [attr.tabindex]=\"isDisabled ? -1 : 0\"\n role=\"button\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-label]=\"placeholder\"\n >\n <!-- Selected Value Display -->\n <div class=\"select-value\">\n <!-- Multi-select Tags -->\n @if (isMulti && selectedOptions().length > 0) {\n <div class=\"tags\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" [@tag]>\n <span class=\"tag-label\">{{getOptionLabel(option)}}</span>\n @if (!isDisabled) {\n <button\n class=\"tag-remove\"\n (click)=\"removeOption(option, $event)\"\n [attr.aria-label]=\"'Remove ' + getOptionLabel(option)\"\n type=\"button\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n </span>\n }\n </div>\n } @else {\n <!-- Single Select or Placeholder -->\n <span class=\"placeholder\" [class.has-value]=\"selectedOptions().length > 0\">\n {{displayText()}}\n </span>\n }\n </div>\n\n <!-- Actions (Clear, Loading, Arrow) -->\n <div class=\"select-actions\">\n @if (isLoading || isLoadingAsync()) {\n <div class=\"spinner\"></div>\n }\n @if (isClearable && selectedOptions().length > 0 && !isDisabled && !isLoading && !isLoadingAsync()) {\n <button\n class=\"clear-button\"\n (click)=\"clearSelection($event)\"\n aria-label=\"Clear selection\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n <span class=\"separator\"></span>\n <span class=\"arrow\" [class.open]=\"isOpen()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\"></path>\n </svg>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n @if (isOpen()) {\n <div\n #menuRef\n class=\"dropdown {{menuPlacement}}\"\n [class.fixed]=\"menuPosition === 'fixed'\"\n [style.max-height]=\"maxHeight\"\n role=\"listbox\"\n [attr.aria-multiselectable]=\"isMulti\"\n [@dropdown]\n >\n <!-- Search Input -->\n @if (isSearchable) {\n <div class=\"search-container\">\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search...\"\n [value]=\"searchTerm()\"\n (input)=\"onSearchChange($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n aria-label=\"Search options\"\n aria-autocomplete=\"list\"\n />\n </div>\n }\n\n <!-- Options List -->\n <div\n class=\"options-list\"\n id=\"options-list\"\n (scroll)=\"onOptionsScroll($event)\"\n >\n <!-- Max Selection Message -->\n @if (isMaxSelectionReached()) {\n <div class=\"info-message max-selection\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{maxSelectedMessage}}\n </div>\n }\n\n <!-- Min Search Length Message -->\n @if (showMinSearchMessage()) {\n <div class=\"info-message min-search\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{minSearchMessage}} ({{searchTerm().length}}/{{minSearchLength}})\n </div>\n }\n\n @if (isLoadingAsync()) {\n <div class=\"loading-message\">{{loadingMessage()}}</div>\n } @else if (displayOptions().length === 0 && !showMinSearchMessage()) {\n <div class=\"no-options\">\n {{searchTerm() ? emptySearchText : emptyStateText}}\n </div>\n } @else if (!showMinSearchMessage()) {\n <!-- Select All (Multi-select only) -->\n @if (isMulti && showSelectAll && !searchTerm()) {\n <div class=\"select-all-container\">\n <button\n class=\"select-all-button\"\n (click)=\"allOptionsSelected() ? deselectAll() : selectAll()\"\n type=\"button\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"allOptionsSelected()\"\n [indeterminate]=\"someOptionsSelected()\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n <span class=\"select-all-text\">\n {{allOptionsSelected() ? deselectAllText : selectAllText}}\n </span>\n <span class=\"select-all-count\">\n ({{selectedOptions().length}}/{{getEnabledOptionsCount()}})\n </span>\n </button>\n </div>\n }\n\n <!-- Grouped Options -->\n @if (isGrouped && groupedOptions()) {\n @for (group of groupedOptions() | keyvalue; track trackByGroup($index, [$any(group.key), $any(group.value)])) {\n <div class=\"option-group\">\n <div class=\"option-group-label\">{{group.key}}</div>\n @for (option of $any(group.value); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n </div>\n }\n } @else {\n <!-- v1.2.0: Recent Selections Header -->\n @if (showRecentSelections && !searchTerm() && recentSelections().length > 0) {\n <div class=\"section-header\">{{recentSelectionsLabel}}</div>\n }\n\n <!-- Regular (Ungrouped) Options - Virtual Scroll or Standard -->\n @if (enableVirtualScroll) {\n <!-- Virtual Scroll Mode -->\n <cdk-virtual-scroll-viewport\n [itemSize]=\"virtualScrollItemSize\"\n [minBufferPx]=\"virtualScrollMinBufferPx\"\n [maxBufferPx]=\"virtualScrollMaxBufferPx\"\n [style.height.px]=\"maxHeight\"\n class=\"virtual-scroll-viewport\"\n >\n @for (option of (showRecentSelections ? displayOptionsWithRecent() : displayOptions()); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n [class.recent-option]=\"option.__isRecent__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n (mouseenter)=\"showTooltip(option, idx)\"\n (mouseleave)=\"hideTooltip()\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [attr.title]=\"showTooltips ? getTooltipContent(option) : null\"\n tabindex=\"-1\"\n >\n <!-- Custom Template or Default Rendering -->\n @if (optionTemplate) {\n <ng-container\n *ngTemplateOutlet=\"optionTemplate; context: { $implicit: option, index: idx, selected: isSelected(option) }\"\n ></ng-container>\n } @else {\n <!-- Default Option Content (same as before) -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n }\n </div>\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <!-- Standard Rendering (Non-Virtual Scroll) -->\n @for (option of (showRecentSelections ? displayOptionsWithRecent() : displayOptions()); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n [class.recent-option]=\"option.__isRecent__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n (mouseenter)=\"showTooltip(option, idx)\"\n (mouseleave)=\"hideTooltip()\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [attr.title]=\"showTooltips ? getTooltipContent(option) : null\"\n tabindex=\"-1\"\n >\n <!-- Custom Template or Default Rendering -->\n @if (optionTemplate) {\n <ng-container\n *ngTemplateOutlet=\"optionTemplate; context: { $implicit: option, index: idx, selected: isSelected(option) }\"\n ></ng-container>\n } @else {\n <!-- Default Option Content -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n }\n </div>\n }\n }\n }\n }\n </div>\n </div>\n }\n\n <!-- v1.2.0: Validation Message -->\n @if (validationMessage && validationState !== 'default') {\n <div class=\"validation-message {{getValidationClass()}}\">\n @if (showValidationIcon) {\n <span class=\"validation-icon\">\n @if (validationState === 'error') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n } @else if (validationState === 'warning') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n } @else if (validationState === 'success') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm-1.707 13.707l-3.5-3.5 1.414-1.414L9 11.586l5.293-5.293 1.414 1.414-6.5 6.5z\"/>\n </svg>\n } @else if (validationState === 'info') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-6h2v6zm0-8H9V5h2v2z\"/>\n </svg>\n }\n </span>\n }\n <span class=\"validation-text\">{{validationMessage}}</span>\n </div>\n }\n</div>\n\n<!-- Hidden native select for form compatibility -->\n@if (isMulti) {\n <select [name]=\"name\" [id]=\"id\" multiple style=\"display: none;\" [attr.aria-hidden]=\"true\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <option [value]=\"getOptionValue(option)\" selected>{{getOptionLabel(option)}}</option>\n }\n </select>\n} @else {\n @if (selectedOptions().length > 0) {\n <select [name]=\"name\" [id]=\"id\" style=\"display: none;\" [attr.aria-hidden]=\"true\">\n <option [value]=\"getOptionValue(selectedOptions()[0])\" selected>{{getOptionLabel(selectedOptions()[0])}}</option>\n </select>\n }\n}\n", styles: [":host{display:block;width:100%}.select-container{position:relative;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.smaller{font-size:11px}.small{font-size:13px}.medium{font-size:14px}.large{font-size:16px}.larger{font-size:18px}.xs .select-trigger{min-height:28px;padding:4px 8px}.sm .select-trigger{min-height:32px;padding:6px 10px}.md .select-trigger{min-height:40px;padding:8px 12px}.lg .select-trigger{min-height:48px;padding:10px 14px}.xl .select-trigger{min-height:56px;padding:12px 16px}.select-container.disabled{opacity:.6;cursor:not-allowed}.select-container.rtl{direction:rtl}.select-trigger{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1.5px solid #D1D5DB;border-radius:10px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);min-height:38px;gap:8px;box-shadow:0 1px 2px #0000000d}.select-trigger:hover:not(.disabled){border-color:#9ca3af;box-shadow:0 2px 4px #00000014}.select-trigger:focus,.select-trigger.focused{outline:none;border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-value{flex:1;display:flex;align-items:center;min-width:0;gap:4px}.placeholder{color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.placeholder.has-value{color:#333}.tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}.tag{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#e6f2ff;color:#0052cc;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #CCE0FF;box-shadow:0 1px 2px #0000000d}.tag.disabled{background:#f0f0f0;color:#666;border-color:#d9d9d9}.tag-label{line-height:1.2}.tag-remove{background:none;border:none;color:inherit;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;border-radius:2px;transition:all .15s}.tag-remove:hover{background:#0052cc26}.tag-remove svg{fill:currentColor}.select-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}.clear-button{background:none;border:none;color:#999;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s}.clear-button:hover{color:#333;background:#f0f0f0}.clear-button svg{fill:currentColor}.separator{width:1px;height:24px;background:#ccc;align-self:stretch}.arrow{color:#999;transition:transform .2s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;padding:4px}.arrow svg{fill:currentColor}.arrow.open{transform:rotate(180deg)}.spinner{width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #2684FF;border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.dropdown{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fffffffa;border:1px solid #E5E7EB;border-radius:12px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d;z-index:1000;overflow:hidden;backdrop-filter:blur(12px) saturate(180%);-webkit-backdrop-filter:blur(12px) saturate(180%)}.dropdown.fixed{position:fixed}.dropdown.top{top:auto;bottom:calc(100% + 4px)}.search-container{padding:8px;border-bottom:1px solid #f0f0f0;background:#fff}.search-input{width:100%;padding:6px 8px;border:1px solid #cccccc;border-radius:3px;font-size:inherit;outline:none;transition:border-color .15s}.search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.options-list{max-height:inherit;overflow-y:auto;overflow-x:hidden;padding:4px 0}.options-list::-webkit-scrollbar{width:8px}.options-list::-webkit-scrollbar-track{background:transparent}.options-list::-webkit-scrollbar-thumb{background:#d9d9d9;border-radius:4px}.options-list::-webkit-scrollbar-thumb:hover{background:#b3b3b3}.select-all-container{padding:8px;border-bottom:1px solid #E5E7EB;background:#f9fafb}.select-all-button{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;background:#fff;border:1px solid #D1D5DB;border-radius:6px;cursor:pointer;transition:all .15s;font-size:inherit;color:inherit;font-family:inherit}.select-all-button:hover{background:#f3f4f6;border-color:#9ca3af}.select-all-button input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.select-all-text{flex:1;text-align:left;font-weight:500}.select-all-count{color:#6b7280;font-size:.9em}.option-group{margin:4px 0}.option-group-label{padding:8px 12px 4px;font-size:.75em;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:1}.option-group .option{padding-left:20px}.option{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;transition:all .15s cubic-bezier(.4,0,.2,1);position:relative;min-height:40px}.option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.option-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6}.option-icon img{width:100%;height:100%;object-fit:cover}.option-badge{padding:2px 8px;border-radius:12px;font-size:.75em;font-weight:500;color:#374151;background:#e5e7eb;flex-shrink:0;white-space:nowrap}.option:hover:not(.disabled):not(.create-option){background:#deebff}.option.highlighted{background:#deebff}.option.selected:not(.create-option){background:#e6f2ff;font-weight:500}.option.disabled{opacity:.5;cursor:not-allowed;background:transparent!important}.option.hidden{display:none}.option.create-option{color:#2684ff;font-weight:500;background:#f0f6ff}.option.create-option:hover{background:#e6f2ff}.option input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.option-label{font-weight:500;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.option-description{font-size:.875em;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.check-icon{color:#2684ff;display:flex;align-items:center;margin-left:auto}.check-icon svg{fill:currentColor}.no-options,.loading-message{padding:16px 12px;text-align:center;color:#999;font-size:.95em}.info-message{padding:12px;margin:8px;border-radius:6px;font-size:.9em;display:flex;align-items:center;gap:8px}.info-message.max-selection{background:#fff4e5;color:#f57c00;border:1px solid #FFE0B2}.info-message.min-search{background:#e3f2fd;color:#1976d2;border:1px solid #BBDEFB}.info-message svg{flex-shrink:0}.rtl .select-actions,.rtl .tags,.rtl .option{flex-direction:row-reverse}.theme-blue .select-trigger:focus,.theme-blue .select-trigger.focused,.theme-blue .select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-blue .option:hover:not(.disabled):not(.create-option),.theme-blue .option.highlighted{background:#deebff}.theme-blue .option.selected:not(.create-option){background:#e6f2ff}.theme-blue .tag{background:#e6f2ff;color:#0052cc;border-color:#cce0ff}.theme-blue .check-icon,.theme-blue .option.create-option{color:#2684ff}.theme-blue .spinner{border-top-color:#2684ff}.theme-blue .search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-purple .select-trigger:focus,.theme-purple .select-trigger.focused,.theme-purple .select-trigger.open{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-purple .option:hover:not(.disabled):not(.create-option),.theme-purple .option.highlighted{background:#f3e8ff}.theme-purple .option.selected:not(.create-option){background:#faf5ff}.theme-purple .tag{background:#faf5ff;color:#7e22ce;border-color:#e9d5ff}.theme-purple .check-icon,.theme-purple .option.create-option{color:#9333ea}.theme-purple .spinner{border-top-color:#9333ea}.theme-purple .search-input:focus{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-green .select-trigger:focus,.theme-green .select-trigger.focused,.theme-green .select-trigger.open{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-green .option:hover:not(.disabled):not(.create-option),.theme-green .option.highlighted{background:#d1fae5}.theme-green .option.selected:not(.create-option){background:#ecfdf5}.theme-green .tag{background:#ecfdf5;color:#059669;border-color:#a7f3d0}.theme-green .check-icon,.theme-green .option.create-option{color:#10b981}.theme-green .spinner{border-top-color:#10b981}.theme-green .search-input:focus{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-red .select-trigger:focus,.theme-red .select-trigger.focused,.theme-red .select-trigger.open{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-red .option:hover:not(.disabled):not(.create-option),.theme-red .option.highlighted{background:#fee2e2}.theme-red .option.selected:not(.create-option){background:#fef2f2}.theme-red .tag{background:#fef2f2;color:#dc2626;border-color:#fecaca}.theme-red .check-icon,.theme-red .option.create-option{color:#ef4444}.theme-red .spinner{border-top-color:#ef4444}.theme-red .search-input:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-orange .select-trigger:focus,.theme-orange .select-trigger.focused,.theme-orange .select-trigger.open{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-orange .option:hover:not(.disabled):not(.create-option),.theme-orange .option.highlighted{background:#ffedd5}.theme-orange .option.selected:not(.create-option){background:#fff7ed}.theme-orange .tag{background:#fff7ed;color:#ea580c;border-color:#fed7aa}.theme-orange .check-icon,.theme-orange .option.create-option{color:#f97316}.theme-orange .spinner{border-top-color:#f97316}.theme-orange .search-input:focus{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-pink .select-trigger:focus,.theme-pink .select-trigger.focused,.theme-pink .select-trigger.open{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-pink .option:hover:not(.disabled):not(.create-option),.theme-pink .option.highlighted{background:#fce7f3}.theme-pink .option.selected:not(.create-option){background:#fdf2f8}.theme-pink .tag{background:#fdf2f8;color:#db2777;border-color:#fbcfe8}.theme-pink .check-icon,.theme-pink .option.create-option{color:#ec4899}.theme-pink .spinner{border-top-color:#ec4899}.theme-pink .search-input:focus{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-dark .select-trigger:focus,.theme-dark .select-trigger.focused,.theme-dark .select-trigger.open{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.theme-dark .option:hover:not(.disabled):not(.create-option),.theme-dark .option.highlighted{background:#e5e7eb}.theme-dark .option.selected:not(.create-option){background:#f3f4f6}.theme-dark .tag{background:#f3f4f6;color:#111827;border-color:#d1d5db}.theme-dark .check-icon,.theme-dark .option.create-option{color:#1f2937}.theme-dark .spinner{border-top-color:#1f2937}.theme-dark .search-input:focus{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.select-container.has-validation .select-trigger{transition:all .2s cubic-bezier(.4,0,.2,1)}.select-container.validation-error .select-trigger{border-color:#ef4444!important}.select-container.validation-error .select-trigger:focus,.select-container.validation-error .select-trigger.focused,.select-container.validation-error .select-trigger.open{border-color:#ef4444!important;box-shadow:0 0 0 3px #ef44441a,0 1px 2px #0000000d}.select-container.validation-warning .select-trigger{border-color:#f59e0b!important}.select-container.validation-warning .select-trigger:focus,.select-container.validation-warning .select-trigger.focused,.select-container.validation-warning .select-trigger.open{border-color:#f59e0b!important;box-shadow:0 0 0 3px #f59e0b1a,0 1px 2px #0000000d}.select-container.validation-success .select-trigger{border-color:#10b981!important}.select-container.validation-success .select-trigger:focus,.select-container.validation-success .select-trigger.focused,.select-container.validation-success .select-trigger.open{border-color:#10b981!important;box-shadow:0 0 0 3px #10b9811a,0 1px 2px #0000000d}.select-container.validation-info .select-trigger{border-color:#3b82f6!important}.select-container.validation-info .select-trigger:focus,.select-container.validation-info .select-trigger.focused,.select-container.validation-info .select-trigger.open{border-color:#3b82f6!important;box-shadow:0 0 0 3px #3b82f61a,0 1px 2px #0000000d}.validation-message{display:flex;align-items:center;gap:6px;margin-top:6px;font-size:13px;line-height:1.4;padding:4px 8px;border-radius:6px;transition:all .2s ease}.validation-message .validation-icon{display:flex;align-items:center;flex-shrink:0}.validation-message .validation-icon svg{display:block}.validation-message .validation-text{flex:1}.validation-message.validation-error{color:#dc2626;background:#fef2f2;border-left:3px solid #EF4444}.validation-message.validation-warning{color:#d97706;background:#fffbeb;border-left:3px solid #F59E0B}.validation-message.validation-success{color:#059669;background:#f0fdf4;border-left:3px solid #10B981}.validation-message.validation-info{color:#2563eb;background:#eff6ff;border-left:3px solid #3B82F6}.option.recent-option{position:relative;background:#fafbfc}.option.recent-option:after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(to bottom,#2684ff,#0052cc);border-radius:0 3px 3px 0}.option.recent-option:hover:not(.disabled){background:#f4f5f7!important}.option.recent-option.selected{background:#e6f2ff!important}.option.recent-option.selected:hover:not(.disabled){background:#d4e8ff!important}.section-header{padding:8px 12px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:#6b7280;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:2}.virtual-scroll-viewport{width:100%}.virtual-scroll-viewport ::ng-deep .cdk-virtual-scroll-content-wrapper{width:100%}.virtual-scroll-viewport ::ng-deep .cdk-virtual-scroll-spacer{display:none}.virtual-scroll-viewport .option{width:100%;box-sizing:border-box}@keyframes copyFlash{0%,to{background:transparent}50%{background:#2684ff1a}}.select-container.copying{animation:copyFlash .5s ease}.options-list.scrolling{position:relative}.options-list.scrolling:after{content:\"\";position:absolute;bottom:0;left:50%;transform:translate(-50%);width:20px;height:20px;border:2px solid #E5E7EB;border-top-color:#2684ff;border-radius:50%;animation:spin .8s linear infinite}.option[title]{cursor:help}.option.highlighted{position:relative}.option.highlighted:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:2px;background:#2684ff}@keyframes typeAheadPulse{0%,to{opacity:1}50%{opacity:.7}}.options-list.type-ahead-active .option.highlighted{animation:typeAheadPulse 1s ease-in-out}.select-container:focus-visible{outline:2px solid #2684FF;outline-offset:2px}@media (prefers-contrast: high){.select-trigger{border-width:2px}.option.highlighted{outline:2px solid currentColor;outline-offset:-2px}.validation-message{border-width:2px}}@media (prefers-reduced-motion: reduce){.select-trigger,.option,.tag,.dropdown,.validation-message{transition:none!important;animation:none!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", outputs: ["clickOutside"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i4.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "component", type: i4.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "pipe", type: i2.KeyValuePipe, name: "keyvalue" }], animations: selectAnimations });
689
993
  }
690
994
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PerfectSelectComponent, decorators: [{
691
995
  type: Component,
692
- args: [{ selector: 'ng-perfect-select', standalone: true, imports: [CommonModule, FormsModule, ClickOutsideDirective], animations: selectAnimations, providers: [{
996
+ args: [{ selector: 'ng-perfect-select', standalone: true, imports: [CommonModule, FormsModule, ClickOutsideDirective, ScrollingModule], animations: selectAnimations, providers: [{
693
997
  provide: NG_VALUE_ACCESSOR,
694
- useExisting: PerfectSelectComponent,
998
+ useExisting: forwardRef(() => PerfectSelectComponent),
695
999
  multi: true
696
- }], template: "<div\n #selectContainer\n class=\"select-container {{selectSize}} {{containerSize}} theme-{{theme}}\"\n [class.disabled]=\"isDisabled\"\n [class.rtl]=\"isRtl\"\n (clickOutside)=\"onClickOutside()\"\n role=\"combobox\"\n tabindex=\"0\"\n [attr.aria-controls]=\"'options-list'\"\n [attr.aria-expanded]=\"isOpen()\"\n (focus)=\"openMenuOnFocus && !isOpen() && toggleDropdown(); focus.emit()\"\n [style]=\"customStyles.container || ''\"\n>\n <!-- Main Select Trigger -->\n <div\n class=\"select-trigger\"\n [class.open]=\"isOpen()\"\n [class.focused]=\"isOpen()\"\n (click)=\"openMenuOnClick && toggleDropdown()\"\n [attr.tabindex]=\"isDisabled ? -1 : 0\"\n role=\"button\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-label]=\"placeholder\"\n >\n <!-- Selected Value Display -->\n <div class=\"select-value\">\n <!-- Multi-select Tags -->\n @if (isMulti && selectedOptions().length > 0) {\n <div class=\"tags\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" [@tag]>\n <span class=\"tag-label\">{{getOptionLabel(option)}}</span>\n @if (!isDisabled) {\n <button\n class=\"tag-remove\"\n (click)=\"removeOption(option, $event)\"\n [attr.aria-label]=\"'Remove ' + getOptionLabel(option)\"\n type=\"button\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n </span>\n }\n </div>\n } @else {\n <!-- Single Select or Placeholder -->\n <span class=\"placeholder\" [class.has-value]=\"selectedOptions().length > 0\">\n {{displayText()}}\n </span>\n }\n </div>\n\n <!-- Actions (Clear, Loading, Arrow) -->\n <div class=\"select-actions\">\n @if (isLoading || isLoadingAsync()) {\n <div class=\"spinner\"></div>\n }\n @if (isClearable && selectedOptions().length > 0 && !isDisabled && !isLoading && !isLoadingAsync()) {\n <button\n class=\"clear-button\"\n (click)=\"clearSelection($event)\"\n aria-label=\"Clear selection\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n <span class=\"separator\"></span>\n <span class=\"arrow\" [class.open]=\"isOpen()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\"></path>\n </svg>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n @if (isOpen()) {\n <div\n #menuRef\n class=\"dropdown {{menuPlacement}}\"\n [class.fixed]=\"menuPosition === 'fixed'\"\n [style.max-height]=\"maxHeight\"\n role=\"listbox\"\n [attr.aria-multiselectable]=\"isMulti\"\n [@dropdown]\n >\n <!-- Search Input -->\n @if (isSearchable) {\n <div class=\"search-container\">\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search...\"\n [value]=\"searchTerm()\"\n (input)=\"onSearchChange($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n aria-label=\"Search options\"\n aria-autocomplete=\"list\"\n />\n </div>\n }\n\n <!-- Options List -->\n <div class=\"options-list\" id=\"options-list\">\n <!-- Max Selection Message -->\n @if (isMaxSelectionReached()) {\n <div class=\"info-message max-selection\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{maxSelectedMessage}}\n </div>\n }\n\n <!-- Min Search Length Message -->\n @if (showMinSearchMessage()) {\n <div class=\"info-message min-search\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{minSearchMessage}} ({{searchTerm().length}}/{{minSearchLength}})\n </div>\n }\n\n @if (isLoadingAsync()) {\n <div class=\"loading-message\">{{loadingMessage()}}</div>\n } @else if (displayOptions().length === 0 && !showMinSearchMessage()) {\n <div class=\"no-options\">\n {{searchTerm() ? emptySearchText : emptyStateText}}\n </div>\n } @else if (!showMinSearchMessage()) {\n <!-- Select All (Multi-select only) -->\n @if (isMulti && showSelectAll && !searchTerm()) {\n <div class=\"select-all-container\">\n <button\n class=\"select-all-button\"\n (click)=\"allOptionsSelected() ? deselectAll() : selectAll()\"\n type=\"button\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"allOptionsSelected()\"\n [indeterminate]=\"someOptionsSelected()\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n <span class=\"select-all-text\">\n {{allOptionsSelected() ? deselectAllText : selectAllText}}\n </span>\n <span class=\"select-all-count\">\n ({{selectedOptions().length}}/{{getEnabledOptionsCount()}})\n </span>\n </button>\n </div>\n }\n\n <!-- Grouped Options -->\n @if (isGrouped && groupedOptions()) {\n @for (group of groupedOptions() | keyvalue; track trackByGroup($index, [$any(group.key), $any(group.value)])) {\n <div class=\"option-group\">\n <div class=\"option-group-label\">{{group.key}}</div>\n @for (option of $any(group.value); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n </div>\n }\n } @else {\n <!-- Regular (Ungrouped) Options -->\n @for (option of displayOptions(); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n</div>\n\n<!-- Hidden native select for form compatibility -->\n@if (isMulti) {\n <select [name]=\"name\" [id]=\"id\" multiple style=\"display: none;\" [attr.aria-hidden]=\"true\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <option [value]=\"getOptionValue(option)\" selected>{{getOptionLabel(option)}}</option>\n }\n </select>\n} @else {\n @if (selectedOptions().length > 0) {\n <select [name]=\"name\" [id]=\"id\" style=\"display: none;\" [attr.aria-hidden]=\"true\">\n <option [value]=\"getOptionValue(selectedOptions()[0])\" selected>{{getOptionLabel(selectedOptions()[0])}}</option>\n </select>\n }\n}\n", styles: [":host{display:block;width:100%}.select-container{position:relative;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.smaller{font-size:11px}.small{font-size:13px}.medium{font-size:14px}.large{font-size:16px}.larger{font-size:18px}.xs .select-trigger{min-height:28px;padding:4px 8px}.sm .select-trigger{min-height:32px;padding:6px 10px}.md .select-trigger{min-height:40px;padding:8px 12px}.lg .select-trigger{min-height:48px;padding:10px 14px}.xl .select-trigger{min-height:56px;padding:12px 16px}.select-container.disabled{opacity:.6;cursor:not-allowed}.select-container.rtl{direction:rtl}.select-trigger{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1.5px solid #D1D5DB;border-radius:10px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);min-height:38px;gap:8px;box-shadow:0 1px 2px #0000000d}.select-trigger:hover:not(.disabled){border-color:#9ca3af;box-shadow:0 2px 4px #00000014}.select-trigger:focus,.select-trigger.focused{outline:none;border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-value{flex:1;display:flex;align-items:center;min-width:0;gap:4px}.placeholder{color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.placeholder.has-value{color:#333}.tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}.tag{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#e6f2ff;color:#0052cc;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #CCE0FF;box-shadow:0 1px 2px #0000000d}.tag.disabled{background:#f0f0f0;color:#666;border-color:#d9d9d9}.tag-label{line-height:1.2}.tag-remove{background:none;border:none;color:inherit;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;border-radius:2px;transition:all .15s}.tag-remove:hover{background:#0052cc26}.tag-remove svg{fill:currentColor}.select-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}.clear-button{background:none;border:none;color:#999;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s}.clear-button:hover{color:#333;background:#f0f0f0}.clear-button svg{fill:currentColor}.separator{width:1px;height:24px;background:#ccc;align-self:stretch}.arrow{color:#999;transition:transform .2s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;padding:4px}.arrow svg{fill:currentColor}.arrow.open{transform:rotate(180deg)}.spinner{width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #2684FF;border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.dropdown{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fffffffa;border:1px solid #E5E7EB;border-radius:12px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d;z-index:1000;overflow:hidden;backdrop-filter:blur(12px) saturate(180%);-webkit-backdrop-filter:blur(12px) saturate(180%)}.dropdown.fixed{position:fixed}.dropdown.top{top:auto;bottom:calc(100% + 4px)}.search-container{padding:8px;border-bottom:1px solid #f0f0f0;background:#fff}.search-input{width:100%;padding:6px 8px;border:1px solid #cccccc;border-radius:3px;font-size:inherit;outline:none;transition:border-color .15s}.search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.options-list{max-height:inherit;overflow-y:auto;overflow-x:hidden;padding:4px 0}.options-list::-webkit-scrollbar{width:8px}.options-list::-webkit-scrollbar-track{background:transparent}.options-list::-webkit-scrollbar-thumb{background:#d9d9d9;border-radius:4px}.options-list::-webkit-scrollbar-thumb:hover{background:#b3b3b3}.select-all-container{padding:8px;border-bottom:1px solid #E5E7EB;background:#f9fafb}.select-all-button{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;background:#fff;border:1px solid #D1D5DB;border-radius:6px;cursor:pointer;transition:all .15s;font-size:inherit;color:inherit;font-family:inherit}.select-all-button:hover{background:#f3f4f6;border-color:#9ca3af}.select-all-button input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.select-all-text{flex:1;text-align:left;font-weight:500}.select-all-count{color:#6b7280;font-size:.9em}.option-group{margin:4px 0}.option-group-label{padding:8px 12px 4px;font-size:.75em;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:1}.option-group .option{padding-left:20px}.option{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;transition:all .15s cubic-bezier(.4,0,.2,1);position:relative;min-height:40px}.option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.option-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6}.option-icon img{width:100%;height:100%;object-fit:cover}.option-badge{padding:2px 8px;border-radius:12px;font-size:.75em;font-weight:500;color:#374151;background:#e5e7eb;flex-shrink:0;white-space:nowrap}.option:hover:not(.disabled):not(.create-option){background:#deebff}.option.highlighted{background:#deebff}.option.selected:not(.create-option){background:#e6f2ff;font-weight:500}.option.disabled{opacity:.5;cursor:not-allowed;background:transparent!important}.option.hidden{display:none}.option.create-option{color:#2684ff;font-weight:500;background:#f0f6ff}.option.create-option:hover{background:#e6f2ff}.option input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.option-label{font-weight:500;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.option-description{font-size:.875em;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.check-icon{color:#2684ff;display:flex;align-items:center;margin-left:auto}.check-icon svg{fill:currentColor}.no-options,.loading-message{padding:16px 12px;text-align:center;color:#999;font-size:.95em}.info-message{padding:12px;margin:8px;border-radius:6px;font-size:.9em;display:flex;align-items:center;gap:8px}.info-message.max-selection{background:#fff4e5;color:#f57c00;border:1px solid #FFE0B2}.info-message.min-search{background:#e3f2fd;color:#1976d2;border:1px solid #BBDEFB}.info-message svg{flex-shrink:0}.rtl .select-actions,.rtl .tags,.rtl .option{flex-direction:row-reverse}.theme-blue .select-trigger:focus,.theme-blue .select-trigger.focused,.theme-blue .select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-blue .option:hover:not(.disabled):not(.create-option),.theme-blue .option.highlighted{background:#deebff}.theme-blue .option.selected:not(.create-option){background:#e6f2ff}.theme-blue .tag{background:#e6f2ff;color:#0052cc;border-color:#cce0ff}.theme-blue .check-icon,.theme-blue .option.create-option{color:#2684ff}.theme-blue .spinner{border-top-color:#2684ff}.theme-blue .search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-purple .select-trigger:focus,.theme-purple .select-trigger.focused,.theme-purple .select-trigger.open{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-purple .option:hover:not(.disabled):not(.create-option),.theme-purple .option.highlighted{background:#f3e8ff}.theme-purple .option.selected:not(.create-option){background:#faf5ff}.theme-purple .tag{background:#faf5ff;color:#7e22ce;border-color:#e9d5ff}.theme-purple .check-icon,.theme-purple .option.create-option{color:#9333ea}.theme-purple .spinner{border-top-color:#9333ea}.theme-purple .search-input:focus{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-green .select-trigger:focus,.theme-green .select-trigger.focused,.theme-green .select-trigger.open{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-green .option:hover:not(.disabled):not(.create-option),.theme-green .option.highlighted{background:#d1fae5}.theme-green .option.selected:not(.create-option){background:#ecfdf5}.theme-green .tag{background:#ecfdf5;color:#059669;border-color:#a7f3d0}.theme-green .check-icon,.theme-green .option.create-option{color:#10b981}.theme-green .spinner{border-top-color:#10b981}.theme-green .search-input:focus{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-red .select-trigger:focus,.theme-red .select-trigger.focused,.theme-red .select-trigger.open{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-red .option:hover:not(.disabled):not(.create-option),.theme-red .option.highlighted{background:#fee2e2}.theme-red .option.selected:not(.create-option){background:#fef2f2}.theme-red .tag{background:#fef2f2;color:#dc2626;border-color:#fecaca}.theme-red .check-icon,.theme-red .option.create-option{color:#ef4444}.theme-red .spinner{border-top-color:#ef4444}.theme-red .search-input:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-orange .select-trigger:focus,.theme-orange .select-trigger.focused,.theme-orange .select-trigger.open{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-orange .option:hover:not(.disabled):not(.create-option),.theme-orange .option.highlighted{background:#ffedd5}.theme-orange .option.selected:not(.create-option){background:#fff7ed}.theme-orange .tag{background:#fff7ed;color:#ea580c;border-color:#fed7aa}.theme-orange .check-icon,.theme-orange .option.create-option{color:#f97316}.theme-orange .spinner{border-top-color:#f97316}.theme-orange .search-input:focus{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-pink .select-trigger:focus,.theme-pink .select-trigger.focused,.theme-pink .select-trigger.open{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-pink .option:hover:not(.disabled):not(.create-option),.theme-pink .option.highlighted{background:#fce7f3}.theme-pink .option.selected:not(.create-option){background:#fdf2f8}.theme-pink .tag{background:#fdf2f8;color:#db2777;border-color:#fbcfe8}.theme-pink .check-icon,.theme-pink .option.create-option{color:#ec4899}.theme-pink .spinner{border-top-color:#ec4899}.theme-pink .search-input:focus{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-dark .select-trigger:focus,.theme-dark .select-trigger.focused,.theme-dark .select-trigger.open{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.theme-dark .option:hover:not(.disabled):not(.create-option),.theme-dark .option.highlighted{background:#e5e7eb}.theme-dark .option.selected:not(.create-option){background:#f3f4f6}.theme-dark .tag{background:#f3f4f6;color:#111827;border-color:#d1d5db}.theme-dark .check-icon,.theme-dark .option.create-option{color:#1f2937}.theme-dark .spinner{border-top-color:#1f2937}.theme-dark .search-input:focus{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}\n"] }]
1000
+ }], template: "<div\n #selectContainer\n class=\"select-container {{selectSize}} {{containerSize}} theme-{{theme}} {{getValidationClass()}}\"\n [class.disabled]=\"isDisabled\"\n [class.rtl]=\"isRtl\"\n [class.has-validation]=\"validationState !== 'default'\"\n (clickOutside)=\"onClickOutside()\"\n role=\"combobox\"\n tabindex=\"0\"\n [attr.aria-controls]=\"'options-list'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"validationState === 'error'\"\n (focus)=\"openMenuOnFocus && !isOpen() && toggleDropdown(); focus.emit()\"\n [style]=\"customStyles.container || ''\"\n>\n <!-- Main Select Trigger -->\n <div\n class=\"select-trigger\"\n [class.open]=\"isOpen()\"\n [class.focused]=\"isOpen()\"\n (click)=\"openMenuOnClick && toggleDropdown()\"\n [attr.tabindex]=\"isDisabled ? -1 : 0\"\n role=\"button\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-label]=\"placeholder\"\n >\n <!-- Selected Value Display -->\n <div class=\"select-value\">\n <!-- Multi-select Tags -->\n @if (isMulti && selectedOptions().length > 0) {\n <div class=\"tags\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" [@tag]>\n <span class=\"tag-label\">{{getOptionLabel(option)}}</span>\n @if (!isDisabled) {\n <button\n class=\"tag-remove\"\n (click)=\"removeOption(option, $event)\"\n [attr.aria-label]=\"'Remove ' + getOptionLabel(option)\"\n type=\"button\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n </span>\n }\n </div>\n } @else {\n <!-- Single Select or Placeholder -->\n <span class=\"placeholder\" [class.has-value]=\"selectedOptions().length > 0\">\n {{displayText()}}\n </span>\n }\n </div>\n\n <!-- Actions (Clear, Loading, Arrow) -->\n <div class=\"select-actions\">\n @if (isLoading || isLoadingAsync()) {\n <div class=\"spinner\"></div>\n }\n @if (isClearable && selectedOptions().length > 0 && !isDisabled && !isLoading && !isLoadingAsync()) {\n <button\n class=\"clear-button\"\n (click)=\"clearSelection($event)\"\n aria-label=\"Clear selection\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n <span class=\"separator\"></span>\n <span class=\"arrow\" [class.open]=\"isOpen()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\"></path>\n </svg>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n @if (isOpen()) {\n <div\n #menuRef\n class=\"dropdown {{menuPlacement}}\"\n [class.fixed]=\"menuPosition === 'fixed'\"\n [style.max-height]=\"maxHeight\"\n role=\"listbox\"\n [attr.aria-multiselectable]=\"isMulti\"\n [@dropdown]\n >\n <!-- Search Input -->\n @if (isSearchable) {\n <div class=\"search-container\">\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search...\"\n [value]=\"searchTerm()\"\n (input)=\"onSearchChange($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n aria-label=\"Search options\"\n aria-autocomplete=\"list\"\n />\n </div>\n }\n\n <!-- Options List -->\n <div\n class=\"options-list\"\n id=\"options-list\"\n (scroll)=\"onOptionsScroll($event)\"\n >\n <!-- Max Selection Message -->\n @if (isMaxSelectionReached()) {\n <div class=\"info-message max-selection\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{maxSelectedMessage}}\n </div>\n }\n\n <!-- Min Search Length Message -->\n @if (showMinSearchMessage()) {\n <div class=\"info-message min-search\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n {{minSearchMessage}} ({{searchTerm().length}}/{{minSearchLength}})\n </div>\n }\n\n @if (isLoadingAsync()) {\n <div class=\"loading-message\">{{loadingMessage()}}</div>\n } @else if (displayOptions().length === 0 && !showMinSearchMessage()) {\n <div class=\"no-options\">\n {{searchTerm() ? emptySearchText : emptyStateText}}\n </div>\n } @else if (!showMinSearchMessage()) {\n <!-- Select All (Multi-select only) -->\n @if (isMulti && showSelectAll && !searchTerm()) {\n <div class=\"select-all-container\">\n <button\n class=\"select-all-button\"\n (click)=\"allOptionsSelected() ? deselectAll() : selectAll()\"\n type=\"button\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"allOptionsSelected()\"\n [indeterminate]=\"someOptionsSelected()\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n <span class=\"select-all-text\">\n {{allOptionsSelected() ? deselectAllText : selectAllText}}\n </span>\n <span class=\"select-all-count\">\n ({{selectedOptions().length}}/{{getEnabledOptionsCount()}})\n </span>\n </button>\n </div>\n }\n\n <!-- Grouped Options -->\n @if (isGrouped && groupedOptions()) {\n @for (group of groupedOptions() | keyvalue; track trackByGroup($index, [$any(group.key), $any(group.value)])) {\n <div class=\"option-group\">\n <div class=\"option-group-label\">{{group.key}}</div>\n @for (option of $any(group.value); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n </div>\n }\n } @else {\n <!-- v1.2.0: Recent Selections Header -->\n @if (showRecentSelections && !searchTerm() && recentSelections().length > 0) {\n <div class=\"section-header\">{{recentSelectionsLabel}}</div>\n }\n\n <!-- Regular (Ungrouped) Options - Virtual Scroll or Standard -->\n @if (enableVirtualScroll) {\n <!-- Virtual Scroll Mode -->\n <cdk-virtual-scroll-viewport\n [itemSize]=\"virtualScrollItemSize\"\n [minBufferPx]=\"virtualScrollMinBufferPx\"\n [maxBufferPx]=\"virtualScrollMaxBufferPx\"\n [style.height.px]=\"maxHeight\"\n class=\"virtual-scroll-viewport\"\n >\n @for (option of (showRecentSelections ? displayOptionsWithRecent() : displayOptions()); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n [class.recent-option]=\"option.__isRecent__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n (mouseenter)=\"showTooltip(option, idx)\"\n (mouseleave)=\"hideTooltip()\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [attr.title]=\"showTooltips ? getTooltipContent(option) : null\"\n tabindex=\"-1\"\n >\n <!-- Custom Template or Default Rendering -->\n @if (optionTemplate) {\n <ng-container\n *ngTemplateOutlet=\"optionTemplate; context: { $implicit: option, index: idx, selected: isSelected(option) }\"\n ></ng-container>\n } @else {\n <!-- Default Option Content (same as before) -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n }\n </div>\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <!-- Standard Rendering (Non-Virtual Scroll) -->\n @for (option of (showRecentSelections ? displayOptionsWithRecent() : displayOptions()); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [class.hidden]=\"hideSelectedOptions && isSelected(option)\"\n [class.create-option]=\"option.__isCreate__\"\n [class.recent-option]=\"option.__isRecent__\"\n (click)=\"selectOption(option)\"\n (keydown)=\"$event.key === 'Enter' && selectOption(option)\"\n (mouseenter)=\"showTooltip(option, idx)\"\n (mouseleave)=\"hideTooltip()\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option) || (isMaxSelectionReached() && !isSelected(option))\"\n [attr.title]=\"showTooltips ? getTooltipContent(option) : null\"\n tabindex=\"-1\"\n >\n <!-- Custom Template or Default Rendering -->\n @if (optionTemplate) {\n <ng-container\n *ngTemplateOutlet=\"optionTemplate; context: { $implicit: option, index: idx, selected: isSelected(option) }\"\n ></ng-container>\n } @else {\n <!-- Default Option Content -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n }\n </div>\n }\n }\n }\n }\n </div>\n </div>\n }\n\n <!-- v1.2.0: Validation Message -->\n @if (validationMessage && validationState !== 'default') {\n <div class=\"validation-message {{getValidationClass()}}\">\n @if (showValidationIcon) {\n <span class=\"validation-icon\">\n @if (validationState === 'error') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n } @else if (validationState === 'warning') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z\"/>\n </svg>\n } @else if (validationState === 'success') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm-1.707 13.707l-3.5-3.5 1.414-1.414L9 11.586l5.293-5.293 1.414 1.414-6.5 6.5z\"/>\n </svg>\n } @else if (validationState === 'info') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0zm1 15H9v-6h2v6zm0-8H9V5h2v2z\"/>\n </svg>\n }\n </span>\n }\n <span class=\"validation-text\">{{validationMessage}}</span>\n </div>\n }\n</div>\n\n<!-- Hidden native select for form compatibility -->\n@if (isMulti) {\n <select [name]=\"name\" [id]=\"id\" multiple style=\"display: none;\" [attr.aria-hidden]=\"true\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <option [value]=\"getOptionValue(option)\" selected>{{getOptionLabel(option)}}</option>\n }\n </select>\n} @else {\n @if (selectedOptions().length > 0) {\n <select [name]=\"name\" [id]=\"id\" style=\"display: none;\" [attr.aria-hidden]=\"true\">\n <option [value]=\"getOptionValue(selectedOptions()[0])\" selected>{{getOptionLabel(selectedOptions()[0])}}</option>\n </select>\n }\n}\n", styles: [":host{display:block;width:100%}.select-container{position:relative;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.smaller{font-size:11px}.small{font-size:13px}.medium{font-size:14px}.large{font-size:16px}.larger{font-size:18px}.xs .select-trigger{min-height:28px;padding:4px 8px}.sm .select-trigger{min-height:32px;padding:6px 10px}.md .select-trigger{min-height:40px;padding:8px 12px}.lg .select-trigger{min-height:48px;padding:10px 14px}.xl .select-trigger{min-height:56px;padding:12px 16px}.select-container.disabled{opacity:.6;cursor:not-allowed}.select-container.rtl{direction:rtl}.select-trigger{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1.5px solid #D1D5DB;border-radius:10px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);min-height:38px;gap:8px;box-shadow:0 1px 2px #0000000d}.select-trigger:hover:not(.disabled){border-color:#9ca3af;box-shadow:0 2px 4px #00000014}.select-trigger:focus,.select-trigger.focused{outline:none;border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-value{flex:1;display:flex;align-items:center;min-width:0;gap:4px}.placeholder{color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.placeholder.has-value{color:#333}.tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}.tag{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#e6f2ff;color:#0052cc;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #CCE0FF;box-shadow:0 1px 2px #0000000d}.tag.disabled{background:#f0f0f0;color:#666;border-color:#d9d9d9}.tag-label{line-height:1.2}.tag-remove{background:none;border:none;color:inherit;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;border-radius:2px;transition:all .15s}.tag-remove:hover{background:#0052cc26}.tag-remove svg{fill:currentColor}.select-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}.clear-button{background:none;border:none;color:#999;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s}.clear-button:hover{color:#333;background:#f0f0f0}.clear-button svg{fill:currentColor}.separator{width:1px;height:24px;background:#ccc;align-self:stretch}.arrow{color:#999;transition:transform .2s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;padding:4px}.arrow svg{fill:currentColor}.arrow.open{transform:rotate(180deg)}.spinner{width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #2684FF;border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.dropdown{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fffffffa;border:1px solid #E5E7EB;border-radius:12px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d;z-index:1000;overflow:hidden;backdrop-filter:blur(12px) saturate(180%);-webkit-backdrop-filter:blur(12px) saturate(180%)}.dropdown.fixed{position:fixed}.dropdown.top{top:auto;bottom:calc(100% + 4px)}.search-container{padding:8px;border-bottom:1px solid #f0f0f0;background:#fff}.search-input{width:100%;padding:6px 8px;border:1px solid #cccccc;border-radius:3px;font-size:inherit;outline:none;transition:border-color .15s}.search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.options-list{max-height:inherit;overflow-y:auto;overflow-x:hidden;padding:4px 0}.options-list::-webkit-scrollbar{width:8px}.options-list::-webkit-scrollbar-track{background:transparent}.options-list::-webkit-scrollbar-thumb{background:#d9d9d9;border-radius:4px}.options-list::-webkit-scrollbar-thumb:hover{background:#b3b3b3}.select-all-container{padding:8px;border-bottom:1px solid #E5E7EB;background:#f9fafb}.select-all-button{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;background:#fff;border:1px solid #D1D5DB;border-radius:6px;cursor:pointer;transition:all .15s;font-size:inherit;color:inherit;font-family:inherit}.select-all-button:hover{background:#f3f4f6;border-color:#9ca3af}.select-all-button input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.select-all-text{flex:1;text-align:left;font-weight:500}.select-all-count{color:#6b7280;font-size:.9em}.option-group{margin:4px 0}.option-group-label{padding:8px 12px 4px;font-size:.75em;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:1}.option-group .option{padding-left:20px}.option{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;transition:all .15s cubic-bezier(.4,0,.2,1);position:relative;min-height:40px}.option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.option-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6}.option-icon img{width:100%;height:100%;object-fit:cover}.option-badge{padding:2px 8px;border-radius:12px;font-size:.75em;font-weight:500;color:#374151;background:#e5e7eb;flex-shrink:0;white-space:nowrap}.option:hover:not(.disabled):not(.create-option){background:#deebff}.option.highlighted{background:#deebff}.option.selected:not(.create-option){background:#e6f2ff;font-weight:500}.option.disabled{opacity:.5;cursor:not-allowed;background:transparent!important}.option.hidden{display:none}.option.create-option{color:#2684ff;font-weight:500;background:#f0f6ff}.option.create-option:hover{background:#e6f2ff}.option input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.option-label{font-weight:500;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.option-description{font-size:.875em;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.check-icon{color:#2684ff;display:flex;align-items:center;margin-left:auto}.check-icon svg{fill:currentColor}.no-options,.loading-message{padding:16px 12px;text-align:center;color:#999;font-size:.95em}.info-message{padding:12px;margin:8px;border-radius:6px;font-size:.9em;display:flex;align-items:center;gap:8px}.info-message.max-selection{background:#fff4e5;color:#f57c00;border:1px solid #FFE0B2}.info-message.min-search{background:#e3f2fd;color:#1976d2;border:1px solid #BBDEFB}.info-message svg{flex-shrink:0}.rtl .select-actions,.rtl .tags,.rtl .option{flex-direction:row-reverse}.theme-blue .select-trigger:focus,.theme-blue .select-trigger.focused,.theme-blue .select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-blue .option:hover:not(.disabled):not(.create-option),.theme-blue .option.highlighted{background:#deebff}.theme-blue .option.selected:not(.create-option){background:#e6f2ff}.theme-blue .tag{background:#e6f2ff;color:#0052cc;border-color:#cce0ff}.theme-blue .check-icon,.theme-blue .option.create-option{color:#2684ff}.theme-blue .spinner{border-top-color:#2684ff}.theme-blue .search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-purple .select-trigger:focus,.theme-purple .select-trigger.focused,.theme-purple .select-trigger.open{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-purple .option:hover:not(.disabled):not(.create-option),.theme-purple .option.highlighted{background:#f3e8ff}.theme-purple .option.selected:not(.create-option){background:#faf5ff}.theme-purple .tag{background:#faf5ff;color:#7e22ce;border-color:#e9d5ff}.theme-purple .check-icon,.theme-purple .option.create-option{color:#9333ea}.theme-purple .spinner{border-top-color:#9333ea}.theme-purple .search-input:focus{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-green .select-trigger:focus,.theme-green .select-trigger.focused,.theme-green .select-trigger.open{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-green .option:hover:not(.disabled):not(.create-option),.theme-green .option.highlighted{background:#d1fae5}.theme-green .option.selected:not(.create-option){background:#ecfdf5}.theme-green .tag{background:#ecfdf5;color:#059669;border-color:#a7f3d0}.theme-green .check-icon,.theme-green .option.create-option{color:#10b981}.theme-green .spinner{border-top-color:#10b981}.theme-green .search-input:focus{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-red .select-trigger:focus,.theme-red .select-trigger.focused,.theme-red .select-trigger.open{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-red .option:hover:not(.disabled):not(.create-option),.theme-red .option.highlighted{background:#fee2e2}.theme-red .option.selected:not(.create-option){background:#fef2f2}.theme-red .tag{background:#fef2f2;color:#dc2626;border-color:#fecaca}.theme-red .check-icon,.theme-red .option.create-option{color:#ef4444}.theme-red .spinner{border-top-color:#ef4444}.theme-red .search-input:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-orange .select-trigger:focus,.theme-orange .select-trigger.focused,.theme-orange .select-trigger.open{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-orange .option:hover:not(.disabled):not(.create-option),.theme-orange .option.highlighted{background:#ffedd5}.theme-orange .option.selected:not(.create-option){background:#fff7ed}.theme-orange .tag{background:#fff7ed;color:#ea580c;border-color:#fed7aa}.theme-orange .check-icon,.theme-orange .option.create-option{color:#f97316}.theme-orange .spinner{border-top-color:#f97316}.theme-orange .search-input:focus{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-pink .select-trigger:focus,.theme-pink .select-trigger.focused,.theme-pink .select-trigger.open{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-pink .option:hover:not(.disabled):not(.create-option),.theme-pink .option.highlighted{background:#fce7f3}.theme-pink .option.selected:not(.create-option){background:#fdf2f8}.theme-pink .tag{background:#fdf2f8;color:#db2777;border-color:#fbcfe8}.theme-pink .check-icon,.theme-pink .option.create-option{color:#ec4899}.theme-pink .spinner{border-top-color:#ec4899}.theme-pink .search-input:focus{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-dark .select-trigger:focus,.theme-dark .select-trigger.focused,.theme-dark .select-trigger.open{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.theme-dark .option:hover:not(.disabled):not(.create-option),.theme-dark .option.highlighted{background:#e5e7eb}.theme-dark .option.selected:not(.create-option){background:#f3f4f6}.theme-dark .tag{background:#f3f4f6;color:#111827;border-color:#d1d5db}.theme-dark .check-icon,.theme-dark .option.create-option{color:#1f2937}.theme-dark .spinner{border-top-color:#1f2937}.theme-dark .search-input:focus{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.select-container.has-validation .select-trigger{transition:all .2s cubic-bezier(.4,0,.2,1)}.select-container.validation-error .select-trigger{border-color:#ef4444!important}.select-container.validation-error .select-trigger:focus,.select-container.validation-error .select-trigger.focused,.select-container.validation-error .select-trigger.open{border-color:#ef4444!important;box-shadow:0 0 0 3px #ef44441a,0 1px 2px #0000000d}.select-container.validation-warning .select-trigger{border-color:#f59e0b!important}.select-container.validation-warning .select-trigger:focus,.select-container.validation-warning .select-trigger.focused,.select-container.validation-warning .select-trigger.open{border-color:#f59e0b!important;box-shadow:0 0 0 3px #f59e0b1a,0 1px 2px #0000000d}.select-container.validation-success .select-trigger{border-color:#10b981!important}.select-container.validation-success .select-trigger:focus,.select-container.validation-success .select-trigger.focused,.select-container.validation-success .select-trigger.open{border-color:#10b981!important;box-shadow:0 0 0 3px #10b9811a,0 1px 2px #0000000d}.select-container.validation-info .select-trigger{border-color:#3b82f6!important}.select-container.validation-info .select-trigger:focus,.select-container.validation-info .select-trigger.focused,.select-container.validation-info .select-trigger.open{border-color:#3b82f6!important;box-shadow:0 0 0 3px #3b82f61a,0 1px 2px #0000000d}.validation-message{display:flex;align-items:center;gap:6px;margin-top:6px;font-size:13px;line-height:1.4;padding:4px 8px;border-radius:6px;transition:all .2s ease}.validation-message .validation-icon{display:flex;align-items:center;flex-shrink:0}.validation-message .validation-icon svg{display:block}.validation-message .validation-text{flex:1}.validation-message.validation-error{color:#dc2626;background:#fef2f2;border-left:3px solid #EF4444}.validation-message.validation-warning{color:#d97706;background:#fffbeb;border-left:3px solid #F59E0B}.validation-message.validation-success{color:#059669;background:#f0fdf4;border-left:3px solid #10B981}.validation-message.validation-info{color:#2563eb;background:#eff6ff;border-left:3px solid #3B82F6}.option.recent-option{position:relative;background:#fafbfc}.option.recent-option:after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(to bottom,#2684ff,#0052cc);border-radius:0 3px 3px 0}.option.recent-option:hover:not(.disabled){background:#f4f5f7!important}.option.recent-option.selected{background:#e6f2ff!important}.option.recent-option.selected:hover:not(.disabled){background:#d4e8ff!important}.section-header{padding:8px 12px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:#6b7280;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:2}.virtual-scroll-viewport{width:100%}.virtual-scroll-viewport ::ng-deep .cdk-virtual-scroll-content-wrapper{width:100%}.virtual-scroll-viewport ::ng-deep .cdk-virtual-scroll-spacer{display:none}.virtual-scroll-viewport .option{width:100%;box-sizing:border-box}@keyframes copyFlash{0%,to{background:transparent}50%{background:#2684ff1a}}.select-container.copying{animation:copyFlash .5s ease}.options-list.scrolling{position:relative}.options-list.scrolling:after{content:\"\";position:absolute;bottom:0;left:50%;transform:translate(-50%);width:20px;height:20px;border:2px solid #E5E7EB;border-top-color:#2684ff;border-radius:50%;animation:spin .8s linear infinite}.option[title]{cursor:help}.option.highlighted{position:relative}.option.highlighted:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:2px;background:#2684ff}@keyframes typeAheadPulse{0%,to{opacity:1}50%{opacity:.7}}.options-list.type-ahead-active .option.highlighted{animation:typeAheadPulse 1s ease-in-out}.select-container:focus-visible{outline:2px solid #2684FF;outline-offset:2px}@media (prefers-contrast: high){.select-trigger{border-width:2px}.option.highlighted{outline:2px solid currentColor;outline-offset:-2px}.validation-message{border-width:2px}}@media (prefers-reduced-motion: reduce){.select-trigger,.option,.tag,.dropdown,.validation-message{transition:none!important;animation:none!important}}\n"] }]
697
1001
  }], ctorParameters: () => [{ type: i1.DomSanitizer }], propDecorators: { options: [{
698
1002
  type: Input
699
1003
  }], placeholder: [{
@@ -794,6 +1098,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
794
1098
  type: Input
795
1099
  }], minSearchMessage: [{
796
1100
  type: Input
1101
+ }], enableVirtualScroll: [{
1102
+ type: Input
1103
+ }], virtualScrollItemSize: [{
1104
+ type: Input
1105
+ }], virtualScrollMinBufferPx: [{
1106
+ type: Input
1107
+ }], virtualScrollMaxBufferPx: [{
1108
+ type: Input
1109
+ }], validationState: [{
1110
+ type: Input
1111
+ }], validationMessage: [{
1112
+ type: Input
1113
+ }], showValidationIcon: [{
1114
+ type: Input
1115
+ }], showTooltips: [{
1116
+ type: Input
1117
+ }], tooltipDelay: [{
1118
+ type: Input
1119
+ }], getOptionTooltip: [{
1120
+ type: Input
1121
+ }], showRecentSelections: [{
1122
+ type: Input
1123
+ }], recentSelectionsLimit: [{
1124
+ type: Input
1125
+ }], recentSelectionsLabel: [{
1126
+ type: Input
1127
+ }], enableRecentSelectionsPersistence: [{
1128
+ type: Input
1129
+ }], enableInfiniteScroll: [{
1130
+ type: Input
1131
+ }], infiniteScrollThreshold: [{
1132
+ type: Input
1133
+ }], totalOptionsCount: [{
1134
+ type: Input
1135
+ }], enableAdvancedKeyboard: [{
1136
+ type: Input
1137
+ }], typeAheadDelay: [{
1138
+ type: Input
1139
+ }], enableCopyPaste: [{
1140
+ type: Input
1141
+ }], copyDelimiter: [{
1142
+ type: Input
1143
+ }], pasteDelimiter: [{
1144
+ type: Input
797
1145
  }], name: [{
798
1146
  type: Input
799
1147
  }], id: [{
@@ -834,6 +1182,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
834
1182
  type: Output
835
1183
  }], loadError: [{
836
1184
  type: Output
1185
+ }], copy: [{
1186
+ type: Output
1187
+ }], paste: [{
1188
+ type: Output
1189
+ }], scrollEnd: [{
1190
+ type: Output
837
1191
  }], selectContainerRef: [{
838
1192
  type: ViewChild,
839
1193
  args: ['selectContainer']
@@ -843,9 +1197,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
843
1197
  }], menuElementRef: [{
844
1198
  type: ViewChild,
845
1199
  args: ['menuRef']
1200
+ }], virtualScrollViewport: [{
1201
+ type: ViewChild,
1202
+ args: [CdkVirtualScrollViewport]
1203
+ }], optionTemplate: [{
1204
+ type: ContentChild,
1205
+ args: ['optionTemplate', { read: TemplateRef }]
1206
+ }], selectedOptionTemplate: [{
1207
+ type: ContentChild,
1208
+ args: ['selectedOptionTemplate', { read: TemplateRef }]
846
1209
  }], handleKeydown: [{
847
1210
  type: HostListener,
848
1211
  args: ['keydown', ['$event']]
1212
+ }], handlePaste: [{
1213
+ type: HostListener,
1214
+ args: ['paste', ['$event']]
849
1215
  }] } });
850
1216
 
851
1217
  /*