import { ChangeDetectionStrategy, Component, booleanAttribute, computed, Injector, inject, input, model, output, signal, viewChild, } from '@angular/core'; import { SlLookupApi } from '../../core/sl/sl-lookup'; import { ValueHelp } from '../value-help/value-help'; import type { VhLookup, VhRow } from '../value-help/value-help.types'; import { TextInput } from './controls'; import { FieldBase } from './field-base'; import { SboField } from './sbo-field'; /** * Campo con botón "…" que abre una lista de valores (flecha dorada de SAP). * Escribir el código a mano también es válido; elegir de la lista lo rellena y emite `picked`. */ @Component({ selector: 'sbo-lookup-field', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SboField, TextInput, ValueHelp], template: ` `, styles: ':host { display: block; }', }) export class LookupField extends FieldBase { readonly value = model(''); readonly lookup = input.required(); /** La etiqueta abre el objeto enlazado (evento `linkClick`). */ readonly linkable = input(false, { transform: booleanAttribute }); /** Al salir del campo con un código escrito a mano, lo busca en la lista (como si se hubiera elegido). */ readonly resolveOnLeave = input(false, { transform: booleanAttribute }); readonly picked = output(); readonly linkClick = output(); protected readonly listOpen = signal(false); /** Código que no existe; el aviso desaparece en cuanto el texto cambia. */ private readonly missing = signal<{ code: string; text: string } | null>(null); protected readonly missingText = computed(() => { const m = this.missing(); return m && m.code === this.value().trim() ? m.text : ''; }); private readonly injector = inject(Injector); /** Último código ya resuelto, para no repetir la consulta al salir y volver a entrar sin cambios. */ private resolved = ''; private readonly box = viewChild.required('box'); protected openList(): void { if (!this.readOnly() && !this.disabled()) this.listOpen.set(true); } protected closeList(): void { this.listOpen.set(false); this.box().focus(); } protected onPicked(rows: VhRow[]): void { const row = rows[0]; if (!row) return; this.apply(row); } private apply(row: VhRow): void { const key = this.lookup().valueKey ?? this.lookup().columns[0]?.key ?? ''; this.missing.set(null); this.value.set(String(row[key] ?? '')); this.resolved = this.value(); this.picked.emit(row); } protected async resolveTyped(): Promise { const code = this.value().trim(); if (!this.resolveOnLeave() || this.readOnly() || this.disabled() || this.listOpen()) return; if (!code) { this.missing.set(null); this.resolved = ''; return; } if (code === this.resolved) return; try { const row = await this.injector.get(SlLookupApi).find(this.lookup(), code); if (this.value().trim() !== code) return; // el usuario siguió escribiendo if (row) this.apply(row); else this.missing.set({ code, text: `No existe «${code}» en ${this.lookup().title.toLowerCase()}.` }); } catch { this.missing.set({ code, text: 'No se pudo comprobar el código.' }); } } }