102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import {
|
|||
|
|
ChangeDetectionStrategy,
|
||
|
|
Component,
|
||
|
|
type ElementRef,
|
||
|
|
booleanAttribute,
|
||
|
|
input,
|
||
|
|
model,
|
||
|
|
output,
|
||
|
|
signal,
|
||
|
|
viewChild,
|
||
|
|
} from '@angular/core';
|
||
|
|
import { ValueHelp } from '../value-help/value-help';
|
||
|
|
import type { VhLookup, VhRow } from '../value-help/value-help.types';
|
||
|
|
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, ValueHelp],
|
||
|
|
template: `
|
||
|
|
<sbo-field
|
||
|
|
[label]="label()"
|
||
|
|
[for]="id"
|
||
|
|
[required]="required()"
|
||
|
|
[error]="error()"
|
||
|
|
action
|
||
|
|
[linkable]="linkable()"
|
||
|
|
(labelClick)="linkClick.emit()"
|
||
|
|
>
|
||
|
|
<div class="sbo-pick">
|
||
|
|
<input
|
||
|
|
#box
|
||
|
|
class="sbo-in"
|
||
|
|
type="text"
|
||
|
|
autocomplete="off"
|
||
|
|
[id]="id"
|
||
|
|
[value]="value()"
|
||
|
|
[readOnly]="readOnly()"
|
||
|
|
[disabled]="disabled()"
|
||
|
|
[required]="required()"
|
||
|
|
[attr.aria-invalid]="error() ? 'true' : null"
|
||
|
|
(input)="value.set($any($event.target).value)"
|
||
|
|
(keydown.f4)="$event.preventDefault(); openList()"
|
||
|
|
/>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
title="Lista de valores (F4)"
|
||
|
|
[attr.aria-label]="'Abrir la lista de ' + label()"
|
||
|
|
[disabled]="readOnly() || disabled()"
|
||
|
|
(click)="openList()"
|
||
|
|
>
|
||
|
|
…
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</sbo-field>
|
||
|
|
<sbo-value-help
|
||
|
|
[open]="listOpen()"
|
||
|
|
[title]="lookup().title"
|
||
|
|
[resource]="lookup().resource"
|
||
|
|
[columns]="lookup().columns"
|
||
|
|
[baseFilter]="lookup().baseFilter"
|
||
|
|
[orderby]="lookup().orderby"
|
||
|
|
(selected)="onPicked($event)"
|
||
|
|
(closed)="closeList()"
|
||
|
|
/>
|
||
|
|
`,
|
||
|
|
styles: ':host { display: block; }',
|
||
|
|
})
|
||
|
|
export class LookupField extends FieldBase {
|
||
|
|
readonly value = model('');
|
||
|
|
readonly lookup = input.required<VhLookup>();
|
||
|
|
/** La etiqueta abre el objeto enlazado (evento `linkClick`). */
|
||
|
|
readonly linkable = input(false, { transform: booleanAttribute });
|
||
|
|
|
||
|
|
readonly picked = output<VhRow>();
|
||
|
|
readonly linkClick = output<void>();
|
||
|
|
|
||
|
|
protected readonly listOpen = signal(false);
|
||
|
|
private readonly box = viewChild.required<ElementRef<HTMLInputElement>>('box');
|
||
|
|
|
||
|
|
protected openList(): void {
|
||
|
|
if (!this.readOnly() && !this.disabled()) this.listOpen.set(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected closeList(): void {
|
||
|
|
this.listOpen.set(false);
|
||
|
|
this.box().nativeElement.focus();
|
||
|
|
}
|
||
|
|
|
||
|
|
protected onPicked(rows: VhRow[]): void {
|
||
|
|
const row = rows[0];
|
||
|
|
if (!row) return;
|
||
|
|
const key = this.lookup().valueKey ?? this.lookup().columns[0]?.key ?? '';
|
||
|
|
this.value.set(String(row[key] ?? ''));
|
||
|
|
this.picked.emit(row);
|
||
|
|
}
|
||
|
|
}
|