UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

1 lines 24.1 kB
{"version":3,"file":"verification-code.cjs","sources":["../../../../src/components/forms/verification-code.tsx"],"sourcesContent":["/**\n * @frank-auth/react - Verification Code Component\n *\n * OTP/verification code input with auto-focus, paste support, and\n * customizable length. Supports MFA, email verification, and SMS codes.\n */\n\n\"use client\";\n\nimport { Button, Input, Progress } from \"@/components/ui\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport React from \"react\";\nimport { useConfig } from \"../../hooks/use-config\";\nimport { FieldError } from \"./field-error\";\nimport { useFormField } from \"./form-wrapper\";\n\n// ============================================================================\n// Verification Code Interface\n// ============================================================================\n\nexport interface VerificationCodeProps {\n\t/**\n\t * Field name for form handling\n\t */\n\tname?: string;\n\n\t/**\n\t * Field label\n\t */\n\tlabel?: string;\n\n\t/**\n\t * Number of code digits\n\t */\n\tlength?: number;\n\n\t/**\n\t * Code value\n\t */\n\tvalue?: string;\n\n\t/**\n\t * Change handler\n\t */\n\tonChange?: (value: string) => void;\n\n\t/**\n\t * Complete handler (called when all digits entered)\n\t */\n\tonComplete?: (value: string) => void;\n\n\t/**\n\t * Blur handler\n\t */\n\tonBlur?: () => void;\n\n\t/**\n\t * Focus handler\n\t */\n\tonFocus?: () => void;\n\n\t/**\n\t * Whether field is required\n\t */\n\trequired?: boolean;\n\n\t/**\n\t * Whether field is disabled\n\t */\n\tdisabled?: boolean;\n\n\t/**\n\t * Whether to auto-focus first input\n\t */\n\tautoFocus?: boolean;\n\n\t/**\n\t * Field size\n\t */\n\tsize?: \"sm\" | \"md\" | \"lg\";\n\n\t/**\n\t * Field variant\n\t */\n\tvariant?: \"flat\" | \"bordered\" | \"underlined\" | \"faded\";\n\n\t/**\n\t * Custom className\n\t */\n\tclassName?: string;\n\n\t/**\n\t * Custom validation error\n\t */\n\terror?: string | string[];\n\n\t/**\n\t * Help text\n\t */\n\tdescription?: string;\n\n\t/**\n\t * Placeholder character\n\t */\n\tplaceholder?: string;\n\n\t/**\n\t * Input type (numeric vs alphanumeric)\n\t */\n\ttype?: \"numeric\" | \"alphanumeric\";\n\n\t/**\n\t * Whether to allow paste\n\t */\n\tallowPaste?: boolean;\n\n\t/**\n\t * Whether to show separator between groups\n\t */\n\tseparator?: boolean;\n\n\t/**\n\t * Group size for separator (e.g., 3 for 123-456)\n\t */\n\tgroupSize?: number;\n\n\t/**\n\t * Separator character\n\t */\n\tseparatorChar?: string;\n\n\t/**\n\t * Resend functionality\n\t */\n\tcanResend?: boolean;\n\n\t/**\n\t * Resend handler\n\t */\n\tonResend?: () => void;\n\n\t/**\n\t * Resend countdown (seconds)\n\t */\n\tresendCountdown?: number;\n\n\t/**\n\t * Loading state\n\t */\n\tisLoading?: boolean;\n\n\t/**\n\t * Success state\n\t */\n\tisSuccess?: boolean;\n\n\t/**\n\t * Input mode for mobile keyboards\n\t */\n\tinputMode?: \"numeric\" | \"text\";\n\n\t/**\n\t * Auto complete\n\t */\n\tautoComplete?: string;\n}\n\n// ============================================================================\n// Verification Code Component\n// ============================================================================\n\nexport function VerificationCode({\n\tname = \"verificationCode\",\n\tlabel = \"Verification Code\",\n\tlength = 6,\n\tvalue = \"\",\n\tonChange,\n\tonComplete,\n\tonBlur,\n\tonFocus,\n\trequired = false,\n\tdisabled = false,\n\tautoFocus = true,\n\tsize = \"md\",\n\tvariant = \"bordered\",\n\tclassName = \"\",\n\terror: externalError,\n\tdescription,\n\tplaceholder = \"\",\n\ttype = \"numeric\",\n\tallowPaste = true,\n\tseparator = false,\n\tgroupSize = 3,\n\tseparatorChar = \"-\",\n\tcanResend = false,\n\tonResend,\n\tresendCountdown = 0,\n\tisLoading = false,\n\tisSuccess = false,\n\tinputMode = \"numeric\",\n\tautoComplete = \"one-time-code\",\n}: VerificationCodeProps) {\n\tconst { components } = useConfig();\n\tconst formField = useFormField(name);\n\n\t// Custom component override\n\tconst CustomVerificationCode = components.VerificationCode;\n\tif (CustomVerificationCode) {\n\t\treturn (\n\t\t\t<CustomVerificationCode\n\t\t\t\t{...{\n\t\t\t\t\tname,\n\t\t\t\t\tlabel,\n\t\t\t\t\tlength,\n\t\t\t\t\tvalue,\n\t\t\t\t\tonChange,\n\t\t\t\t\tonComplete,\n\t\t\t\t\tonBlur,\n\t\t\t\t\tonFocus,\n\t\t\t\t\trequired,\n\t\t\t\t\tdisabled,\n\t\t\t\t\tautoFocus,\n\t\t\t\t\tsize,\n\t\t\t\t\tvariant,\n\t\t\t\t\tclassName,\n\t\t\t\t\terror: externalError,\n\t\t\t\t\tdescription,\n\t\t\t\t\tplaceholder,\n\t\t\t\t\ttype,\n\t\t\t\t\tallowPaste,\n\t\t\t\t\tseparator,\n\t\t\t\t\tgroupSize,\n\t\t\t\t\tseparatorChar,\n\t\t\t\t\tcanResend,\n\t\t\t\t\tonResend,\n\t\t\t\t\tresendCountdown,\n\t\t\t\t\tisLoading,\n\t\t\t\t\tisSuccess,\n\t\t\t\t\tinputMode,\n\t\t\t\t\tautoComplete,\n\t\t\t\t}}\n\t\t\t/>\n\t\t);\n\t}\n\n\t// State\n\tconst [internalValue, setInternalValue] = React.useState(value);\n\tconst [activeIndex, setActiveIndex] = React.useState(0);\n\tconst [isFocused, setIsFocused] = React.useState(false);\n\tconst inputRefs = React.useRef<(HTMLInputElement | null)[]>([]);\n\n\t// Use external value if controlled\n\tconst currentValue = onChange ? value : internalValue;\n\n\t// Split value into individual digits\n\tconst digits = React.useMemo(() => {\n\t\tconst valueArray = currentValue.split(\"\").slice(0, length);\n\t\treturn [\n\t\t\t...valueArray,\n\t\t\t...Array(Math.max(0, length - valueArray.length)).fill(\"\"),\n\t\t];\n\t}, [currentValue, length]);\n\n\t// Combined errors\n\tconst errors = React.useMemo(() => {\n\t\tconst allErrors: string[] = [];\n\n\t\tif (externalError) {\n\t\t\tif (Array.isArray(externalError)) {\n\t\t\t\tallErrors.push(...externalError);\n\t\t\t} else {\n\t\t\t\tallErrors.push(externalError);\n\t\t\t}\n\t\t}\n\n\t\tif (formField.error) {\n\t\t\tif (Array.isArray(formField.error)) {\n\t\t\t\tallErrors.push(...formField.error);\n\t\t\t} else {\n\t\t\t\tallErrors.push(formField.error);\n\t\t\t}\n\t\t}\n\n\t\treturn allErrors.length > 0 ? allErrors : null;\n\t}, [externalError, formField.error]);\n\n\t// Handle value change\n\tconst handleChange = React.useCallback(\n\t\t(newValue: string) => {\n\t\t\t// Validate input based on type\n\t\t\tconst validValue =\n\t\t\t\ttype === \"numeric\"\n\t\t\t\t\t? newValue.replace(/[^0-9]/g, \"\")\n\t\t\t\t\t: newValue.replace(/[^a-zA-Z0-9]/g, \"\").toUpperCase();\n\n\t\t\tif (onChange) {\n\t\t\t\tonChange(validValue);\n\t\t\t} else {\n\t\t\t\tsetInternalValue(validValue);\n\t\t\t}\n\n\t\t\t// Clear errors when user starts typing\n\t\t\tif (formField.clearError) {\n\t\t\t\tformField.clearError();\n\t\t\t}\n\n\t\t\t// Check if complete\n\t\t\tif (validValue.length === length && onComplete) {\n\t\t\t\tonComplete(validValue);\n\t\t\t}\n\t\t},\n\t\t[onChange, formField, length, onComplete, type],\n\t);\n\n\t// Handle single digit change\n\tconst handleDigitChange = React.useCallback(\n\t\t(index: number, digit: string) => {\n\t\t\tif (disabled) return;\n\n\t\t\tconst newDigits = [...digits];\n\t\t\tconst validDigit =\n\t\t\t\ttype === \"numeric\"\n\t\t\t\t\t? digit.replace(/[^0-9]/g, \"\")\n\t\t\t\t\t: digit.replace(/[^a-zA-Z0-9]/g, \"\").toUpperCase();\n\n\t\t\tif (validDigit.length > 1) {\n\t\t\t\t// Handle multiple characters (paste or rapid input)\n\t\t\t\tconst chars = validDigit.split(\"\");\n\t\t\t\tfor (let i = 0; i < chars.length && index + i < length; i++) {\n\t\t\t\t\tnewDigits[index + i] = chars[i];\n\t\t\t\t}\n\n\t\t\t\t// Focus next empty field or last field\n\t\t\t\tconst nextIndex = Math.min(index + chars.length, length - 1);\n\t\t\t\tsetActiveIndex(nextIndex);\n\t\t\t\tinputRefs.current[nextIndex]?.focus();\n\t\t\t} else {\n\t\t\t\tnewDigits[index] = validDigit;\n\n\t\t\t\t// Move to next field if digit entered and not last field\n\t\t\t\tif (validDigit && index < length - 1) {\n\t\t\t\t\tsetActiveIndex(index + 1);\n\t\t\t\t\tinputRefs.current[index + 1]?.focus();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst newValue = newDigits.join(\"\");\n\t\t\thandleChange(newValue);\n\t\t},\n\t\t[digits, disabled, length, type, handleChange],\n\t);\n\n\t// Handle key down\n\tconst handleKeyDown = React.useCallback(\n\t\t(index: number, event: React.KeyboardEvent) => {\n\t\t\tif (disabled) return;\n\n\t\t\tswitch (event.key) {\n\t\t\t\tcase \"Backspace\":\n\t\t\t\t\tif (!digits[index] && index > 0) {\n\t\t\t\t\t\t// Move to previous field if current is empty\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tsetActiveIndex(index - 1);\n\t\t\t\t\t\tinputRefs.current[index - 1]?.focus();\n\t\t\t\t\t} else if (digits[index]) {\n\t\t\t\t\t\t// Clear current field\n\t\t\t\t\t\tconst newDigits = [...digits];\n\t\t\t\t\t\tnewDigits[index] = \"\";\n\t\t\t\t\t\tconst newValue = newDigits.join(\"\");\n\t\t\t\t\t\thandleChange(newValue);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"Delete\":\n\t\t\t\t\t// Clear current field\n\t\t\t\t\tconst newDigits = [...digits];\n\t\t\t\t\tnewDigits[index] = \"\";\n\t\t\t\t\tconst newValue = newDigits.join(\"\");\n\t\t\t\t\thandleChange(newValue);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"ArrowLeft\":\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (index > 0) {\n\t\t\t\t\t\tsetActiveIndex(index - 1);\n\t\t\t\t\t\tinputRefs.current[index - 1]?.focus();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"ArrowRight\":\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (index < length - 1) {\n\t\t\t\t\t\tsetActiveIndex(index + 1);\n\t\t\t\t\t\tinputRefs.current[index + 1]?.focus();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"Home\":\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tsetActiveIndex(0);\n\t\t\t\t\tinputRefs.current[0]?.focus();\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"End\":\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tsetActiveIndex(length - 1);\n\t\t\t\t\tinputRefs.current[length - 1]?.focus();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t[digits, disabled, length, handleChange],\n\t);\n\n\t// Handle paste\n\tconst handlePaste = React.useCallback(\n\t\t(event: React.ClipboardEvent) => {\n\t\t\tif (!allowPaste || disabled) return;\n\n\t\t\tevent.preventDefault();\n\t\t\tconst pastedData = event.clipboardData.getData(\"text\");\n\t\t\tconst validData =\n\t\t\t\ttype === \"numeric\"\n\t\t\t\t\t? pastedData.replace(/[^0-9]/g, \"\")\n\t\t\t\t\t: pastedData.replace(/[^a-zA-Z0-9]/g, \"\").toUpperCase();\n\n\t\t\tif (validData) {\n\t\t\t\tconst truncatedData = validData.slice(0, length);\n\t\t\t\thandleChange(truncatedData);\n\n\t\t\t\t// Focus last filled field\n\t\t\t\tconst focusIndex = Math.min(truncatedData.length - 1, length - 1);\n\t\t\t\tsetActiveIndex(focusIndex);\n\t\t\t\tinputRefs.current[focusIndex]?.focus();\n\t\t\t}\n\t\t},\n\t\t[allowPaste, disabled, type, length, handleChange],\n\t);\n\n\t// Handle input focus\n\tconst handleInputFocus = React.useCallback(\n\t\t(index: number) => {\n\t\t\tsetActiveIndex(index);\n\t\t\tsetIsFocused(true);\n\t\t\tonFocus?.();\n\t\t},\n\t\t[onFocus],\n\t);\n\n\t// Handle input blur\n\tconst handleInputBlur = React.useCallback(() => {\n\t\tsetIsFocused(false);\n\t\tif (formField.setTouched) {\n\t\t\tformField.setTouched(true);\n\t\t}\n\t\tonBlur?.();\n\t}, [formField, onBlur]);\n\n\t// Auto focus first input on mount\n\tReact.useEffect(() => {\n\t\tif (autoFocus && !disabled) {\n\t\t\tinputRefs.current[0]?.focus();\n\t\t}\n\t}, [autoFocus, disabled]);\n\n\t// Size classes\n\tconst sizeClasses = {\n\t\tsm: \"w-8 h-8 text-sm\",\n\t\tmd: \"w-10 h-10 text-base\",\n\t\tlg: \"w-12 h-12 text-lg\",\n\t};\n\n\t// Render separator\n\tconst renderSeparator = (index: number) => {\n\t\tif (!separator || (index + 1) % groupSize !== 0 || index === length - 1) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn (\n\t\t\t<span className=\"text-default-400 text-xl font-mono mx-1\">\n\t\t\t\t{separatorChar}\n\t\t\t</span>\n\t\t);\n\t};\n\n\t// Resend button\n\tconst ResendButton = () => {\n\t\tif (!canResend || !onResend) return null;\n\n\t\tconst canClickResend = resendCountdown === 0 && !isLoading;\n\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center mt-4\">\n\t\t\t\t{canClickResend ? (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"light\"\n\t\t\t\t\t\tcolor=\"primary\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonPress={onResend}\n\t\t\t\t\t\tisDisabled={isLoading}\n\t\t\t\t\t>\n\t\t\t\t\t\tResend Code\n\t\t\t\t\t</Button>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"text-sm text-default-500\">\n\t\t\t\t\t\tResend code in {resendCountdown}s\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t};\n\n\treturn (\n\t\t<div className={`space-y-4 ${className}`}>\n\t\t\t{/* Label */}\n\t\t\t{label && (\n\t\t\t\t<div className=\"text-sm font-medium text-foreground\">\n\t\t\t\t\t{label}\n\t\t\t\t\t{required && <span className=\"text-danger ml-1\">*</span>}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Description */}\n\t\t\t{description && (\n\t\t\t\t<div className=\"text-sm text-default-500\">{description}</div>\n\t\t\t)}\n\n\t\t\t{/* Input Fields */}\n\t\t\t<div className=\"flex items-center justify-center gap-2 flex-wrap\">\n\t\t\t\t{digits.map((digit, index) => (\n\t\t\t\t\t<React.Fragment key={index}>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tref={(el: HTMLInputElement | null) => {\n\t\t\t\t\t\t\t\tinputRefs.current[index] = el;\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tvalue={digit}\n\t\t\t\t\t\t\tonChange={(e) => handleDigitChange(index, e.target.value)}\n\t\t\t\t\t\t\tonKeyDown={(e) => handleKeyDown(index, e)}\n\t\t\t\t\t\t\tonFocus={() => handleInputFocus(index)}\n\t\t\t\t\t\t\tonBlur={handleInputBlur}\n\t\t\t\t\t\t\tonPaste={handlePaste}\n\t\t\t\t\t\t\tmaxLength={1}\n\t\t\t\t\t\t\tclassName={`${sizeClasses[size]} text-center font-mono`}\n\t\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\t\tvariant={variant}\n\t\t\t\t\t\t\tisDisabled={disabled || isLoading}\n\t\t\t\t\t\t\tisInvalid={!!errors}\n\t\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\t\tinputMode={inputMode}\n\t\t\t\t\t\t\tautoComplete={index === 0 ? autoComplete : \"off\"}\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{renderSeparator(index)}\n\t\t\t\t\t</React.Fragment>\n\t\t\t\t))}\n\t\t\t</div>\n\n\t\t\t{/* Loading Progress */}\n\t\t\t<AnimatePresence>\n\t\t\t\t{isLoading && (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tinitial={{ opacity: 0, height: 0 }}\n\t\t\t\t\t\tanimate={{ opacity: 1, height: \"auto\" }}\n\t\t\t\t\t\texit={{ opacity: 0, height: 0 }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Progress\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tisIndeterminate\n\t\t\t\t\t\t\tcolor=\"primary\"\n\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t\tlabel=\"Verifying...\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</motion.div>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\n\t\t\t{/* Success State */}\n\t\t\t<AnimatePresence>\n\t\t\t\t{isSuccess && (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tinitial={{ opacity: 0, scale: 0.8 }}\n\t\t\t\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center gap-2 text-success-600\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tclassName=\"w-5 h-5\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\td=\"M5 13l4 4L19 7\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t<span className=\"text-sm font-medium\">Verified!</span>\n\t\t\t\t\t</motion.div>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\n\t\t\t{/* Field Errors */}\n\t\t\t{errors && <FieldError error={errors} fieldName={name} />}\n\n\t\t\t{/* Resend Button */}\n\t\t\t<ResendButton />\n\n\t\t\t{/* Progress Indicator */}\n\t\t\t<div className=\"flex justify-center\">\n\t\t\t\t<div className=\"flex gap-1\">\n\t\t\t\t\t{Array.from({ length }, (_, index) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\tclassName={`\n w-2 h-1 rounded-full transition-colors duration-200\n ${\n\t\t\t\t\t\t\t\t\tindex < currentValue.length\n\t\t\t\t\t\t\t\t\t\t? \"bg-primary\"\n\t\t\t\t\t\t\t\t\t\t: \"bg-default-200 dark:bg-default-800\"\n\t\t\t\t\t\t\t\t}\n `}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport default VerificationCode;\n"],"names":["VerificationCode","name","label","length","value","onChange","onComplete","onBlur","onFocus","required","disabled","autoFocus","size","variant","className","externalError","description","placeholder","type","allowPaste","separator","groupSize","separatorChar","canResend","onResend","resendCountdown","isLoading","isSuccess","inputMode","autoComplete","components","useConfig","formField","useFormField","CustomVerificationCode","jsx","internalValue","setInternalValue","React","activeIndex","setActiveIndex","isFocused","setIsFocused","inputRefs","currentValue","digits","valueArray","errors","allErrors","handleChange","newValue","validValue","handleDigitChange","index","digit","newDigits","validDigit","chars","i","nextIndex","handleKeyDown","event","handlePaste","pastedData","validData","truncatedData","focusIndex","handleInputFocus","handleInputBlur","sizeClasses","renderSeparator","ResendButton","canClickResend","Button","jsxs","Input","el","e","AnimatePresence","motion","Progress","FieldError","_"],"mappings":"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"}