angular-perfect-select 2.0.0 → 2.2.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,11 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, HostListener, Output, Directive, signal, computed, TemplateRef, forwardRef, ContentChild, ViewChild, Input, Component } from '@angular/core';
2
+ import { EventEmitter, HostListener, Output, Directive, signal, computed, SecurityContext, TemplateRef, forwardRef, ContentChild, ViewChild, Input, Component } from '@angular/core';
3
3
  import * as i2 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i3 from '@angular/forms';
6
6
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
7
  import * as i4 from '@angular/cdk/scrolling';
8
8
  import { ScrollingModule, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
9
+ import * as i5 from '@angular/cdk/drag-drop';
10
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
9
11
  import { trigger, transition, style, animate, query, stagger } from '@angular/animations';
10
12
  import * as i1 from '@angular/platform-browser';
11
13
 
@@ -208,6 +210,25 @@ class PerfectSelectComponent {
208
210
  enableCopyPaste = true;
209
211
  copyDelimiter = ', ';
210
212
  pasteDelimiter = ',';
213
+ // v2.1.0 Features - Drag & Drop Reordering
214
+ enableDragDrop = false;
215
+ dragDropPlaceholder = 'Drop here';
216
+ dragDropAnimation = 200;
217
+ // v2.1.0 Features - Option Pinning
218
+ enablePinning = false;
219
+ maxPinnedOptions = null;
220
+ pinnedOptionsLabel = 'Pinned';
221
+ persistPinnedOptions = false;
222
+ // v2.2.0 Features - Search Highlighting
223
+ enableSearchHighlight = true;
224
+ searchHighlightColor = '#ffeb3b';
225
+ searchHighlightTextColor = '#000';
226
+ // v2.2.0 Features - Tag Overflow Management
227
+ maxVisibleTags = null;
228
+ showMoreTagsText = '+{count} more';
229
+ collapsibleTags = false;
230
+ showAllTagsText = 'Show all';
231
+ showLessTagsText = 'Show less';
211
232
  // Behavior
212
233
  name = 'angular-perfect-select';
213
234
  id = 'angular-perfect-select';
@@ -234,6 +255,9 @@ class PerfectSelectComponent {
234
255
  copy = new EventEmitter();
235
256
  paste = new EventEmitter();
236
257
  scrollEnd = new EventEmitter();
258
+ // v2.1.0 Events
259
+ reorder = new EventEmitter();
260
+ pin = new EventEmitter();
237
261
  // ViewChildren
238
262
  selectContainerRef;
239
263
  searchInputRef;
@@ -261,24 +285,38 @@ class PerfectSelectComponent {
261
285
  tooltipTimeout = null;
262
286
  recentSelectionsStorageKey = '';
263
287
  scrollEndTimeout = null;
288
+ // v2.1.0 Signals
289
+ pinnedOptions = signal([], ...(ngDevMode ? [{ debugName: "pinnedOptions" }] : []));
290
+ isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : []));
291
+ // v2.1.0 Private state
292
+ pinnedOptionsStorageKey = '';
293
+ // v2.2.0 Signals
294
+ tagsExpanded = signal(false, ...(ngDevMode ? [{ debugName: "tagsExpanded" }] : []));
264
295
  // Computed signals
265
296
  currentTheme = computed(() => THEMES[this.theme] || THEMES.blue, ...(ngDevMode ? [{ debugName: "currentTheme" }] : []));
266
297
  filteredOptions = computed(() => {
267
298
  const term = this.searchTerm();
268
299
  const opts = this.internalOptions();
300
+ const pinned = this.pinnedOptions();
269
301
  // Check min search length
270
302
  if (this.minSearchLength > 0 && term.length < this.minSearchLength) {
271
303
  return [];
272
304
  }
273
- if (!term)
274
- return opts;
275
- return opts.filter(option => {
305
+ let filtered = !term ? opts : opts.filter(option => {
276
306
  if (this.filterOption) {
277
307
  return this.filterOption(option, term);
278
308
  }
279
309
  const label = this.getOptionLabel(option);
280
310
  return label.toLowerCase().includes(term.toLowerCase());
281
311
  });
312
+ // v2.1.0: Sort pinned options to the top
313
+ if (this.enablePinning && pinned.length > 0) {
314
+ const pinnedIds = new Set(pinned.map(p => p.id));
315
+ const pinnedFiltered = filtered.filter(opt => pinnedIds.has(opt.id));
316
+ const unpinnedFiltered = filtered.filter(opt => !pinnedIds.has(opt.id));
317
+ filtered = [...pinnedFiltered, ...unpinnedFiltered];
318
+ }
319
+ return filtered;
282
320
  }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
283
321
  selectedOptions = computed(() => {
284
322
  const value = this.internalValue();
@@ -307,6 +345,20 @@ class PerfectSelectComponent {
307
345
  }
308
346
  return this.getOptionLabel(selected[0]);
309
347
  }, ...(ngDevMode ? [{ debugName: "displayText" }] : []));
348
+ // v2.2.0: Visible tags for overflow management
349
+ visibleTags = computed(() => {
350
+ const selected = this.selectedOptions();
351
+ const expanded = this.tagsExpanded();
352
+ if (!this.maxVisibleTags || expanded || selected.length <= this.maxVisibleTags) {
353
+ return selected;
354
+ }
355
+ return selected.slice(0, this.maxVisibleTags);
356
+ }, ...(ngDevMode ? [{ debugName: "visibleTags" }] : []));
357
+ hiddenTagsCount = computed(() => {
358
+ const selected = this.selectedOptions();
359
+ const visible = this.visibleTags();
360
+ return selected.length - visible.length;
361
+ }, ...(ngDevMode ? [{ debugName: "hiddenTagsCount" }] : []));
310
362
  groupedOptions = computed(() => {
311
363
  if (!this.isGrouped || !this.groupBy) {
312
364
  return null;
@@ -445,6 +497,11 @@ class PerfectSelectComponent {
445
497
  this.recentSelectionsStorageKey = `ps-recent-${this.name || this.id}`;
446
498
  this.loadRecentSelections();
447
499
  }
500
+ // v2.1.0: Initialize pinned options
501
+ if (this.enablePinning) {
502
+ this.pinnedOptionsStorageKey = `ps-pinned-${this.name || this.id}`;
503
+ this.loadPinnedOptions();
504
+ }
448
505
  // Auto-focus if needed
449
506
  if (this.autoFocus) {
450
507
  setTimeout(() => {
@@ -941,6 +998,109 @@ class PerfectSelectComponent {
941
998
  console.warn('Failed to save recent selections:', error);
942
999
  }
943
1000
  }
1001
+ // v2.1.0: Drag & Drop handlers
1002
+ onTagsReorder(event) {
1003
+ if (!this.enableDragDrop || !this.isMulti)
1004
+ return;
1005
+ const values = Array.isArray(this.internalValue()) ? [...this.internalValue()] : [];
1006
+ const options = this.selectedOptions();
1007
+ moveItemInArray(values, event.previousIndex, event.currentIndex);
1008
+ this.internalValue.set(values);
1009
+ this.onChange(values);
1010
+ // Emit reorder event
1011
+ this.reorder.emit({
1012
+ previousIndex: event.previousIndex,
1013
+ currentIndex: event.currentIndex,
1014
+ values: values,
1015
+ options: options
1016
+ });
1017
+ }
1018
+ onDragStart() {
1019
+ this.isDragging.set(true);
1020
+ }
1021
+ onDragEnd() {
1022
+ setTimeout(() => {
1023
+ this.isDragging.set(false);
1024
+ }, this.dragDropAnimation);
1025
+ }
1026
+ // v2.1.0: Pinning handlers
1027
+ togglePin(option, event) {
1028
+ event.stopPropagation();
1029
+ if (!this.enablePinning)
1030
+ return;
1031
+ const pinned = this.pinnedOptions();
1032
+ const isPinned = pinned.some(p => p.id === option.id);
1033
+ if (isPinned) {
1034
+ // Unpin
1035
+ const updated = pinned.filter(p => p.id !== option.id);
1036
+ this.pinnedOptions.set(updated);
1037
+ this.pin.emit({ option, pinned: false });
1038
+ }
1039
+ else {
1040
+ // Pin
1041
+ if (this.maxPinnedOptions && pinned.length >= this.maxPinnedOptions) {
1042
+ return; // Max limit reached
1043
+ }
1044
+ const updated = [...pinned, option];
1045
+ this.pinnedOptions.set(updated);
1046
+ this.pin.emit({ option, pinned: true });
1047
+ }
1048
+ // Persist if enabled
1049
+ if (this.persistPinnedOptions) {
1050
+ this.savePinnedOptions();
1051
+ }
1052
+ }
1053
+ isPinned(option) {
1054
+ const pinned = this.pinnedOptions();
1055
+ return pinned.some(p => p.id === option.id);
1056
+ }
1057
+ loadPinnedOptions() {
1058
+ if (!this.persistPinnedOptions)
1059
+ return;
1060
+ try {
1061
+ const stored = localStorage.getItem(this.pinnedOptionsStorageKey);
1062
+ if (stored) {
1063
+ const parsed = JSON.parse(stored);
1064
+ this.pinnedOptions.set(parsed);
1065
+ }
1066
+ }
1067
+ catch (error) {
1068
+ console.warn('Failed to load pinned options:', error);
1069
+ }
1070
+ }
1071
+ savePinnedOptions() {
1072
+ if (!this.persistPinnedOptions)
1073
+ return;
1074
+ try {
1075
+ const pinned = this.pinnedOptions();
1076
+ localStorage.setItem(this.pinnedOptionsStorageKey, JSON.stringify(pinned));
1077
+ }
1078
+ catch (error) {
1079
+ console.warn('Failed to save pinned options:', error);
1080
+ }
1081
+ }
1082
+ // v2.2.0: Search highlighting
1083
+ highlightSearchTerm(text) {
1084
+ const term = this.searchTerm();
1085
+ if (!this.enableSearchHighlight || !term || !text) {
1086
+ return text;
1087
+ }
1088
+ // Escape special regex characters in search term
1089
+ const escapedTerm = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1090
+ const regex = new RegExp(`(${escapedTerm})`, 'gi');
1091
+ const highlighted = text.replace(regex, (match) => {
1092
+ return `<mark style="background-color: ${this.searchHighlightColor}; color: ${this.searchHighlightTextColor}; padding: 0 2px; border-radius: 2px;">${match}</mark>`;
1093
+ });
1094
+ return this.sanitizer.sanitize(SecurityContext.HTML, highlighted) || text;
1095
+ }
1096
+ // v2.2.0: Tag overflow management
1097
+ toggleTagsExpanded() {
1098
+ this.tagsExpanded.set(!this.tagsExpanded());
1099
+ }
1100
+ getMoreTagsText() {
1101
+ const count = this.hiddenTagsCount();
1102
+ return this.showMoreTagsText.replace('{count}', count.toString());
1103
+ }
944
1104
  // Infinite scroll handler
945
1105
  onOptionsScroll(event) {
946
1106
  if (!this.enableInfiniteScroll)
@@ -985,19 +1145,19 @@ class PerfectSelectComponent {
985
1145
  return `validation-${this.validationState}`;
986
1146
  }
987
1147
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PerfectSelectComponent, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
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: [{
1148
+ 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", enableDragDrop: "enableDragDrop", dragDropPlaceholder: "dragDropPlaceholder", dragDropAnimation: "dragDropAnimation", enablePinning: "enablePinning", maxPinnedOptions: "maxPinnedOptions", pinnedOptionsLabel: "pinnedOptionsLabel", persistPinnedOptions: "persistPinnedOptions", enableSearchHighlight: "enableSearchHighlight", searchHighlightColor: "searchHighlightColor", searchHighlightTextColor: "searchHighlightTextColor", maxVisibleTags: "maxVisibleTags", showMoreTagsText: "showMoreTagsText", collapsibleTags: "collapsibleTags", showAllTagsText: "showAllTagsText", showLessTagsText: "showLessTagsText", 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", reorder: "reorder", pin: "pin" }, host: { listeners: { "keydown": "handleKeydown($event)", "paste": "handlePaste($event)" } }, providers: [{
989
1149
  provide: NG_VALUE_ACCESSOR,
990
1150
  useExisting: forwardRef(() => PerfectSelectComponent),
991
1151
  multi: true
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 });
1152
+ }], 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 <!-- v2.1.0: Drag-drop enabled tags -->\n @if (enableDragDrop) {\n <div class=\"tags\" cdkDropList cdkDropListOrientation=\"horizontal\" (cdkDropListDropped)=\"onTagsReorder($event)\" [class.dragging]=\"isDragging()\">\n @for (option of visibleTags(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" cdkDrag (cdkDragStarted)=\"onDragStart()\" (cdkDragEnded)=\"onDragEnd()\" [@tag]>\n <div class=\"drag-handle\" cdkDragHandle>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M9 3h2v2H9V3zm0 4h2v2H9V7zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V3zm0 4h2v2h-2V7zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z\"/>\n </svg>\n </div>\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 <div *cdkDragPlaceholder class=\"drag-placeholder\">{{dragDropPlaceholder}}</div>\n </span>\n }\n </div>\n } @else {\n <div class=\"tags\">\n @for (option of visibleTags(); 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 }\n\n <!-- v2.2.0: Tag overflow indicator -->\n @if (hiddenTagsCount() > 0) {\n @if (collapsibleTags) {\n <button\n class=\"tags-toggle\"\n (click)=\"toggleTagsExpanded()\"\n [attr.aria-label]=\"tagsExpanded() ? showLessTagsText : showAllTagsText\"\n type=\"button\"\n >\n {{ tagsExpanded() ? showLessTagsText : showAllTagsText }}\n </button>\n } @else {\n <span class=\"tags-overflow-indicator\">\n {{ getMoreTagsText() }}\n </span>\n }\n }\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\" [innerHTML]=\"highlightSearchTerm(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 <!-- v2.1.0: Pin button -->\n @if (enablePinning) {\n <button\n class=\"pin-button\"\n [class.pinned]=\"isPinned(option)\"\n (click)=\"togglePin(option, $event)\"\n [attr.aria-label]=\"isPinned(option) ? 'Unpin option' : 'Pin option'\"\n type=\"button\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n @if (isPinned(option)) {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z\"/>\n } @else {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z\"/>\n }\n </svg>\n </button>\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\" [innerHTML]=\"highlightSearchTerm(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 <!-- v2.1.0: Pin button -->\n @if (enablePinning) {\n <button\n class=\"pin-button\"\n [class.pinned]=\"isPinned(option)\"\n (click)=\"togglePin(option, $event)\"\n [attr.aria-label]=\"isPinned(option) ? 'Unpin option' : 'Pin option'\"\n type=\"button\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n @if (isPinned(option)) {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z\"/>\n } @else {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z\"/>\n }\n </svg>\n </button>\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\" [innerHTML]=\"highlightSearchTerm(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 <!-- v2.1.0: Pin button -->\n @if (enablePinning) {\n <button\n class=\"pin-button\"\n [class.pinned]=\"isPinned(option)\"\n (click)=\"togglePin(option, $event)\"\n [attr.aria-label]=\"isPinned(option) ? 'Unpin option' : 'Pin option'\"\n type=\"button\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n @if (isPinned(option)) {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z\"/>\n } @else {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z\"/>\n }\n </svg>\n </button>\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}.tags.dragging .tag{cursor:move}.drag-handle{display:flex;align-items:center;justify-content:center;cursor:move;padding:0;margin-left:-4px;opacity:.5;transition:opacity .15s}.drag-handle svg{fill:currentColor}.drag-handle:hover{opacity:.8}.cdk-drag-preview{box-shadow:0 5px 15px #0003;opacity:.8}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.drag-placeholder{background:#0052cc1a;border:2px dashed var(--ps-primary, #0052cc);border-radius:6px;padding:4px 8px;min-height:28px;display:flex;align-items:center;justify-content:center;color:var(--ps-primary, #0052cc);font-size:.75em;opacity:.6}.cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}.tags-toggle{background:none;border:none;color:var(--ps-primary, #0052cc);cursor:pointer;font-size:.875em;font-weight:500;padding:4px 8px;border-radius:4px;transition:all .15s;white-space:nowrap}.tags-toggle:hover{background:#0052cc1a;text-decoration:underline}.tags-toggle:focus{outline:2px solid var(--ps-primary, #0052cc);outline-offset:2px}.tags-overflow-indicator{display:inline-flex;align-items:center;padding:4px 8px;background:#f3f4f6;color:#6b7280;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #d1d5db}.option-label mark{background-color:#ffeb3b;color:#000;padding:0 2px;border-radius:2px;font-weight:600}.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}.pin-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;margin-left:auto;opacity:.5}.pin-button svg{fill:currentColor}.pin-button:hover{opacity:1;background:#0000000d}.pin-button.pinned{color:var(--ps-primary, #0052cc);opacity:1}.option.pinned-option{background:#0052cc0d;border-left:3px solid var(--ps-primary, #0052cc);padding-left:9px}.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: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i5.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "pipe", type: i2.KeyValuePipe, name: "keyvalue" }], animations: selectAnimations });
993
1153
  }
994
1154
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PerfectSelectComponent, decorators: [{
995
1155
  type: Component,
996
- args: [{ selector: 'ng-perfect-select', standalone: true, imports: [CommonModule, FormsModule, ClickOutsideDirective, ScrollingModule], animations: selectAnimations, providers: [{
1156
+ args: [{ selector: 'ng-perfect-select', standalone: true, imports: [CommonModule, FormsModule, ClickOutsideDirective, ScrollingModule, DragDropModule], animations: selectAnimations, providers: [{
997
1157
  provide: NG_VALUE_ACCESSOR,
998
1158
  useExisting: forwardRef(() => PerfectSelectComponent),
999
1159
  multi: true
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"] }]
1160
+ }], 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 <!-- v2.1.0: Drag-drop enabled tags -->\n @if (enableDragDrop) {\n <div class=\"tags\" cdkDropList cdkDropListOrientation=\"horizontal\" (cdkDropListDropped)=\"onTagsReorder($event)\" [class.dragging]=\"isDragging()\">\n @for (option of visibleTags(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" cdkDrag (cdkDragStarted)=\"onDragStart()\" (cdkDragEnded)=\"onDragEnd()\" [@tag]>\n <div class=\"drag-handle\" cdkDragHandle>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M9 3h2v2H9V3zm0 4h2v2H9V7zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V3zm0 4h2v2h-2V7zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z\"/>\n </svg>\n </div>\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 <div *cdkDragPlaceholder class=\"drag-placeholder\">{{dragDropPlaceholder}}</div>\n </span>\n }\n </div>\n } @else {\n <div class=\"tags\">\n @for (option of visibleTags(); 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 }\n\n <!-- v2.2.0: Tag overflow indicator -->\n @if (hiddenTagsCount() > 0) {\n @if (collapsibleTags) {\n <button\n class=\"tags-toggle\"\n (click)=\"toggleTagsExpanded()\"\n [attr.aria-label]=\"tagsExpanded() ? showLessTagsText : showAllTagsText\"\n type=\"button\"\n >\n {{ tagsExpanded() ? showLessTagsText : showAllTagsText }}\n </button>\n } @else {\n <span class=\"tags-overflow-indicator\">\n {{ getMoreTagsText() }}\n </span>\n }\n }\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\" [innerHTML]=\"highlightSearchTerm(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 <!-- v2.1.0: Pin button -->\n @if (enablePinning) {\n <button\n class=\"pin-button\"\n [class.pinned]=\"isPinned(option)\"\n (click)=\"togglePin(option, $event)\"\n [attr.aria-label]=\"isPinned(option) ? 'Unpin option' : 'Pin option'\"\n type=\"button\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n @if (isPinned(option)) {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z\"/>\n } @else {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z\"/>\n }\n </svg>\n </button>\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\" [innerHTML]=\"highlightSearchTerm(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 <!-- v2.1.0: Pin button -->\n @if (enablePinning) {\n <button\n class=\"pin-button\"\n [class.pinned]=\"isPinned(option)\"\n (click)=\"togglePin(option, $event)\"\n [attr.aria-label]=\"isPinned(option) ? 'Unpin option' : 'Pin option'\"\n type=\"button\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n @if (isPinned(option)) {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z\"/>\n } @else {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z\"/>\n }\n </svg>\n </button>\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\" [innerHTML]=\"highlightSearchTerm(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 <!-- v2.1.0: Pin button -->\n @if (enablePinning) {\n <button\n class=\"pin-button\"\n [class.pinned]=\"isPinned(option)\"\n (click)=\"togglePin(option, $event)\"\n [attr.aria-label]=\"isPinned(option) ? 'Unpin option' : 'Pin option'\"\n type=\"button\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n @if (isPinned(option)) {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z\"/>\n } @else {\n <path d=\"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z\"/>\n }\n </svg>\n </button>\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}.tags.dragging .tag{cursor:move}.drag-handle{display:flex;align-items:center;justify-content:center;cursor:move;padding:0;margin-left:-4px;opacity:.5;transition:opacity .15s}.drag-handle svg{fill:currentColor}.drag-handle:hover{opacity:.8}.cdk-drag-preview{box-shadow:0 5px 15px #0003;opacity:.8}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.drag-placeholder{background:#0052cc1a;border:2px dashed var(--ps-primary, #0052cc);border-radius:6px;padding:4px 8px;min-height:28px;display:flex;align-items:center;justify-content:center;color:var(--ps-primary, #0052cc);font-size:.75em;opacity:.6}.cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}.tags-toggle{background:none;border:none;color:var(--ps-primary, #0052cc);cursor:pointer;font-size:.875em;font-weight:500;padding:4px 8px;border-radius:4px;transition:all .15s;white-space:nowrap}.tags-toggle:hover{background:#0052cc1a;text-decoration:underline}.tags-toggle:focus{outline:2px solid var(--ps-primary, #0052cc);outline-offset:2px}.tags-overflow-indicator{display:inline-flex;align-items:center;padding:4px 8px;background:#f3f4f6;color:#6b7280;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #d1d5db}.option-label mark{background-color:#ffeb3b;color:#000;padding:0 2px;border-radius:2px;font-weight:600}.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}.pin-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;margin-left:auto;opacity:.5}.pin-button svg{fill:currentColor}.pin-button:hover{opacity:1;background:#0000000d}.pin-button.pinned{color:var(--ps-primary, #0052cc);opacity:1}.option.pinned-option{background:#0052cc0d;border-left:3px solid var(--ps-primary, #0052cc);padding-left:9px}.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"] }]
1001
1161
  }], ctorParameters: () => [{ type: i1.DomSanitizer }], propDecorators: { options: [{
1002
1162
  type: Input
1003
1163
  }], placeholder: [{
@@ -1142,6 +1302,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1142
1302
  type: Input
1143
1303
  }], pasteDelimiter: [{
1144
1304
  type: Input
1305
+ }], enableDragDrop: [{
1306
+ type: Input
1307
+ }], dragDropPlaceholder: [{
1308
+ type: Input
1309
+ }], dragDropAnimation: [{
1310
+ type: Input
1311
+ }], enablePinning: [{
1312
+ type: Input
1313
+ }], maxPinnedOptions: [{
1314
+ type: Input
1315
+ }], pinnedOptionsLabel: [{
1316
+ type: Input
1317
+ }], persistPinnedOptions: [{
1318
+ type: Input
1319
+ }], enableSearchHighlight: [{
1320
+ type: Input
1321
+ }], searchHighlightColor: [{
1322
+ type: Input
1323
+ }], searchHighlightTextColor: [{
1324
+ type: Input
1325
+ }], maxVisibleTags: [{
1326
+ type: Input
1327
+ }], showMoreTagsText: [{
1328
+ type: Input
1329
+ }], collapsibleTags: [{
1330
+ type: Input
1331
+ }], showAllTagsText: [{
1332
+ type: Input
1333
+ }], showLessTagsText: [{
1334
+ type: Input
1145
1335
  }], name: [{
1146
1336
  type: Input
1147
1337
  }], id: [{
@@ -1188,6 +1378,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1188
1378
  type: Output
1189
1379
  }], scrollEnd: [{
1190
1380
  type: Output
1381
+ }], reorder: [{
1382
+ type: Output
1383
+ }], pin: [{
1384
+ type: Output
1191
1385
  }], selectContainerRef: [{
1192
1386
  type: ViewChild,
1193
1387
  args: ['selectContainer']