Files
familytree/UI/src/app/shares/timeinput.ts
T
2025-08-10 22:01:36 +10:00

150 lines
4.0 KiB
TypeScript

import { CommonModule } from '@angular/common';
import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'time-input',
imports:[ReactiveFormsModule,CommonModule],
template: `
<input
type="text"
[formControl]="timeControl"
(input)="onInput($event)"
(blur)="onBlur()"
class="time-input"
style="width: 80px; text-align: center;"
/>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeInputComponent),
multi: true,
},
],
})
export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
@Input() placeholder: string = 'HH:MM';
@Input() initialValue: string = ''; // Added initialValue input
@Input() disabled: boolean = false;
timeControl = new FormControl('', [
Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
]);
private destroy$ = new Subject<void>();
private _value: string = '';
private onChange: (value: string) => void = () => {};
private onTouched: () => void = () => {};
constructor() {}
ngOnInit(): void {
this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.onChange(value || ''); // Pass the value to the parent form
});
if (this.initialValue) {
this.writeValue(this.initialValue);
}
this.timeControl.disable();
if(!this.disabled){
this.timeControl.enable();
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
get value(): string {
return this._value;
}
set value(val: string) {
this._value = val;
this.onChange(val);
this.onTouched();
}
writeValue(value: string): void {
this._value = value || '';
this.formatInput(this._value); // Use the formatting function
}
registerOnChange(fn: (value: string) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.timeControl.disable();
} else {
this.timeControl.enable();
}
}
onInput(event: Event): void {
let input = (event.target as HTMLInputElement).value;
input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
if (input.length > 4) {
input = input.slice(0, 4); // Limit to 4 digits
}
let formatted = '';
if (input.length > 2) {
formatted = input.slice(0, 2) + ':' + input.slice(2);
} else {
formatted = input;
}
this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
this.value = formatted;
}
onBlur(): void {
this.onTouched();
if (this.timeControl.valid) {
return;
}
if (this.timeControl.value?.length === 0) {
this.timeControl.setValue('', {emitEvent: false});
this.value = '';
return
}
this.formatInput(this.timeControl.value);
}
private formatInput(val: string | null): void {
if (!val) {
this.timeControl.setValue('', {emitEvent: false});
this.value = '';
return;
}
let numbersOnly = val.replace(/[^0-9]/g, '');
let formatted = '';
if (numbersOnly.length > 2) {
formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
} else {
formatted = numbersOnly;
}
if (formatted.length === 5 && this.timeControl.valid) {
this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted;
} else if (numbersOnly.length <= 2) {
this.timeControl.setValue(formatted, {emitEvent: false});
this.value = formatted;
} else {
this.timeControl.setValue('00:00', {emitEvent: false});
this.value = '00:00'
}
}
}