line-gestao-frontend/src/app/components/custom-select/custom-select.ts

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();
}
}