import {
Component,
ElementRef,
EventEmitter,
Input,
Output,
QueryList,
ViewChildren,
} from '@angular/core';
@Component({
selector: 'vsp-otp-input',
template: `
@for (i of indices; track i) {
}
`,
})
export class VspOTPInput {
@Input() length = 6;
@Input() value = '';
@Input() disabled = false;
@Input() invalid = false;
@Output() valueChange = new EventEmitter();
@Output() complete = new EventEmitter();
@ViewChildren('otp') inputs!: QueryList>;
get indices(): number[] {
return Array.from({ length: this.length }, (_, i) => i);
}
private chars(): string[] {
return Array.from({ length: this.length }, (_, k) => this.value[k] ?? '');
}
private emit(next: string[]): void {
this.value = next.join('');
this.valueChange.emit(this.value);
if (this.value.length === this.length) this.complete.emit(this.value);
}
set(i: number, raw: string): void {
const clean = raw.replace(/\D/g, '');
const next = this.chars();
next[i] = clean.slice(-1);
this.emit(next);
if (clean && i < this.length - 1) this.inputs.get(i + 1)?.nativeElement.focus();
}
onPaste(i: number, e: ClipboardEvent): void {
const text = (e.clipboardData?.getData('text') ?? '').replace(/\D/g, '');
if (!text) return;
e.preventDefault();
const next = this.chars();
for (let j = 0; j < text.length && i + j < this.length; j++) next[i + j] = text[j]!;
this.emit(next);
this.inputs.get(Math.min(i + text.length, this.length - 1))?.nativeElement.focus();
}
onKey(i: number, e: KeyboardEvent): void {
if (e.key === 'Backspace' && !this.value[i] && i > 0)
this.inputs.get(i - 1)?.nativeElement.focus();
}
}