angular-perfect-select 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,807 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, HostListener, Output, Directive, inject, DestroyRef, signal, computed, effect, forwardRef, ViewChild, Input, Component } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i1 from '@angular/forms';
6
+ import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import { DomSanitizer } from '@angular/platform-browser';
8
+ import { trigger, transition, style, animate, query, stagger } from '@angular/animations';
9
+
10
+ const dropdownAnimation = trigger('dropdown', [
11
+ transition(':enter', [
12
+ style({ opacity: 0, transform: 'translateY(-10px)' }),
13
+ animate('200ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 1, transform: 'translateY(0)' }))
14
+ ]),
15
+ transition(':leave', [
16
+ animate('150ms ease-out', style({ opacity: 0 }))
17
+ ])
18
+ ]);
19
+ const tagAnimation = trigger('tag', [
20
+ transition(':enter', [
21
+ style({ opacity: 0, transform: 'scale(0.8)' }),
22
+ animate('200ms cubic-bezier(0.34, 1.56, 0.64, 1)', style({ opacity: 1, transform: 'scale(1)' }))
23
+ ]),
24
+ transition(':leave', [
25
+ animate('150ms ease-out', style({ opacity: 0, transform: 'scale(0.8)' }))
26
+ ])
27
+ ]);
28
+ const optionListAnimation = trigger('optionList', [
29
+ transition(':enter', [
30
+ query('.option', [
31
+ style({ opacity: 0, transform: 'translateY(-5px)' }),
32
+ stagger(15, [
33
+ animate('150ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
34
+ ])
35
+ ], { optional: true })
36
+ ])
37
+ ]);
38
+ const selectAnimations = [dropdownAnimation, tagAnimation, optionListAnimation];
39
+
40
+ class ClickOutsideDirective {
41
+ elementRef;
42
+ clickOutside = new EventEmitter();
43
+ constructor(elementRef) {
44
+ this.elementRef = elementRef;
45
+ }
46
+ onClick(event) {
47
+ const clickedInside = this.elementRef.nativeElement.contains(event.target);
48
+ if (!clickedInside) {
49
+ this.clickOutside.emit();
50
+ }
51
+ }
52
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ClickOutsideDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
53
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: ClickOutsideDirective, isStandalone: true, selector: "[clickOutside]", outputs: { clickOutside: "clickOutside" }, host: { listeners: { "document:click": "onClick($event)" } }, ngImport: i0 });
54
+ }
55
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ClickOutsideDirective, decorators: [{
56
+ type: Directive,
57
+ args: [{
58
+ selector: '[clickOutside]',
59
+ standalone: true
60
+ }]
61
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { clickOutside: [{
62
+ type: Output
63
+ }], onClick: [{
64
+ type: HostListener,
65
+ args: ['document:click', ['$event']]
66
+ }] } });
67
+
68
+ const THEMES = {
69
+ blue: {
70
+ primary: '#2684FF',
71
+ secondary: '#DEEBFF',
72
+ tag: '#E6F2FF',
73
+ tagText: '#0052CC',
74
+ tagBorder: '#CCE0FF'
75
+ },
76
+ purple: {
77
+ primary: '#9333EA',
78
+ secondary: '#F3E8FF',
79
+ tag: '#FAF5FF',
80
+ tagText: '#7E22CE',
81
+ tagBorder: '#E9D5FF'
82
+ },
83
+ green: {
84
+ primary: '#10B981',
85
+ secondary: '#D1FAE5',
86
+ tag: '#ECFDF5',
87
+ tagText: '#059669',
88
+ tagBorder: '#A7F3D0'
89
+ },
90
+ red: {
91
+ primary: '#EF4444',
92
+ secondary: '#FEE2E2',
93
+ tag: '#FEF2F2',
94
+ tagText: '#DC2626',
95
+ tagBorder: '#FECACA'
96
+ },
97
+ orange: {
98
+ primary: '#F97316',
99
+ secondary: '#FFEDD5',
100
+ tag: '#FFF7ED',
101
+ tagText: '#EA580C',
102
+ tagBorder: '#FED7AA'
103
+ },
104
+ pink: {
105
+ primary: '#EC4899',
106
+ secondary: '#FCE7F3',
107
+ tag: '#FDF2F8',
108
+ tagText: '#DB2777',
109
+ tagBorder: '#FBCFE8'
110
+ },
111
+ dark: {
112
+ primary: '#1F2937',
113
+ secondary: '#E5E7EB',
114
+ tag: '#F3F4F6',
115
+ tagText: '#111827',
116
+ tagBorder: '#D1D5DB'
117
+ }
118
+ };
119
+
120
+ class PerfectSelectComponent {
121
+ destroyRef = inject(DestroyRef);
122
+ sanitizer = inject(DomSanitizer);
123
+ // Core Props
124
+ options = [];
125
+ placeholder = 'Select...';
126
+ // React-Select Compatible Props (dual naming)
127
+ isMulti = false;
128
+ set multiple(value) { this.isMulti = value; }
129
+ isSearchable = true;
130
+ set searchable(value) { this.isSearchable = value; }
131
+ isClearable = true;
132
+ set clearable(value) { this.isClearable = value; }
133
+ isDisabled = false;
134
+ set disabled(value) { this.isDisabled = value; }
135
+ isLoading = false;
136
+ set loading(value) { this.isLoading = value; }
137
+ isRtl = false;
138
+ closeMenuOnSelect = true;
139
+ hideSelectedOptions = false;
140
+ // Creatable Mode
141
+ isCreatable = false;
142
+ allowCreateWhileLoading = false;
143
+ createOptionPosition = 'last';
144
+ formatCreateLabel = (inputValue) => `Create "${inputValue}"`;
145
+ // Async Loading
146
+ loadOptions = null;
147
+ cacheOptions = true;
148
+ defaultOptions = false;
149
+ // Styling & Theming
150
+ selectSize = 'medium';
151
+ containerSize = 'md';
152
+ theme = 'blue';
153
+ borderRadius = '8px';
154
+ customStyles = {};
155
+ maxHeight = '300px';
156
+ menuPlacement = 'auto';
157
+ menuPosition = 'absolute';
158
+ // Option Customization
159
+ getOptionLabel = (option) => option.label || String(option.value);
160
+ getOptionValue = (option) => option.id || option.value;
161
+ isOptionDisabled = (option) => option.disabled || false;
162
+ filterOption = null;
163
+ // Grouping
164
+ isGrouped = false;
165
+ groupBy = null;
166
+ // Advanced Features
167
+ showSelectAll = false;
168
+ selectAllText = 'Select All';
169
+ deselectAllText = 'Deselect All';
170
+ showOptionIcons = false;
171
+ showOptionBadges = false;
172
+ maxOptionsDisplay = 1000;
173
+ optionHeight = 40;
174
+ emptyStateText = 'No options available';
175
+ emptySearchText = 'No results found';
176
+ // Behavior
177
+ name = 'angular-perfect-select';
178
+ id = 'angular-perfect-select';
179
+ autoFocus = false;
180
+ openMenuOnFocus = false;
181
+ openMenuOnClick = true;
182
+ tabSelectsValue = true;
183
+ backspaceRemovesValue = true;
184
+ escapeClearsValue = false;
185
+ noOptionsMessage = () => 'No options';
186
+ loadingMessage = () => 'Loading...';
187
+ // Events
188
+ change = new EventEmitter();
189
+ clear = new EventEmitter();
190
+ focus = new EventEmitter();
191
+ blur = new EventEmitter();
192
+ menuOpen = new EventEmitter();
193
+ menuClose = new EventEmitter();
194
+ inputChange = new EventEmitter();
195
+ createOption = new EventEmitter();
196
+ optionsLoaded = new EventEmitter();
197
+ loadError = new EventEmitter();
198
+ // ViewChildren
199
+ selectContainerRef;
200
+ searchInputRef;
201
+ menuElementRef;
202
+ // Signals for reactive state
203
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
204
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
205
+ highlightedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : []));
206
+ internalValue = signal(this.isMulti ? [] : null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
207
+ internalOptions = signal([], ...(ngDevMode ? [{ debugName: "internalOptions" }] : []));
208
+ isLoadingAsync = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingAsync" }] : []));
209
+ optionsCache = new Map();
210
+ // Computed signals
211
+ currentTheme = computed(() => THEMES[this.theme] || THEMES.blue, ...(ngDevMode ? [{ debugName: "currentTheme" }] : []));
212
+ filteredOptions = computed(() => {
213
+ const term = this.searchTerm();
214
+ const opts = this.internalOptions();
215
+ if (!term)
216
+ return opts;
217
+ return opts.filter(option => {
218
+ if (this.filterOption) {
219
+ return this.filterOption(option, term);
220
+ }
221
+ const label = this.getOptionLabel(option);
222
+ return label.toLowerCase().includes(term.toLowerCase());
223
+ });
224
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
225
+ selectedOptions = computed(() => {
226
+ const value = this.internalValue();
227
+ if (!value)
228
+ return [];
229
+ const allOptions = this.internalOptions();
230
+ if (this.isMulti) {
231
+ const values = Array.isArray(value) ? value : [];
232
+ // Map values to option objects
233
+ return values.map(v => {
234
+ const found = allOptions.find(opt => this.getOptionValue(opt) === v);
235
+ return found || { id: v, label: String(v), value: v };
236
+ });
237
+ }
238
+ // Single select - find the option by value
239
+ const found = allOptions.find(opt => this.getOptionValue(opt) === value);
240
+ return found ? [found] : [];
241
+ }, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : []));
242
+ displayText = computed(() => {
243
+ const selected = this.selectedOptions();
244
+ if (selected.length === 0) {
245
+ return this.placeholder;
246
+ }
247
+ if (this.isMulti) {
248
+ return `${selected.length} selected`;
249
+ }
250
+ return this.getOptionLabel(selected[0]);
251
+ }, ...(ngDevMode ? [{ debugName: "displayText" }] : []));
252
+ groupedOptions = computed(() => {
253
+ if (!this.isGrouped || !this.groupBy) {
254
+ return null;
255
+ }
256
+ const opts = this.filteredOptions();
257
+ const groups = {};
258
+ opts.forEach(option => {
259
+ const group = this.groupBy(option);
260
+ if (!groups[group]) {
261
+ groups[group] = [];
262
+ }
263
+ groups[group].push(option);
264
+ });
265
+ return groups;
266
+ }, ...(ngDevMode ? [{ debugName: "groupedOptions" }] : []));
267
+ displayOptions = computed(() => {
268
+ let opts = this.filteredOptions();
269
+ if (this.isCreatable && this.searchTerm() && !this.isLoadingAsync()) {
270
+ const term = this.searchTerm();
271
+ const exactMatch = opts.some(opt => this.getOptionLabel(opt).toLowerCase() === term.toLowerCase());
272
+ if (!exactMatch) {
273
+ const createOption = {
274
+ id: '__create__',
275
+ label: this.formatCreateLabel(term),
276
+ value: term,
277
+ __isCreate__: true
278
+ };
279
+ if (this.createOptionPosition === 'first') {
280
+ opts = [createOption, ...opts];
281
+ }
282
+ else {
283
+ opts = [...opts, createOption];
284
+ }
285
+ }
286
+ }
287
+ return opts;
288
+ }, ...(ngDevMode ? [{ debugName: "displayOptions" }] : []));
289
+ allOptionsSelected = computed(() => {
290
+ if (!this.isMulti)
291
+ return false;
292
+ const opts = this.filteredOptions().filter(opt => !this.isOptionDisabled(opt));
293
+ const selected = this.selectedOptions();
294
+ return opts.length > 0 && opts.every(opt => selected.some(s => this.getOptionValue(s) === this.getOptionValue(opt)));
295
+ }, ...(ngDevMode ? [{ debugName: "allOptionsSelected" }] : []));
296
+ someOptionsSelected = computed(() => {
297
+ if (!this.isMulti)
298
+ return false;
299
+ const opts = this.filteredOptions().filter(opt => !this.isOptionDisabled(opt));
300
+ const selected = this.selectedOptions();
301
+ const selectedCount = opts.filter(opt => selected.some(s => this.getOptionValue(s) === this.getOptionValue(opt))).length;
302
+ return selectedCount > 0 && selectedCount < opts.length;
303
+ }, ...(ngDevMode ? [{ debugName: "someOptionsSelected" }] : []));
304
+ // Helper method for template
305
+ getEnabledOptionsCount() {
306
+ return this.filteredOptions().filter(opt => !this.isOptionDisabled(opt)).length;
307
+ }
308
+ // ControlValueAccessor
309
+ onChange = () => { };
310
+ onTouched = () => { };
311
+ constructor() {
312
+ // Update closeMenuOnSelect based on isMulti
313
+ effect(() => {
314
+ if (this.isMulti && this.closeMenuOnSelect === true) {
315
+ this.closeMenuOnSelect = false;
316
+ }
317
+ });
318
+ }
319
+ ngOnChanges(changes) {
320
+ // Update internal options when the options input changes
321
+ if (changes['options'] && this.options.length > 0 && !this.loadOptions) {
322
+ this.internalOptions.set([...this.options]);
323
+ }
324
+ }
325
+ writeValue(value) {
326
+ this.internalValue.set(value);
327
+ }
328
+ registerOnChange(fn) {
329
+ this.onChange = fn;
330
+ }
331
+ registerOnTouched(fn) {
332
+ this.onTouched = fn;
333
+ }
334
+ setDisabledState(disabled) {
335
+ this.isDisabled = disabled;
336
+ }
337
+ ngOnInit() {
338
+ // Initialize options
339
+ if (this.options.length > 0) {
340
+ this.internalOptions.set([...this.options]);
341
+ }
342
+ // Load default options if async
343
+ if (this.loadOptions && this.defaultOptions) {
344
+ this.handleLoadOptions('');
345
+ }
346
+ // Auto-focus if needed
347
+ if (this.autoFocus) {
348
+ setTimeout(() => {
349
+ if (this.searchInputRef) {
350
+ this.searchInputRef.nativeElement.focus();
351
+ }
352
+ });
353
+ }
354
+ }
355
+ ngOnDestroy() {
356
+ // Cleanup handled by DestroyRef
357
+ }
358
+ // Keyboard Navigation
359
+ handleKeydown(event) {
360
+ if (this.isDisabled)
361
+ return;
362
+ switch (event.key) {
363
+ case 'ArrowDown':
364
+ event.preventDefault();
365
+ if (!this.isOpen()) {
366
+ this.toggleDropdown();
367
+ }
368
+ else {
369
+ this.moveHighlight(1);
370
+ }
371
+ break;
372
+ case 'ArrowUp':
373
+ event.preventDefault();
374
+ this.moveHighlight(-1);
375
+ break;
376
+ case 'Enter':
377
+ event.preventDefault();
378
+ if (this.isOpen()) {
379
+ const index = this.highlightedIndex();
380
+ const opts = this.displayOptions();
381
+ if (index >= 0 && index < opts.length) {
382
+ this.selectOption(opts[index]);
383
+ }
384
+ }
385
+ else {
386
+ this.toggleDropdown();
387
+ }
388
+ break;
389
+ case 'Escape':
390
+ event.preventDefault();
391
+ if (this.isOpen()) {
392
+ this.closeDropdown();
393
+ }
394
+ if (this.escapeClearsValue) {
395
+ this.clearSelection(event);
396
+ }
397
+ break;
398
+ case 'Tab':
399
+ if (this.isOpen() && this.tabSelectsValue) {
400
+ const index = this.highlightedIndex();
401
+ const opts = this.displayOptions();
402
+ if (index >= 0 && index < opts.length) {
403
+ event.preventDefault();
404
+ this.selectOption(opts[index]);
405
+ }
406
+ }
407
+ if (this.isOpen()) {
408
+ this.closeDropdown();
409
+ }
410
+ break;
411
+ case 'Backspace':
412
+ if (this.backspaceRemovesValue && this.isMulti && !this.searchTerm()) {
413
+ const selected = this.selectedOptions();
414
+ if (selected.length > 0) {
415
+ event.preventDefault();
416
+ this.removeOption(selected[selected.length - 1], event);
417
+ }
418
+ }
419
+ break;
420
+ }
421
+ }
422
+ // Toggle dropdown
423
+ toggleDropdown() {
424
+ if (this.isDisabled)
425
+ return;
426
+ if (this.isOpen()) {
427
+ this.closeDropdown();
428
+ }
429
+ else {
430
+ this.openDropdown();
431
+ }
432
+ }
433
+ openDropdown() {
434
+ this.isOpen.set(true);
435
+ this.menuOpen.emit();
436
+ // Focus search input
437
+ setTimeout(() => {
438
+ if (this.isSearchable && this.searchInputRef) {
439
+ this.searchInputRef.nativeElement.focus();
440
+ }
441
+ });
442
+ }
443
+ closeDropdown() {
444
+ this.isOpen.set(false);
445
+ this.searchTerm.set('');
446
+ this.highlightedIndex.set(-1);
447
+ this.menuClose.emit();
448
+ this.onTouched();
449
+ }
450
+ // Select option
451
+ selectOption(option) {
452
+ if (this.isOptionDisabled(option))
453
+ return;
454
+ // Handle create option
455
+ if (option.__isCreate__) {
456
+ const newOption = {
457
+ id: Date.now().toString(),
458
+ label: this.searchTerm(),
459
+ value: this.searchTerm(),
460
+ __isNew__: true
461
+ };
462
+ this.internalOptions.update(opts => [...opts, newOption]);
463
+ this.createOption.emit({ option: newOption });
464
+ option = newOption;
465
+ }
466
+ const optionValue = this.getOptionValue(option);
467
+ if (this.isMulti) {
468
+ const currentValue = Array.isArray(this.internalValue()) ? [...this.internalValue()] : [];
469
+ const exists = currentValue.includes(optionValue);
470
+ let newValue;
471
+ if (exists) {
472
+ newValue = currentValue.filter((v) => v !== optionValue);
473
+ }
474
+ else {
475
+ newValue = [...currentValue, optionValue];
476
+ }
477
+ this.internalValue.set(newValue);
478
+ this.onChange(newValue);
479
+ this.change.emit({
480
+ value: newValue,
481
+ option,
482
+ action: exists ? 'remove-value' : 'select-option'
483
+ });
484
+ }
485
+ else {
486
+ this.internalValue.set(optionValue);
487
+ this.onChange(optionValue);
488
+ this.change.emit({
489
+ value: optionValue,
490
+ option,
491
+ action: 'select-option'
492
+ });
493
+ }
494
+ if (this.closeMenuOnSelect) {
495
+ this.closeDropdown();
496
+ }
497
+ this.searchTerm.set('');
498
+ }
499
+ // Remove option (multi-select)
500
+ removeOption(option, event) {
501
+ event.stopPropagation();
502
+ const currentValue = Array.isArray(this.internalValue()) ? [...this.internalValue()] : [];
503
+ const optionValue = this.getOptionValue(option);
504
+ const newValue = currentValue.filter((v) => v !== optionValue);
505
+ this.internalValue.set(newValue);
506
+ this.onChange(newValue);
507
+ this.change.emit({
508
+ value: newValue,
509
+ option,
510
+ action: 'remove-value'
511
+ });
512
+ }
513
+ // Clear selection
514
+ clearSelection(event) {
515
+ event.stopPropagation();
516
+ const newValue = this.isMulti ? [] : null;
517
+ this.internalValue.set(newValue);
518
+ this.onChange(newValue);
519
+ this.change.emit({
520
+ value: newValue,
521
+ action: 'clear'
522
+ });
523
+ this.clear.emit();
524
+ }
525
+ // Select All / Deselect All
526
+ selectAll() {
527
+ const opts = this.filteredOptions().filter(opt => !this.isOptionDisabled(opt));
528
+ const values = opts.map(opt => this.getOptionValue(opt));
529
+ this.internalValue.set(values);
530
+ this.onChange(values);
531
+ this.change.emit({
532
+ value: values,
533
+ option: opts,
534
+ action: 'select-all'
535
+ });
536
+ }
537
+ deselectAll() {
538
+ this.internalValue.set([]);
539
+ this.onChange([]);
540
+ this.change.emit({
541
+ value: [],
542
+ action: 'deselect-all'
543
+ });
544
+ }
545
+ // Search input change
546
+ onSearchChange(term) {
547
+ this.searchTerm.set(term);
548
+ this.highlightedIndex.set(0);
549
+ this.inputChange.emit({
550
+ value: term,
551
+ action: 'input-change'
552
+ });
553
+ // Trigger async loading if configured
554
+ if (this.loadOptions) {
555
+ this.handleLoadOptions(term);
556
+ }
557
+ }
558
+ // Async loading
559
+ async handleLoadOptions(inputValue) {
560
+ if (!this.loadOptions)
561
+ return;
562
+ // Check cache
563
+ if (this.cacheOptions && this.optionsCache.has(inputValue)) {
564
+ this.internalOptions.set(this.optionsCache.get(inputValue));
565
+ return;
566
+ }
567
+ this.isLoadingAsync.set(true);
568
+ try {
569
+ const options = await this.loadOptions(inputValue);
570
+ this.internalOptions.set(options);
571
+ if (this.cacheOptions) {
572
+ this.optionsCache.set(inputValue, options);
573
+ }
574
+ this.optionsLoaded.emit({ options });
575
+ }
576
+ catch (error) {
577
+ this.loadError.emit({ error: error });
578
+ }
579
+ finally {
580
+ this.isLoadingAsync.set(false);
581
+ }
582
+ }
583
+ // Move highlight index
584
+ moveHighlight(direction) {
585
+ const opts = this.displayOptions();
586
+ if (opts.length === 0)
587
+ return;
588
+ let newIndex = this.highlightedIndex() + direction;
589
+ if (newIndex < 0)
590
+ newIndex = 0;
591
+ if (newIndex >= opts.length)
592
+ newIndex = opts.length - 1;
593
+ this.highlightedIndex.set(newIndex);
594
+ this.scrollHighlightedIntoView();
595
+ }
596
+ // Scroll highlighted option into view
597
+ scrollHighlightedIntoView() {
598
+ setTimeout(() => {
599
+ if (!this.menuElementRef)
600
+ return;
601
+ const menu = this.menuElementRef.nativeElement;
602
+ const highlighted = menu.querySelector('.option.highlighted');
603
+ if (highlighted) {
604
+ const menuRect = menu.getBoundingClientRect();
605
+ const optionRect = highlighted.getBoundingClientRect();
606
+ if (optionRect.bottom > menuRect.bottom) {
607
+ menu.scrollTop += optionRect.bottom - menuRect.bottom;
608
+ }
609
+ else if (optionRect.top < menuRect.top) {
610
+ menu.scrollTop -= menuRect.top - optionRect.top;
611
+ }
612
+ }
613
+ });
614
+ }
615
+ // Click outside handler
616
+ onClickOutside() {
617
+ if (this.isOpen()) {
618
+ this.closeDropdown();
619
+ this.blur.emit();
620
+ }
621
+ }
622
+ // Check if option is selected
623
+ isSelected(option) {
624
+ const selected = this.selectedOptions();
625
+ const optionValue = this.getOptionValue(option);
626
+ return selected.some(s => this.getOptionValue(s) === optionValue);
627
+ }
628
+ // Sanitize HTML for icons
629
+ sanitizeHtml(html) {
630
+ return this.sanitizer.bypassSecurityTrustHtml(html);
631
+ }
632
+ // Track by function for ngFor
633
+ trackByValue(index, option) {
634
+ return this.getOptionValue(option);
635
+ }
636
+ trackByGroup(index, item) {
637
+ return item[0];
638
+ }
639
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PerfectSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
640
+ 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", name: "name", id: "id", autoFocus: "autoFocus", openMenuOnFocus: "openMenuOnFocus", openMenuOnClick: "openMenuOnClick", tabSelectsValue: "tabSelectsValue", backspaceRemovesValue: "backspaceRemovesValue", escapeClearsValue: "escapeClearsValue", noOptionsMessage: "noOptionsMessage", loadingMessage: "loadingMessage" }, outputs: { change: "change", clear: "clear", focus: "focus", blur: "blur", menuOpen: "menuOpen", menuClose: "menuClose", inputChange: "inputChange", createOption: "createOption", optionsLoaded: "optionsLoaded", loadError: "loadError" }, host: { listeners: { "keydown": "handleKeydown($event)" } }, providers: [{
641
+ provide: NG_VALUE_ACCESSOR,
642
+ useExisting: forwardRef(() => PerfectSelectComponent),
643
+ multi: true
644
+ }], viewQueries: [{ propertyName: "selectContainerRef", first: true, predicate: ["selectContainer"], descendants: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "menuElementRef", first: true, predicate: ["menuRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #selectContainer\n class=\"select-container {{selectSize}} {{containerSize}} theme-{{theme}}\"\n [class.disabled]=\"isDisabled\"\n [class.rtl]=\"isRtl\"\n (clickOutside)=\"onClickOutside()\"\n role=\"combobox\"\n tabindex=\"0\"\n [attr.aria-controls]=\"'options-list'\"\n [attr.aria-expanded]=\"isOpen()\"\n (focus)=\"openMenuOnFocus && !isOpen() && toggleDropdown(); focus.emit()\"\n [style]=\"customStyles.container || ''\"\n>\n <!-- Main Select Trigger -->\n <div\n class=\"select-trigger\"\n [class.open]=\"isOpen()\"\n [class.focused]=\"isOpen()\"\n (click)=\"openMenuOnClick && toggleDropdown()\"\n [attr.tabindex]=\"isDisabled ? -1 : 0\"\n role=\"button\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-label]=\"placeholder\"\n >\n <!-- Selected Value Display -->\n <div class=\"select-value\">\n <!-- Multi-select Tags -->\n @if (isMulti && selectedOptions().length > 0) {\n <div class=\"tags\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" [@tag]>\n <span class=\"tag-label\">{{getOptionLabel(option)}}</span>\n @if (!isDisabled) {\n <button\n class=\"tag-remove\"\n (click)=\"removeOption(option, $event)\"\n [attr.aria-label]=\"'Remove ' + getOptionLabel(option)\"\n type=\"button\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n </span>\n }\n </div>\n } @else {\n <!-- Single Select or Placeholder -->\n <span class=\"placeholder\" [class.has-value]=\"selectedOptions().length > 0\">\n {{displayText()}}\n </span>\n }\n </div>\n\n <!-- Actions (Clear, Loading, Arrow) -->\n <div class=\"select-actions\">\n @if (isLoading || isLoadingAsync()) {\n <div class=\"spinner\"></div>\n }\n @if (isClearable && selectedOptions().length > 0 && !isDisabled && !isLoading && !isLoadingAsync()) {\n <button\n class=\"clear-button\"\n (click)=\"clearSelection($event)\"\n aria-label=\"Clear selection\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n <span class=\"separator\"></span>\n <span class=\"arrow\" [class.open]=\"isOpen()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\"></path>\n </svg>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n @if (isOpen()) {\n <div\n #menuRef\n class=\"dropdown {{menuPlacement}}\"\n [class.fixed]=\"menuPosition === 'fixed'\"\n [style.max-height]=\"maxHeight\"\n role=\"listbox\"\n [attr.aria-multiselectable]=\"isMulti\"\n [@dropdown]\n >\n <!-- Search Input -->\n @if (isSearchable) {\n <div class=\"search-container\">\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search...\"\n [value]=\"searchTerm()\"\n (input)=\"onSearchChange($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n aria-label=\"Search options\"\n aria-autocomplete=\"list\"\n />\n </div>\n }\n\n <!-- Options List -->\n <div class=\"options-list\" id=\"options-list\">\n @if (isLoadingAsync()) {\n <div class=\"loading-message\">{{loadingMessage()}}</div>\n } @else if (displayOptions().length === 0) {\n <div class=\"no-options\">\n {{searchTerm() ? emptySearchText : emptyStateText}}\n </div>\n } @else {\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)\"\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)\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n </div>\n }\n } @else {\n <!-- Regular (Ungrouped) Options -->\n @for (option of displayOptions(); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option)\"\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)\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n</div>\n\n<!-- Hidden native select for form compatibility -->\n@if (isMulti) {\n <select [name]=\"name\" [id]=\"id\" multiple style=\"display: none;\" [attr.aria-hidden]=\"true\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <option [value]=\"getOptionValue(option)\" selected>{{getOptionLabel(option)}}</option>\n }\n </select>\n} @else {\n @if (selectedOptions().length > 0) {\n <select [name]=\"name\" [id]=\"id\" style=\"display: none;\" [attr.aria-hidden]=\"true\">\n <option [value]=\"getOptionValue(selectedOptions()[0])\" selected>{{getOptionLabel(selectedOptions()[0])}}</option>\n </select>\n }\n}\n", styles: [":host{display:block;width:100%}.select-container{position:relative;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.smaller{font-size:11px}.small{font-size:13px}.medium{font-size:14px}.large{font-size:16px}.larger{font-size:18px}.xs .select-trigger{min-height:28px;padding:4px 8px}.sm .select-trigger{min-height:32px;padding:6px 10px}.md .select-trigger{min-height:40px;padding:8px 12px}.lg .select-trigger{min-height:48px;padding:10px 14px}.xl .select-trigger{min-height:56px;padding:12px 16px}.select-container.disabled{opacity:.6;cursor:not-allowed}.select-container.rtl{direction:rtl}.select-trigger{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1.5px solid #D1D5DB;border-radius:10px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);min-height:38px;gap:8px;box-shadow:0 1px 2px #0000000d}.select-trigger:hover:not(.disabled){border-color:#9ca3af;box-shadow:0 2px 4px #00000014}.select-trigger:focus,.select-trigger.focused{outline:none;border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-value{flex:1;display:flex;align-items:center;min-width:0;gap:4px}.placeholder{color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.placeholder.has-value{color:#333}.tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}.tag{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#e6f2ff;color:#0052cc;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #CCE0FF;box-shadow:0 1px 2px #0000000d}.tag.disabled{background:#f0f0f0;color:#666;border-color:#d9d9d9}.tag-label{line-height:1.2}.tag-remove{background:none;border:none;color:inherit;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;border-radius:2px;transition:all .15s}.tag-remove:hover{background:#0052cc26}.tag-remove svg{fill:currentColor}.select-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}.clear-button{background:none;border:none;color:#999;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s}.clear-button:hover{color:#333;background:#f0f0f0}.clear-button svg{fill:currentColor}.separator{width:1px;height:24px;background:#ccc;align-self:stretch}.arrow{color:#999;transition:transform .2s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;padding:4px}.arrow svg{fill:currentColor}.arrow.open{transform:rotate(180deg)}.spinner{width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #2684FF;border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.dropdown{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fffffffa;border:1px solid #E5E7EB;border-radius:12px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d;z-index:1000;overflow:hidden;backdrop-filter:blur(12px) saturate(180%);-webkit-backdrop-filter:blur(12px) saturate(180%)}.dropdown.fixed{position:fixed}.dropdown.top{top:auto;bottom:calc(100% + 4px)}.search-container{padding:8px;border-bottom:1px solid #f0f0f0;background:#fff}.search-input{width:100%;padding:6px 8px;border:1px solid #cccccc;border-radius:3px;font-size:inherit;outline:none;transition:border-color .15s}.search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.options-list{max-height:inherit;overflow-y:auto;overflow-x:hidden;padding:4px 0}.options-list::-webkit-scrollbar{width:8px}.options-list::-webkit-scrollbar-track{background:transparent}.options-list::-webkit-scrollbar-thumb{background:#d9d9d9;border-radius:4px}.options-list::-webkit-scrollbar-thumb:hover{background:#b3b3b3}.select-all-container{padding:8px;border-bottom:1px solid #E5E7EB;background:#f9fafb}.select-all-button{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;background:#fff;border:1px solid #D1D5DB;border-radius:6px;cursor:pointer;transition:all .15s;font-size:inherit;color:inherit;font-family:inherit}.select-all-button:hover{background:#f3f4f6;border-color:#9ca3af}.select-all-button input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.select-all-text{flex:1;text-align:left;font-weight:500}.select-all-count{color:#6b7280;font-size:.9em}.option-group{margin:4px 0}.option-group-label{padding:8px 12px 4px;font-size:.75em;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:1}.option-group .option{padding-left:20px}.option{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;transition:all .15s cubic-bezier(.4,0,.2,1);position:relative;min-height:40px}.option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.option-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6}.option-icon img{width:100%;height:100%;object-fit:cover}.option-badge{padding:2px 8px;border-radius:12px;font-size:.75em;font-weight:500;color:#374151;background:#e5e7eb;flex-shrink:0;white-space:nowrap}.option:hover:not(.disabled):not(.create-option){background:#deebff}.option.highlighted{background:#deebff}.option.selected:not(.create-option){background:#e6f2ff;font-weight:500}.option.disabled{opacity:.5;cursor:not-allowed;background:transparent!important}.option.hidden{display:none}.option.create-option{color:#2684ff;font-weight:500;background:#f0f6ff}.option.create-option:hover{background:#e6f2ff}.option input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.option-label{font-weight:500;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.option-description{font-size:.875em;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.check-icon{color:#2684ff;display:flex;align-items:center;margin-left:auto}.check-icon svg{fill:currentColor}.no-options,.loading-message{padding:16px 12px;text-align:center;color:#999;font-size:.95em}.rtl .select-actions,.rtl .tags,.rtl .option{flex-direction:row-reverse}.theme-blue .select-trigger:focus,.theme-blue .select-trigger.focused,.theme-blue .select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-blue .option:hover:not(.disabled):not(.create-option),.theme-blue .option.highlighted{background:#deebff}.theme-blue .option.selected:not(.create-option){background:#e6f2ff}.theme-blue .tag{background:#e6f2ff;color:#0052cc;border-color:#cce0ff}.theme-blue .check-icon,.theme-blue .option.create-option{color:#2684ff}.theme-blue .spinner{border-top-color:#2684ff}.theme-blue .search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-purple .select-trigger:focus,.theme-purple .select-trigger.focused,.theme-purple .select-trigger.open{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-purple .option:hover:not(.disabled):not(.create-option),.theme-purple .option.highlighted{background:#f3e8ff}.theme-purple .option.selected:not(.create-option){background:#faf5ff}.theme-purple .tag{background:#faf5ff;color:#7e22ce;border-color:#e9d5ff}.theme-purple .check-icon,.theme-purple .option.create-option{color:#9333ea}.theme-purple .spinner{border-top-color:#9333ea}.theme-purple .search-input:focus{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-green .select-trigger:focus,.theme-green .select-trigger.focused,.theme-green .select-trigger.open{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-green .option:hover:not(.disabled):not(.create-option),.theme-green .option.highlighted{background:#d1fae5}.theme-green .option.selected:not(.create-option){background:#ecfdf5}.theme-green .tag{background:#ecfdf5;color:#059669;border-color:#a7f3d0}.theme-green .check-icon,.theme-green .option.create-option{color:#10b981}.theme-green .spinner{border-top-color:#10b981}.theme-green .search-input:focus{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-red .select-trigger:focus,.theme-red .select-trigger.focused,.theme-red .select-trigger.open{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-red .option:hover:not(.disabled):not(.create-option),.theme-red .option.highlighted{background:#fee2e2}.theme-red .option.selected:not(.create-option){background:#fef2f2}.theme-red .tag{background:#fef2f2;color:#dc2626;border-color:#fecaca}.theme-red .check-icon,.theme-red .option.create-option{color:#ef4444}.theme-red .spinner{border-top-color:#ef4444}.theme-red .search-input:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-orange .select-trigger:focus,.theme-orange .select-trigger.focused,.theme-orange .select-trigger.open{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-orange .option:hover:not(.disabled):not(.create-option),.theme-orange .option.highlighted{background:#ffedd5}.theme-orange .option.selected:not(.create-option){background:#fff7ed}.theme-orange .tag{background:#fff7ed;color:#ea580c;border-color:#fed7aa}.theme-orange .check-icon,.theme-orange .option.create-option{color:#f97316}.theme-orange .spinner{border-top-color:#f97316}.theme-orange .search-input:focus{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-pink .select-trigger:focus,.theme-pink .select-trigger.focused,.theme-pink .select-trigger.open{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-pink .option:hover:not(.disabled):not(.create-option),.theme-pink .option.highlighted{background:#fce7f3}.theme-pink .option.selected:not(.create-option){background:#fdf2f8}.theme-pink .tag{background:#fdf2f8;color:#db2777;border-color:#fbcfe8}.theme-pink .check-icon,.theme-pink .option.create-option{color:#ec4899}.theme-pink .spinner{border-top-color:#ec4899}.theme-pink .search-input:focus{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-dark .select-trigger:focus,.theme-dark .select-trigger.focused,.theme-dark .select-trigger.open{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.theme-dark .option:hover:not(.disabled):not(.create-option),.theme-dark .option.highlighted{background:#e5e7eb}.theme-dark .option.selected:not(.create-option){background:#f3f4f6}.theme-dark .tag{background:#f3f4f6;color:#111827;border-color:#d1d5db}.theme-dark .check-icon,.theme-dark .option.create-option{color:#1f2937}.theme-dark .spinner{border-top-color:#1f2937}.theme-dark .search-input:focus{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", outputs: ["clickOutside"] }, { kind: "pipe", type: i2.KeyValuePipe, name: "keyvalue" }], animations: selectAnimations });
645
+ }
646
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PerfectSelectComponent, decorators: [{
647
+ type: Component,
648
+ args: [{ selector: 'ng-perfect-select', standalone: true, imports: [CommonModule, FormsModule, ClickOutsideDirective], animations: selectAnimations, providers: [{
649
+ provide: NG_VALUE_ACCESSOR,
650
+ useExisting: forwardRef(() => PerfectSelectComponent),
651
+ multi: true
652
+ }], template: "<div\n #selectContainer\n class=\"select-container {{selectSize}} {{containerSize}} theme-{{theme}}\"\n [class.disabled]=\"isDisabled\"\n [class.rtl]=\"isRtl\"\n (clickOutside)=\"onClickOutside()\"\n role=\"combobox\"\n tabindex=\"0\"\n [attr.aria-controls]=\"'options-list'\"\n [attr.aria-expanded]=\"isOpen()\"\n (focus)=\"openMenuOnFocus && !isOpen() && toggleDropdown(); focus.emit()\"\n [style]=\"customStyles.container || ''\"\n>\n <!-- Main Select Trigger -->\n <div\n class=\"select-trigger\"\n [class.open]=\"isOpen()\"\n [class.focused]=\"isOpen()\"\n (click)=\"openMenuOnClick && toggleDropdown()\"\n [attr.tabindex]=\"isDisabled ? -1 : 0\"\n role=\"button\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-label]=\"placeholder\"\n >\n <!-- Selected Value Display -->\n <div class=\"select-value\">\n <!-- Multi-select Tags -->\n @if (isMulti && selectedOptions().length > 0) {\n <div class=\"tags\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <span class=\"tag\" [class.disabled]=\"isDisabled\" [@tag]>\n <span class=\"tag-label\">{{getOptionLabel(option)}}</span>\n @if (!isDisabled) {\n <button\n class=\"tag-remove\"\n (click)=\"removeOption(option, $event)\"\n [attr.aria-label]=\"'Remove ' + getOptionLabel(option)\"\n type=\"button\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n </span>\n }\n </div>\n } @else {\n <!-- Single Select or Placeholder -->\n <span class=\"placeholder\" [class.has-value]=\"selectedOptions().length > 0\">\n {{displayText()}}\n </span>\n }\n </div>\n\n <!-- Actions (Clear, Loading, Arrow) -->\n <div class=\"select-actions\">\n @if (isLoading || isLoadingAsync()) {\n <div class=\"spinner\"></div>\n }\n @if (isClearable && selectedOptions().length > 0 && !isDisabled && !isLoading && !isLoadingAsync()) {\n <button\n class=\"clear-button\"\n (click)=\"clearSelection($event)\"\n aria-label=\"Clear selection\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z\"></path>\n </svg>\n </button>\n }\n <span class=\"separator\"></span>\n <span class=\"arrow\" [class.open]=\"isOpen()\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\">\n <path d=\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\"></path>\n </svg>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n @if (isOpen()) {\n <div\n #menuRef\n class=\"dropdown {{menuPlacement}}\"\n [class.fixed]=\"menuPosition === 'fixed'\"\n [style.max-height]=\"maxHeight\"\n role=\"listbox\"\n [attr.aria-multiselectable]=\"isMulti\"\n [@dropdown]\n >\n <!-- Search Input -->\n @if (isSearchable) {\n <div class=\"search-container\">\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search...\"\n [value]=\"searchTerm()\"\n (input)=\"onSearchChange($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n aria-label=\"Search options\"\n aria-autocomplete=\"list\"\n />\n </div>\n }\n\n <!-- Options List -->\n <div class=\"options-list\" id=\"options-list\">\n @if (isLoadingAsync()) {\n <div class=\"loading-message\">{{loadingMessage()}}</div>\n } @else if (displayOptions().length === 0) {\n <div class=\"no-options\">\n {{searchTerm() ? emptySearchText : emptyStateText}}\n </div>\n } @else {\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)\"\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)\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n </div>\n }\n } @else {\n <!-- Regular (Ungrouped) Options -->\n @for (option of displayOptions(); track trackByValue($index, option); let idx = $index) {\n <div\n class=\"option\"\n [class.selected]=\"isSelected(option)\"\n [class.highlighted]=\"idx === highlightedIndex()\"\n [class.disabled]=\"isOptionDisabled(option)\"\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)\"\n tabindex=\"-1\"\n >\n <!-- Checkbox for multi-select -->\n @if (isMulti) {\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n readonly\n />\n }\n\n <!-- Option Icon -->\n @if (showOptionIcons && option.icon) {\n <div class=\"option-icon\">\n @if (option.icon.includes('<')) {\n <div [innerHTML]=\"sanitizeHtml(option.icon)\"></div>\n } @else {\n <img [src]=\"option.icon\" [alt]=\"getOptionLabel(option)\" />\n }\n </div>\n }\n\n <!-- Option Content -->\n <div class=\"option-content\">\n <div class=\"option-label\">{{getOptionLabel(option)}}</div>\n @if (option.description) {\n <div class=\"option-description\">{{option.description}}</div>\n }\n </div>\n\n <!-- Option Badge -->\n @if (showOptionBadges && option.badge) {\n <span\n class=\"option-badge\"\n [style.background-color]=\"option.badgeColor || currentTheme().primary\"\n >\n {{option.badge}}\n </span>\n }\n\n <!-- Check Icon for selected -->\n @if (!isMulti && isSelected(option)) {\n <svg class=\"check-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <path d=\"M7.629,14.566c0.125,0.125,0.291,0.188,0.456,0.188c0.164,0,0.329-0.062,0.456-0.188l8.219-8.221c0.252-0.252,0.252-0.659,0-0.911c-0.252-0.252-0.659-0.252-0.911,0l-7.764,7.763L4.152,9.267c-0.252-0.251-0.66-0.251-0.911,0c-0.252,0.252-0.252,0.66,0,0.911L7.629,14.566z\"></path>\n </svg>\n }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n</div>\n\n<!-- Hidden native select for form compatibility -->\n@if (isMulti) {\n <select [name]=\"name\" [id]=\"id\" multiple style=\"display: none;\" [attr.aria-hidden]=\"true\">\n @for (option of selectedOptions(); track trackByValue($index, option)) {\n <option [value]=\"getOptionValue(option)\" selected>{{getOptionLabel(option)}}</option>\n }\n </select>\n} @else {\n @if (selectedOptions().length > 0) {\n <select [name]=\"name\" [id]=\"id\" style=\"display: none;\" [attr.aria-hidden]=\"true\">\n <option [value]=\"getOptionValue(selectedOptions()[0])\" selected>{{getOptionLabel(selectedOptions()[0])}}</option>\n </select>\n }\n}\n", styles: [":host{display:block;width:100%}.select-container{position:relative;width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.smaller{font-size:11px}.small{font-size:13px}.medium{font-size:14px}.large{font-size:16px}.larger{font-size:18px}.xs .select-trigger{min-height:28px;padding:4px 8px}.sm .select-trigger{min-height:32px;padding:6px 10px}.md .select-trigger{min-height:40px;padding:8px 12px}.lg .select-trigger{min-height:48px;padding:10px 14px}.xl .select-trigger{min-height:56px;padding:12px 16px}.select-container.disabled{opacity:.6;cursor:not-allowed}.select-container.rtl{direction:rtl}.select-trigger{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1.5px solid #D1D5DB;border-radius:10px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);min-height:38px;gap:8px;box-shadow:0 1px 2px #0000000d}.select-trigger:hover:not(.disabled){border-color:#9ca3af;box-shadow:0 2px 4px #00000014}.select-trigger:focus,.select-trigger.focused{outline:none;border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 3px #2684ff1a,0 1px 2px #0000000d}.select-value{flex:1;display:flex;align-items:center;min-width:0;gap:4px}.placeholder{color:#999;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.placeholder.has-value{color:#333}.tags{display:flex;flex-wrap:wrap;gap:4px;flex:1}.tag{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#e6f2ff;color:#0052cc;border-radius:6px;font-size:.875em;font-weight:500;white-space:nowrap;border:1px solid #CCE0FF;box-shadow:0 1px 2px #0000000d}.tag.disabled{background:#f0f0f0;color:#666;border-color:#d9d9d9}.tag-label{line-height:1.2}.tag-remove{background:none;border:none;color:inherit;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;border-radius:2px;transition:all .15s}.tag-remove:hover{background:#0052cc26}.tag-remove svg{fill:currentColor}.select-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}.clear-button{background:none;border:none;color:#999;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:all .15s}.clear-button:hover{color:#333;background:#f0f0f0}.clear-button svg{fill:currentColor}.separator{width:1px;height:24px;background:#ccc;align-self:stretch}.arrow{color:#999;transition:transform .2s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;padding:4px}.arrow svg{fill:currentColor}.arrow.open{transform:rotate(180deg)}.spinner{width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #2684FF;border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.dropdown{position:absolute;top:calc(100% + 8px);left:0;right:0;background:#fffffffa;border:1px solid #E5E7EB;border-radius:12px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #0000000d;z-index:1000;overflow:hidden;backdrop-filter:blur(12px) saturate(180%);-webkit-backdrop-filter:blur(12px) saturate(180%)}.dropdown.fixed{position:fixed}.dropdown.top{top:auto;bottom:calc(100% + 4px)}.search-container{padding:8px;border-bottom:1px solid #f0f0f0;background:#fff}.search-input{width:100%;padding:6px 8px;border:1px solid #cccccc;border-radius:3px;font-size:inherit;outline:none;transition:border-color .15s}.search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.options-list{max-height:inherit;overflow-y:auto;overflow-x:hidden;padding:4px 0}.options-list::-webkit-scrollbar{width:8px}.options-list::-webkit-scrollbar-track{background:transparent}.options-list::-webkit-scrollbar-thumb{background:#d9d9d9;border-radius:4px}.options-list::-webkit-scrollbar-thumb:hover{background:#b3b3b3}.select-all-container{padding:8px;border-bottom:1px solid #E5E7EB;background:#f9fafb}.select-all-button{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;background:#fff;border:1px solid #D1D5DB;border-radius:6px;cursor:pointer;transition:all .15s;font-size:inherit;color:inherit;font-family:inherit}.select-all-button:hover{background:#f3f4f6;border-color:#9ca3af}.select-all-button input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.select-all-text{flex:1;text-align:left;font-weight:500}.select-all-count{color:#6b7280;font-size:.9em}.option-group{margin:4px 0}.option-group-label{padding:8px 12px 4px;font-size:.75em;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;background:#f9fafb;border-bottom:1px solid #E5E7EB;position:sticky;top:0;z-index:1}.option-group .option{padding-left:20px}.option{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;transition:all .15s cubic-bezier(.4,0,.2,1);position:relative;min-height:40px}.option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.option-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6}.option-icon img{width:100%;height:100%;object-fit:cover}.option-badge{padding:2px 8px;border-radius:12px;font-size:.75em;font-weight:500;color:#374151;background:#e5e7eb;flex-shrink:0;white-space:nowrap}.option:hover:not(.disabled):not(.create-option){background:#deebff}.option.highlighted{background:#deebff}.option.selected:not(.create-option){background:#e6f2ff;font-weight:500}.option.disabled{opacity:.5;cursor:not-allowed;background:transparent!important}.option.hidden{display:none}.option.create-option{color:#2684ff;font-weight:500;background:#f0f6ff}.option.create-option:hover{background:#e6f2ff}.option input[type=checkbox]{cursor:pointer;width:16px;height:16px;margin:0;accent-color:#2684FF}.option-label{font-weight:500;color:#1f2937;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.option-description{font-size:.875em;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3}.check-icon{color:#2684ff;display:flex;align-items:center;margin-left:auto}.check-icon svg{fill:currentColor}.no-options,.loading-message{padding:16px 12px;text-align:center;color:#999;font-size:.95em}.rtl .select-actions,.rtl .tags,.rtl .option{flex-direction:row-reverse}.theme-blue .select-trigger:focus,.theme-blue .select-trigger.focused,.theme-blue .select-trigger.open{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-blue .option:hover:not(.disabled):not(.create-option),.theme-blue .option.highlighted{background:#deebff}.theme-blue .option.selected:not(.create-option){background:#e6f2ff}.theme-blue .tag{background:#e6f2ff;color:#0052cc;border-color:#cce0ff}.theme-blue .check-icon,.theme-blue .option.create-option{color:#2684ff}.theme-blue .spinner{border-top-color:#2684ff}.theme-blue .search-input:focus{border-color:#2684ff;box-shadow:0 0 0 1px #2684ff}.theme-purple .select-trigger:focus,.theme-purple .select-trigger.focused,.theme-purple .select-trigger.open{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-purple .option:hover:not(.disabled):not(.create-option),.theme-purple .option.highlighted{background:#f3e8ff}.theme-purple .option.selected:not(.create-option){background:#faf5ff}.theme-purple .tag{background:#faf5ff;color:#7e22ce;border-color:#e9d5ff}.theme-purple .check-icon,.theme-purple .option.create-option{color:#9333ea}.theme-purple .spinner{border-top-color:#9333ea}.theme-purple .search-input:focus{border-color:#9333ea;box-shadow:0 0 0 1px #9333ea}.theme-green .select-trigger:focus,.theme-green .select-trigger.focused,.theme-green .select-trigger.open{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-green .option:hover:not(.disabled):not(.create-option),.theme-green .option.highlighted{background:#d1fae5}.theme-green .option.selected:not(.create-option){background:#ecfdf5}.theme-green .tag{background:#ecfdf5;color:#059669;border-color:#a7f3d0}.theme-green .check-icon,.theme-green .option.create-option{color:#10b981}.theme-green .spinner{border-top-color:#10b981}.theme-green .search-input:focus{border-color:#10b981;box-shadow:0 0 0 1px #10b981}.theme-red .select-trigger:focus,.theme-red .select-trigger.focused,.theme-red .select-trigger.open{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-red .option:hover:not(.disabled):not(.create-option),.theme-red .option.highlighted{background:#fee2e2}.theme-red .option.selected:not(.create-option){background:#fef2f2}.theme-red .tag{background:#fef2f2;color:#dc2626;border-color:#fecaca}.theme-red .check-icon,.theme-red .option.create-option{color:#ef4444}.theme-red .spinner{border-top-color:#ef4444}.theme-red .search-input:focus{border-color:#ef4444;box-shadow:0 0 0 1px #ef4444}.theme-orange .select-trigger:focus,.theme-orange .select-trigger.focused,.theme-orange .select-trigger.open{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-orange .option:hover:not(.disabled):not(.create-option),.theme-orange .option.highlighted{background:#ffedd5}.theme-orange .option.selected:not(.create-option){background:#fff7ed}.theme-orange .tag{background:#fff7ed;color:#ea580c;border-color:#fed7aa}.theme-orange .check-icon,.theme-orange .option.create-option{color:#f97316}.theme-orange .spinner{border-top-color:#f97316}.theme-orange .search-input:focus{border-color:#f97316;box-shadow:0 0 0 1px #f97316}.theme-pink .select-trigger:focus,.theme-pink .select-trigger.focused,.theme-pink .select-trigger.open{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-pink .option:hover:not(.disabled):not(.create-option),.theme-pink .option.highlighted{background:#fce7f3}.theme-pink .option.selected:not(.create-option){background:#fdf2f8}.theme-pink .tag{background:#fdf2f8;color:#db2777;border-color:#fbcfe8}.theme-pink .check-icon,.theme-pink .option.create-option{color:#ec4899}.theme-pink .spinner{border-top-color:#ec4899}.theme-pink .search-input:focus{border-color:#ec4899;box-shadow:0 0 0 1px #ec4899}.theme-dark .select-trigger:focus,.theme-dark .select-trigger.focused,.theme-dark .select-trigger.open{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}.theme-dark .option:hover:not(.disabled):not(.create-option),.theme-dark .option.highlighted{background:#e5e7eb}.theme-dark .option.selected:not(.create-option){background:#f3f4f6}.theme-dark .tag{background:#f3f4f6;color:#111827;border-color:#d1d5db}.theme-dark .check-icon,.theme-dark .option.create-option{color:#1f2937}.theme-dark .spinner{border-top-color:#1f2937}.theme-dark .search-input:focus{border-color:#1f2937;box-shadow:0 0 0 1px #1f2937}\n"] }]
653
+ }], ctorParameters: () => [], propDecorators: { options: [{
654
+ type: Input
655
+ }], placeholder: [{
656
+ type: Input
657
+ }], isMulti: [{
658
+ type: Input
659
+ }], multiple: [{
660
+ type: Input
661
+ }], isSearchable: [{
662
+ type: Input
663
+ }], searchable: [{
664
+ type: Input
665
+ }], isClearable: [{
666
+ type: Input
667
+ }], clearable: [{
668
+ type: Input
669
+ }], isDisabled: [{
670
+ type: Input
671
+ }], disabled: [{
672
+ type: Input
673
+ }], isLoading: [{
674
+ type: Input
675
+ }], loading: [{
676
+ type: Input
677
+ }], isRtl: [{
678
+ type: Input
679
+ }], closeMenuOnSelect: [{
680
+ type: Input
681
+ }], hideSelectedOptions: [{
682
+ type: Input
683
+ }], isCreatable: [{
684
+ type: Input
685
+ }], allowCreateWhileLoading: [{
686
+ type: Input
687
+ }], createOptionPosition: [{
688
+ type: Input
689
+ }], formatCreateLabel: [{
690
+ type: Input
691
+ }], loadOptions: [{
692
+ type: Input
693
+ }], cacheOptions: [{
694
+ type: Input
695
+ }], defaultOptions: [{
696
+ type: Input
697
+ }], selectSize: [{
698
+ type: Input
699
+ }], containerSize: [{
700
+ type: Input
701
+ }], theme: [{
702
+ type: Input
703
+ }], borderRadius: [{
704
+ type: Input
705
+ }], customStyles: [{
706
+ type: Input
707
+ }], maxHeight: [{
708
+ type: Input
709
+ }], menuPlacement: [{
710
+ type: Input
711
+ }], menuPosition: [{
712
+ type: Input
713
+ }], getOptionLabel: [{
714
+ type: Input
715
+ }], getOptionValue: [{
716
+ type: Input
717
+ }], isOptionDisabled: [{
718
+ type: Input
719
+ }], filterOption: [{
720
+ type: Input
721
+ }], isGrouped: [{
722
+ type: Input
723
+ }], groupBy: [{
724
+ type: Input
725
+ }], showSelectAll: [{
726
+ type: Input
727
+ }], selectAllText: [{
728
+ type: Input
729
+ }], deselectAllText: [{
730
+ type: Input
731
+ }], showOptionIcons: [{
732
+ type: Input
733
+ }], showOptionBadges: [{
734
+ type: Input
735
+ }], maxOptionsDisplay: [{
736
+ type: Input
737
+ }], optionHeight: [{
738
+ type: Input
739
+ }], emptyStateText: [{
740
+ type: Input
741
+ }], emptySearchText: [{
742
+ type: Input
743
+ }], name: [{
744
+ type: Input
745
+ }], id: [{
746
+ type: Input
747
+ }], autoFocus: [{
748
+ type: Input
749
+ }], openMenuOnFocus: [{
750
+ type: Input
751
+ }], openMenuOnClick: [{
752
+ type: Input
753
+ }], tabSelectsValue: [{
754
+ type: Input
755
+ }], backspaceRemovesValue: [{
756
+ type: Input
757
+ }], escapeClearsValue: [{
758
+ type: Input
759
+ }], noOptionsMessage: [{
760
+ type: Input
761
+ }], loadingMessage: [{
762
+ type: Input
763
+ }], change: [{
764
+ type: Output
765
+ }], clear: [{
766
+ type: Output
767
+ }], focus: [{
768
+ type: Output
769
+ }], blur: [{
770
+ type: Output
771
+ }], menuOpen: [{
772
+ type: Output
773
+ }], menuClose: [{
774
+ type: Output
775
+ }], inputChange: [{
776
+ type: Output
777
+ }], createOption: [{
778
+ type: Output
779
+ }], optionsLoaded: [{
780
+ type: Output
781
+ }], loadError: [{
782
+ type: Output
783
+ }], selectContainerRef: [{
784
+ type: ViewChild,
785
+ args: ['selectContainer']
786
+ }], searchInputRef: [{
787
+ type: ViewChild,
788
+ args: ['searchInput']
789
+ }], menuElementRef: [{
790
+ type: ViewChild,
791
+ args: ['menuRef']
792
+ }], handleKeydown: [{
793
+ type: HostListener,
794
+ args: ['keydown', ['$event']]
795
+ }] } });
796
+
797
+ /*
798
+ * Public API Surface of angular-perfect-select
799
+ */
800
+ // Main Component
801
+
802
+ /**
803
+ * Generated bundle index. Do not edit.
804
+ */
805
+
806
+ export { ClickOutsideDirective, PerfectSelectComponent, THEMES, dropdownAnimation, optionListAnimation, selectAnimations, tagAnimation };
807
+ //# sourceMappingURL=angular-perfect-select.mjs.map