// @ts-nocheck import { Component, Input, Output, EventEmitter, ElementRef, HostListener, ViewChild, OnInit } from "@angular/core"; export interface DropdownOption { value: string; label: string; } /** * Angular Component Wrapper for Mayvio UI Dropdown. * Supports single and multi-select modes, search, and full keyboard navigation. */ @Component({ selector: "mayvio-dropdown", template: ` ` }) export class DropdownComponent implements OnInit { @Input() options: DropdownOption[] = []; @Input() isMulti: boolean = false; @Input() placeholder: string = "Select option..."; @Input() value: string | string[] = ""; @Output() valueChange = new EventEmitter(); @ViewChild("searchInput") searchInput!: ElementRef; isOpen = false; searchQuery = ""; focusedIndex = -1; constructor(private elementRef: ElementRef) {} ngOnInit() { if (this.isMulti && !Array.isArray(this.value)) { this.value = []; } } // Close dropdown if clicking outside the component @HostListener("document:click", ["$event"]) onDocumentClick(event: MouseEvent) { if (!this.elementRef.nativeElement.contains(event.target)) { this.close(); } } toggleOpen(event: MouseEvent) { event.stopPropagation(); if (this.isOpen) { this.close(); } else { this.isOpen = true; this.searchQuery = ""; this.focusedIndex = -1; // Focus the search box on next tick setTimeout(() => this.searchInput?.nativeElement.focus(), 0); } } close() { this.isOpen = false; this.searchQuery = ""; this.focusedIndex = -1; } onSearchInput(query: string) { this.searchQuery = query; this.focusedIndex = -1; // reset selection focus } getVisibleOptions(): DropdownOption[] { const query = this.searchQuery.toLowerCase(); return this.options.filter((opt) => opt.label.toLowerCase().includes(query)); } isSelected(option: DropdownOption): boolean { if (this.isMulti) { return Array.isArray(this.value) && this.value.includes(option.value); } return this.value === option.value; } isPlaceholderActive(): boolean { if (this.isMulti) { return !Array.isArray(this.value) || this.value.length === 0; } return !this.value; } getTriggerLabel(): string { const selected = this.getSelectedOptions(); if (this.isMulti) { return selected.length ? `${selected.length} selected` : this.placeholder; } return selected.length ? selected[0].label : this.placeholder; } getSelectedOptions(): DropdownOption[] { if (this.isMulti) { return this.options.filter((opt) => Array.isArray(this.value) && this.value.includes(opt.value)); } const match = this.options.find((opt) => opt.value === this.value); return match ? [match] : []; } selectOption(option: DropdownOption, event?: MouseEvent) { if (event) event.stopPropagation(); if (this.isMulti) { const currentValues = Array.isArray(this.value) ? [...this.value] : []; const index = currentValues.indexOf(option.value); if (index >= 0) { currentValues.splice(index, 1); } else { currentValues.push(option.value); } this.value = currentValues; this.valueChange.emit(this.value); } else { this.value = option.value; this.valueChange.emit(this.value); this.close(); } } removeChip(option: DropdownOption, event: MouseEvent) { event.stopPropagation(); if (this.isMulti && Array.isArray(this.value)) { this.value = this.value.filter((v) => v !== option.value); this.valueChange.emit(this.value); } } handleKeyDown(event: KeyboardEvent) { const visible = this.getVisibleOptions(); if (!this.isOpen || visible.length === 0) return; if (event.key === "ArrowDown") { event.preventDefault(); this.focusedIndex = Math.min(this.focusedIndex + 1, visible.length - 1); } else if (event.key === "ArrowUp") { event.preventDefault(); this.focusedIndex = Math.max(this.focusedIndex - 1, 0); } else if (event.key === "Enter") { event.preventDefault(); if (this.focusedIndex >= 0 && this.focusedIndex < visible.length) { this.selectOption(visible[this.focusedIndex]); } } else if (event.key === "Escape") { this.close(); } } }