import { Component, booleanAttribute, forwardRef, input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; /** Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive). */ @Component({ selector: 'app-text-input', styles: [ ` :host { display: block; } input { inline-size: 100%; box-sizing: border-box; } `, ], template: ` `, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextInputComponent), multi: true }, ], }) export class TextInputComponent implements ControlValueAccessor { type = input<'text' | 'password' | 'email'>('text'); placeholder = input(''); invalid = input(false); inputId = input(); /** Set when the paired form-field renders a `-desc` hint, so it gets announced. */ hasDescription = input(false, { transform: booleanAttribute }); value = ''; disabled = false; onChange: (v: string) => void = () => {}; onTouched: () => void = () => {}; describedBy(): string | null { const id = this.inputId(); if (!id) return null; const ids = [ ...(this.hasDescription() ? [`${id}-desc`] : []), ...(this.invalid() ? [`${id}-error`] : []), ]; return ids.length ? ids.join(' ') : null; } onInput(e: Event) { this.value = (e.target as HTMLInputElement).value; this.onChange(this.value); } writeValue(v: string) { this.value = v ?? ''; } registerOnChange(fn: (v: string) => void) { this.onChange = fn; } registerOnTouched(fn: () => void) { this.onTouched = fn; } setDisabledState(d: boolean) { this.disabled = d; } }