All files / components/text-input text-input.component.ts

97.82% Statements 45/46
50% Branches 3/6
83.33% Functions 10/12
97.77% Lines 44/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 981x 1x 1x 1x 1x                                         1x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x   22x   22x 22x   22x   22x     22x 26x 26x       22x   22x     22x         2x       1x 1x       2x       1x       3x 3x 3x 3x 3x       2x      
import { ChangeDetectionStrategy, Component, computed, effect, input, output, forwardRef, signal } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldAppearance, MatFormFieldModule } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
 
type TextInputAppearance = 'outline' | 'fill' | 'underline';
 
@Component({
    selector: 'ui-text-input',
    imports: [MatFormFieldModule, MatIcon, MatInputModule, ReactiveFormsModule],
    templateUrl: './text-input.component.html',
    styleUrl: './text-input.component.scss',
    changeDetection: ChangeDetectionStrategy.OnPush,
    host: {
        '[class.appearance-underline]': 'appearance() === "underline"',
    },
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => TextInputComponent),
            multi: true,
        },
    ],
})
export class TextInputComponent implements ControlValueAccessor {
    appearance = input<TextInputAppearance>('outline');
    icon = input<string>();
    label = input<string>('');
    placeholder = input<string>('');
    hint = input<string>('');
    errorMessage = input<string>('');
    type = input<'text' | 'email' | 'password' | 'number' | 'tel' | 'url'>('text');
    autocomplete = input<string>('off');
    disabled = input<boolean>(false);
    readonly = input<boolean>(false);
    required = input<boolean>(false);
    maxLength = input<number | undefined>(undefined);
    minLength = input<number | undefined>(undefined);
    multiline = input<boolean>(false);
    rows = input<number>(3);
    value = input<string>('');
 
    valueChanged = output<string>();
 
    protected matAppearance = computed<MatFormFieldAppearance>(() =>
        this.appearance() === 'outline' ? 'outline' : 'fill'
    );
    protected displayValue = signal<string>('');
 
    private isChangesHandled = false;
 
    constructor() {
        effect(() => {
            Eif (!this.isChangesHandled) {
                this.displayValue.set(this.value());
            }
        });
    }
    protected isDisabled = signal<boolean>(false);
 
    private onChange: (value: string) => void = () => {
        /* empty */
    };
    private onTouched: () => void = () => {
        /* empty */
    };
 
    writeValue(value: string): void {
        this.displayValue.set(value ?? '');
    }
 
    registerOnChange(fn: (value: string) => void): void {
        this.isChangesHandled = true;
        this.onChange = fn;
    }
 
    registerOnTouched(fn: () => void): void {
        this.onTouched = fn;
    }
 
    setDisabledState(isDisabled: boolean): void {
        this.isDisabled.set(isDisabled);
    }
 
    protected onInput(event: Event): void {
        const input = event.target as HTMLInputElement | HTMLTextAreaElement;
        const newValue = input.value;
        this.displayValue.set(newValue);
        this.onChange(newValue);
        this.valueChanged.emit(newValue);
    }
 
    protected onBlur(): void {
        this.onTouched();
    }
}