import { createContext, useCallback, useContext, useMemo, useRef, useState, } from 'react'; import type { TextInput } from 'react-native'; import type { TextInputOTPContextProps, TextInputOTPProviderProps, } from '../types'; const TextInputOTPContext = createContext({ code: '', currentIndex: 0, inputRef: { current: null }, handleChangeText: () => {}, handlePress: () => {}, setValue: () => {}, focus: () => {}, blur: () => {}, clear: () => {}, caretHidden: false, }); export function TextInputOTPProvider({ autoFocus = true, maxLength, value = '', onFilled, onChangeText, caretHidden = false, caretColor, children, }: TextInputOTPProviderProps) { const [code, setCode] = useState(value); const [currentIndex, setCurrentIndex] = useState(() => (autoFocus ? 0 : -1)); const inputRef = useRef(null); const handleChangeText = useCallback( (text: string) => { if (text.length > maxLength) { return; } setCode(text); onChangeText?.(text); if (text.length === maxLength) { onFilled?.(text); setCurrentIndex(text.length - 1); return; } if (text.length < maxLength) { setCurrentIndex(text.length); } }, [maxLength, onChangeText, onFilled] ); const handlePress = useCallback(() => { setCurrentIndex(code.length); inputRef.current?.focus(); }, [code.length]); const setValue = useCallback( (text: string) => { const filledCode = text.length > maxLength ? text.slice(0, maxLength) : text; setCode(filledCode); setCurrentIndex(maxLength - 1); onFilled?.(filledCode); }, [maxLength] ); function focus() { inputRef.current?.focus(); } function blur() { inputRef.current?.blur(); } const clear = useCallback(() => { setCode(''); setCurrentIndex(0); }, []); const contextValue = useMemo( () => ({ code: value || code, currentIndex, inputRef, handleChangeText, handlePress, setValue, focus, blur, clear, caretHidden, caretColor, }), [ clear, code, currentIndex, handleChangeText, handlePress, setValue, value, caretHidden, caretColor, ] ); return ( {children} ); } export function useTextInputOTP() { return useContext(TextInputOTPContext); }