import { useState, useRef, useEffect } from "react"; // ============================================= // Constants (DO NOT MODIFY) // ============================================= const OTP_LENGTH = 6; // ============================================= // TODO: Implement OTP Input Component // ============================================= // // Requirements: // 1. Render OTP_LENGTH individual digit input boxes // 2. Only allow numeric input (0-9) // 3. Auto-focus the first input on mount // 4. Auto-advance: typing a digit moves focus to the next box // 5. Backspace: if current box is empty, move focus to previous box // 6. Paste support: pasting "123456" fills all boxes at once // 7. Show a "complete" state when all digits are filled // 8. Clear button resets all boxes and focuses the first one // // State: // - otp: string[] of length OTP_LENGTH (each element is "" or a digit) // - isComplete: boolean — true when every slot has a digit // // Refs: // - inputRefs: an array of refs, one per input box // useRef<(HTMLInputElement | null)[]>([]) // Assign each input via: ref={(el) => { inputRefs.current[i] = el; }} // // Hints: // - handleChange(index, value): // • Reject non-numeric input: if (!/^\d*$/.test(value)) return // • Take only the last character: value.slice(-1) // • Update otp[index] with the digit // • If digit exists and index < OTP_LENGTH - 1 → focus next input // • Check if all slots filled → setIsComplete // // - handleKeyDown(index, e): // • If Backspace and current box is empty and index > 0 // → focus previous input // • Note: the Backspace keypress propagates to the newly focused // input, so if that input has a value it will be deleted by the // browser's default behavior (this is expected) // // - handlePaste(e): // • e.preventDefault() // • Get pasted text, strip non-digits, slice to OTP_LENGTH // • Fill otp array from index 0 // • Focus the box after the last pasted digit // • Check completeness export default function App() { // TODO: Initialize OTP state as an array of empty strings // const [otp, setOtp] = useState<string[]>(Array(OTP_LENGTH).fill("")); // const [isComplete, setIsComplete] = useState(false); // TODO: Create refs array for the input elements // const inputRefs = useRef<(HTMLInputElement | null)[]>([]); // ============================================= // TODO: Auto-focus first input on mount // ============================================= // useEffect(() => { // inputRefs.current[0]?.focus(); // }, []); // ============================================= // TODO: Implement handleChange // ============================================= // Called on each input's onChange event. // Should: // 1. Reject non-numeric characters // 2. Take only the last character typed // 3. Update the otp array at the given index // 4. Auto-advance focus to the next input // 5. Update isComplete if all slots are filled const handleChange = (index: number, value: string) => { // Your code here }; // ============================================= // TODO: Implement handleKeyDown // ============================================= // Called on each input's onKeyDown event. // Should: // - On Backspace: if current input is empty and not the first, // move focus to the previous input const handleKeyDown = (index: number, e: React.KeyboardEvent) => { // Your code here }; // ============================================= // TODO: Implement handlePaste // ============================================= // Called on any input's onPaste event. // Should: // 1. Prevent default paste behavior // 2. Extract pasted text, remove non-digits, limit to OTP_LENGTH // 3. Fill the otp array starting from index 0 // 4. Focus the appropriate input after pasting // 5. Check completeness const handlePaste = (e: React.ClipboardEvent) => { // Your code here }; // ============================================= // TODO: Implement clearOtp // ============================================= // Reset all digits to empty, set isComplete false, // and focus the first input. const clearOtp = () => { // Your code here }; // Placeholder state for rendering (remove once you add real state) const otp = Array(OTP_LENGTH).fill(""); const isComplete = false; return ( <div style={{ maxWidth: 480, margin: "0 auto", padding: 24, fontFamily: "system-ui" }}> <h2 style={{ fontSize: 20, fontWeight: 700, marginBottom: 4 }}> OTP Input Component </h2> <p style={{ fontSize: 14, color: "#666", marginBottom: 32 }}> Build a {OTP_LENGTH}-digit OTP input with auto-advance, backspace navigation, paste support, and numeric-only validation. </p> {/* ============================================= */} {/* TODO: OTP Input Boxes */} {/* ============================================= */} {/* Map over the otp array to render OTP_LENGTH inputs. Each input should: - ref={(el) => { inputRefs.current[i] = el; }} - type="text" inputMode="numeric" maxLength={1} - value={otp[i]} - onChange → handleChange(i, e.target.value) - onKeyDown → handleKeyDown(i, e) - onPaste → handlePaste - Style: filled inputs get a darker border */} <div style={{ display: "flex", justifyContent: "center", gap: 12, marginBottom: 32 }}> {otp.map((digit: string, i: number) => ( <input key={i} // TODO: Attach ref, onChange, onKeyDown, onPaste type="text" inputMode="numeric" maxLength={1} value={digit} readOnly style={{ width: 48, height: 56, textAlign: "center", fontSize: 20, fontWeight: 600, border: digit ? "2px solid #111" : "2px solid #d1d5db", borderRadius: 8, outline: "none", background: digit ? "#f9fafb" : "#fff", color: "#111", transition: "border-color 0.2s, background 0.2s", }} /> ))} </div> {/* Actions */} <div style={{ display: "flex", justifyContent: "center", gap: 12, marginBottom: 32 }}> <button onClick={clearOtp} style={{ padding: "8px 20px", border: "1px solid #d1d5db", borderRadius: 8, fontSize: 13, color: "#555", cursor: "pointer", }} > Clear </button> </div> {/* Status */} <div style={{ textAlign: "center", fontSize: 14 }}> {isComplete ? ( <span style={{ color: "#22c55e", fontWeight: 600 }}> OTP: {otp.join("")} ✓ </span> ) : ( <span style={{ color: "#999" }}> Enter your {OTP_LENGTH}-digit OTP </span> )} </div> {/* Hint */} <div style={{ marginTop: 32, padding: 16, background: "#f9fafb", borderLeft: "4px solid #111", borderRadius: "0 8px 8px 0", fontSize: 13, color: "#555", }}> <strong>Key concepts:</strong> <ul style={{ margin: "8px 0 0 16px", padding: 0, lineHeight: 1.8 }}> <li>Use <code>useRef</code> to hold an array of input refs for focus management</li> <li>Validate with <code>/^\d*$/</code> — reject non-numeric input</li> <li>Auto-advance: after setting a digit, call <code>inputRefs.current[i+1]?.focus()</code></li> <li>Backspace on empty box: focus previous input (the keypress propagates and deletes its value)</li> <li>Paste: <code>e.clipboardData.getData("text").replace(/\D/g, "")</code></li> </ul> <p style={{ marginTop: 12 }}> Try typing digits, pressing Backspace, or pasting{" "} <code style={{ background: "#f3f4f6", padding: "2px 6px", borderRadius: 4 }}>123456</code> </p> </div> </div> ); }