// components/ui/input-otp.tsx import * as React from 'react'; import { View, TextInput, Pressable } from 'react-native'; import { Text } from './text'; import { cn } from '@/lib/utils'; export interface InputOTPProps { value?: string; defaultValue?: string; maxLength?: number; onValueChange?: (value: string) => void; disabled?: boolean; className?: string; containerClassName?: string; inputType?: 'numeric' | 'default' | 'password'; } export function InputOTP({ value: controlledValue, defaultValue = '', maxLength = 4, onValueChange, disabled = false, className, containerClassName, inputType = 'numeric', }: InputOTPProps) { const [internalValue, setInternalValue] = React.useState(defaultValue); const currentValue = controlledValue ?? internalValue; const inputRef = React.useRef(null); const handleChange = (text: string) => { const cleaned = text.replace(/[^0-9a-zA-Z]/g, '').slice(0, maxLength); if (controlledValue === undefined) { setInternalValue(cleaned); } onValueChange?.(cleaned); }; const handlePress = () => { inputRef.current?.focus(); }; const cells = Array.from({ length: maxLength }, (_, i) => { const char = currentValue[i] || ''; const isFocused = currentValue.length === i; const isFilled = char.length > 0; return ( {inputType === 'password' && isFilled ? '●' : char} {isFocused && !disabled && ( )} ); }); return ( {cells} ); } // Subtle cursor animation import Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming } from 'react-native-reanimated'; function AnimatedCursor() { const opacity = useSharedValue(1); React.useEffect(() => { opacity.value = withRepeat(withTiming(0, { duration: 500 }), -1, true); }, []); const style = useAnimatedStyle(() => ({ opacity: opacity.value, })); return ; }