"use client"; import { useEffect, useRef, useState } from "react"; import { Input } from "../../../../shadcnui"; interface TotpInputProps { onComplete: (code: string) => void; disabled?: boolean; autoFocus?: boolean; error?: string; } export function TotpInput({ onComplete, disabled = false, autoFocus = true, error }: TotpInputProps) { const [digits, setDigits] = useState(["", "", "", "", "", ""]); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); useEffect(() => { if (autoFocus && inputRefs.current[0]) { inputRefs.current[0].focus(); } }, [autoFocus]); const handleChange = (index: number, value: string) => { // Only allow digits const digit = value.replace(/\D/g, "").slice(-1); const newDigits = [...digits]; newDigits[index] = digit; setDigits(newDigits); // Move to next input if (digit && index < 5) { inputRefs.current[index + 1]?.focus(); } // Check if complete const code = newDigits.join(""); if (code.length === 6 && newDigits.every((d) => d !== "")) { onComplete(code); } }; const handleKeyDown = (index: number, e: React.KeyboardEvent) => { if (e.key === "Backspace" && !digits[index] && index > 0) { inputRefs.current[index - 1]?.focus(); } }; const handlePaste = (e: React.ClipboardEvent) => { e.preventDefault(); const pastedData = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, 6); if (pastedData.length === 6) { const newDigits = pastedData.split(""); setDigits(newDigits); inputRefs.current[5]?.focus(); onComplete(pastedData); } }; return (
{digits.map((digit, index) => ( { inputRefs.current[index] = el; }} type="text" inputMode="numeric" maxLength={1} value={digit} onChange={(e) => handleChange(index, e.target.value)} onKeyDown={(e) => handleKeyDown(index, e)} onPaste={handlePaste} disabled={disabled} className={`w-12 h-14 text-center text-2xl tabular-nums ${error ? "border-destructive" : ""}`} data-testid={`totp-input-${index}`} /> ))}
{error && (

{error}

)}
); }