import {
Component,
Input,
Output,
EventEmitter,
ChangeDetectionStrategy,
ChangeDetectorRef,
HostListener,
ElementRef,
inject,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import 'mayvio-ui/multiselect/css';
export interface MultiSelectOption {
value: string | number;
label: string;
disabled?: boolean;
}
@Component({
selector: 'mv-multiselect',
standalone: true,
imports: [CommonModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
{{ placeholder }}
0">
{{ opt.label }}
`,
})
export class MultiSelectComponent {
@Input() options: MultiSelectOption[] = [];
@Input() value: (string | number)[] = [];
@Input() disabled = false;
@Input() searchable = false;
@Input() placeholder = 'Select options...';
@Input() emptyText = 'No options found.';
@Input() className = '';
@Output() selectionChange = new EventEmitter<(string | number)[]>();
isOpen = false;
searchQuery = '';
private el = inject(ElementRef);
private cdr = inject(ChangeDetectorRef);
get selectedOptions(): MultiSelectOption[] {
return this.options.filter((opt) => this.value.includes(opt.value));
}
get filteredOptions(): MultiSelectOption[] {
if (!this.searchable || !this.searchQuery) {
return this.options;
}
const q = this.searchQuery.toLowerCase();
return this.options.filter((opt) => opt.label.toLowerCase().includes(q));
}
isSelected(val: string | number): boolean {
return this.value.includes(val);
}
toggleMenu() {
if (this.disabled) return;
this.isOpen = !this.isOpen;
if (!this.isOpen) {
this.searchQuery = '';
}
this.cdr.markForCheck();
}
toggleOption(val: string | number) {
const newVal = this.value.includes(val)
? this.value.filter((v) => v !== val)
: [...this.value, val];
this.value = newVal;
this.selectionChange.emit(this.value);
this.cdr.markForCheck();
}
onOptionClick(event: Event, opt: MultiSelectOption) {
event.stopPropagation();
if (!opt.disabled) {
this.toggleOption(opt.value);
}
}
removeTag(event: Event, val: string | number) {
event.stopPropagation();
this.toggleOption(val);
}
onSearch(event: Event) {
const input = event.target as HTMLInputElement;
this.searchQuery = input.value;
this.cdr.markForCheck();
}
@HostListener('document:mousedown', ['$event.target'])
onClickOutside(target: EventTarget | null) {
if (this.isOpen && !this.el.nativeElement.contains(target as Node)) {
this.isOpen = false;
this.searchQuery = '';
this.cdr.markForCheck();
}
}
}