119 lines
3.1 KiB
TypeScript
119 lines
3.1 KiB
TypeScript
import { Component, ElementRef, HostListener, Input, forwardRef } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
|
|
@Component({
|
|
selector: 'app-select',
|
|
standalone: true,
|
|
imports: [CommonModule],
|
|
templateUrl: './custom-select.html',
|
|
styleUrls: ['./custom-select.scss'],
|
|
providers: [
|
|
{
|
|
provide: NG_VALUE_ACCESSOR,
|
|
useExisting: forwardRef(() => CustomSelectComponent),
|
|
multi: true,
|
|
},
|
|
],
|
|
})
|
|
export class CustomSelectComponent implements ControlValueAccessor {
|
|
@Input() options: any[] = [];
|
|
@Input() placeholder = 'Selecione uma opção';
|
|
@Input() labelKey = 'label';
|
|
@Input() valueKey = 'value';
|
|
@Input() size: 'sm' | 'md' = 'md';
|
|
@Input() disabled = false;
|
|
|
|
isOpen = false;
|
|
value: any = null;
|
|
|
|
private onChange: (value: any) => void = () => {};
|
|
private onTouched: () => void = () => {};
|
|
|
|
constructor(private host: ElementRef<HTMLElement>) {}
|
|
|
|
writeValue(value: any): void {
|
|
this.value = value;
|
|
}
|
|
|
|
registerOnChange(fn: (value: any) => void): void {
|
|
this.onChange = fn;
|
|
}
|
|
|
|
registerOnTouched(fn: () => void): void {
|
|
this.onTouched = fn;
|
|
}
|
|
|
|
setDisabledState(isDisabled: boolean): void {
|
|
this.disabled = isDisabled;
|
|
if (this.disabled) this.isOpen = false;
|
|
}
|
|
|
|
get displayLabel(): string {
|
|
const selected = this.findOption(this.value);
|
|
if (selected !== undefined) return this.getOptionLabel(selected);
|
|
if (this.value === null || this.value === undefined || this.value === '') return this.placeholder;
|
|
return String(this.value);
|
|
}
|
|
|
|
get hasValue(): boolean {
|
|
return !(this.value === null || this.value === undefined || this.value === '');
|
|
}
|
|
|
|
toggle(): void {
|
|
if (this.disabled) return;
|
|
this.isOpen = !this.isOpen;
|
|
}
|
|
|
|
close(): void {
|
|
this.isOpen = false;
|
|
}
|
|
|
|
selectOption(option: any): void {
|
|
if (this.disabled) return;
|
|
const value = this.getOptionValue(option);
|
|
this.value = value;
|
|
this.onChange(value);
|
|
this.onTouched();
|
|
this.close();
|
|
}
|
|
|
|
isSelected(option: any): boolean {
|
|
return this.getOptionValue(option) === this.value;
|
|
}
|
|
|
|
trackByValue = (_: number, option: any) => this.getOptionValue(option);
|
|
|
|
private getOptionValue(option: any): any {
|
|
if (option && typeof option === 'object') {
|
|
return option[this.valueKey];
|
|
}
|
|
return option;
|
|
}
|
|
|
|
getOptionLabel(option: any): string {
|
|
if (option && typeof option === 'object') {
|
|
const v = option[this.labelKey];
|
|
return v === undefined || v === null ? '' : String(v);
|
|
}
|
|
return option === undefined || option === null ? '' : String(option);
|
|
}
|
|
|
|
private findOption(value: any): any {
|
|
return (this.options || []).find((o) => this.getOptionValue(o) === value);
|
|
}
|
|
|
|
@HostListener('document:click', ['$event'])
|
|
onDocumentClick(event: MouseEvent): void {
|
|
if (!this.isOpen) return;
|
|
const target = event.target as Node | null;
|
|
if (target && this.host.nativeElement.contains(target)) return;
|
|
this.close();
|
|
}
|
|
|
|
@HostListener('document:keydown.escape')
|
|
onEsc(): void {
|
|
if (this.isOpen) this.close();
|
|
}
|
|
}
|