diff --git a/README.md b/README.md
index 8aa7a40..bd1ca9d 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ npm install react-native-input-code-otp
## Usage
-```ts
+```tsx
import {
TextInputOTP,
TextInputOTPSlot,
@@ -35,7 +35,7 @@ export function MyComponent() {
- )
+ );
}
```
@@ -46,10 +46,14 @@ export function MyComponent() {
| `maxLength` | number - Required | The max number of digits to OTP code. |
| `onFilled` | (code: string) => void - Optional | The callback function is executed when the OTP input has been entirely completed, and it receives the OTP code as a parameter. |
+---
+
| TextInputOTPGroup | Type | Description |
| ----------------- | -------------------- | ----------------------------- |
| `groupStyles` | ViewStyle - Optional | Custom styles for the `View`. |
+---
+
| TextInputOTPSlot | Type | Description |
| ----------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------- |
| `index` | number - Required | The position of the slot within the OTP input sequence. Each slot must have a unique index starting from 0. |
@@ -58,6 +62,8 @@ export function MyComponent() {
| `slotTextStyles` | TextStyle - Optional | Custom styles for the `Text`. |
| `focusedSlotTextStyles` | TextStyle - Optional | Custom styles applied to the slot `Text` when it is focused. |
+---
+
| TextInputOTPSeparator | Type | Description |
| --------------------- | -------------------- | ----------------------------- |
| `separatorStyles` | ViewStyle - Optional | Custom styles for the `View`. |
@@ -66,12 +72,116 @@ export function MyComponent() {
The `TextInputOTP` component exposes these functions with `ref`:
-| Prop | Type | Description |
-| ---------- | ------------------------ | -------------------------------------------------------------------------- |
-| `clear` | () => void; | Resets the OTP input by clearing all entered values. |
-| `focus` | () => void; | Activates the OTP input field, allowing the user to type. |
-| `blue` | () => void; | Deactivates the OTP input field, removing focus. |
-| `setValue` | (value: string) => void; | Sets a custom value to the OTP input fields, overriding any current input. |
+| Prop | Type | Description |
+| ---------- | ----------------------- | -------------------------------------------------------------------------- |
+| `clear` | () => void | Resets the OTP input by clearing all entered values. |
+| `focus` | () => void | Activates the OTP input field, allowing the user to type. |
+| `blue` | () => void | Deactivates the OTP input field, removing focus. |
+| `setValue` | (value: string) => void | Sets a custom value to the OTP input fields, overriding any current input. |
+
+## Examples
+
+
+Usage with react-hook-form
+
+```tsx
+import { Button, View } from 'react-native';
+import {
+ TextInputOTP,
+ TextInputOTPSlot,
+ TextInputOTPGroup,
+ TextInputOTPSeparator,
+} from 'react-native-input-code-otp';
+import { Controller, useForm } from 'react-hook-form';
+
+type FormValues = {
+ code: string;
+};
+
+export function MyComponent() {
+ const { control, handleSubmit } = useForm({
+ defaultValues: {
+ code: '',
+ },
+ });
+
+ function onSubmit({ code }: FormValues) {
+ console.log({ code });
+ }
+
+ return (
+
+ (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+ />
+
+
+
+ );
+}
+```
+
+
+
+
+Usage of ref to programmatically set OTP value
+
+```tsx
+import { useRef } from 'react';
+import { Button, View } from 'react-native';
+import {
+ TextInputOTP,
+ TextInputOTPSlot,
+ TextInputOTPGroup,
+ TextInputOTPSeparator,
+} from 'react-native-input-code-otp';
+
+export function MyComponent() {
+ const inputRef = useRef(null);
+
+ function onSomeAction() {
+ inputRef.current?.setValue('123456');
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+
## Contributing
diff --git a/example/src/App.tsx b/example/src/App.tsx
index fd7fd8e..5732a01 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,25 +1,15 @@
-import { useRef } from 'react';
-import { StyleSheet, View, useColorScheme } from 'react-native';
+import { StyleSheet, View } from 'react-native';
import {
TextInputOTP,
TextInputOTPSlot,
TextInputOTPGroup,
TextInputOTPSeparator,
- type TextInputOTPRef,
} from 'react-native-input-code-otp';
export default function App() {
- const colorSchema = useColorScheme();
- const inputRef = useRef(null);
- const backgroundColor = colorSchema === 'light' ? 'white' : 'black';
-
- function handleSubmit(code: string) {
- console.log({ code });
- }
-
return (
-
-
+
+ console.log(code)}>
diff --git a/src/components/text-input-otp-slot.tsx b/src/components/text-input-otp-slot.tsx
index 5059030..9722bda 100644
--- a/src/components/text-input-otp-slot.tsx
+++ b/src/components/text-input-otp-slot.tsx
@@ -25,12 +25,12 @@ function TextInputOTPSlotComponent({
return (
handlePress(index)}
{...rest}
>
{code[index] && (
diff --git a/src/components/text-input.tsx b/src/components/text-input.tsx
index dde36b9..ee11032 100644
--- a/src/components/text-input.tsx
+++ b/src/components/text-input.tsx
@@ -7,15 +7,8 @@ export const TextInput = forwardRef<
TextInputOTPRef,
Omit
>(({ autoFocus = true, ...rest }, ref) => {
- const {
- inputRef,
- handleKeyPress,
- handleChangeText,
- setValue,
- focus,
- blur,
- clear,
- } = useTextInputOTP();
+ const { inputRef, handleChangeText, setValue, focus, blur, clear } =
+ useTextInputOTP();
useImperativeHandle(ref, () => ({
setValue,
@@ -26,14 +19,12 @@ export const TextInput = forwardRef<
return (
);
});
diff --git a/src/hooks/use-slot-border-styles.ts b/src/hooks/use-slot-border-styles.ts
index ae880d3..711228a 100644
--- a/src/hooks/use-slot-border-styles.ts
+++ b/src/hooks/use-slot-border-styles.ts
@@ -1,4 +1,4 @@
-import type { UseSlotBorderStylesProps } from '../types/text-input-otp-slot';
+import type { UseSlotBorderStylesProps } from '../types';
export function useSlotBorderStyles({
isFocused,
diff --git a/src/hooks/use-text-input-otp.tsx b/src/hooks/use-text-input-otp.tsx
index f0ed698..e03d172 100644
--- a/src/hooks/use-text-input-otp.tsx
+++ b/src/hooks/use-text-input-otp.tsx
@@ -7,19 +7,13 @@ import {
useState,
type PropsWithChildren,
} from 'react';
-import type {
- NativeSyntheticEvent,
- TextInput,
- TextInputKeyPressEventData,
-} from 'react-native';
-import { BACKSPACE_KEY } from '../constants';
+import type { TextInput } from 'react-native';
import type { TextInputOTPContextProps, TextInputOTPProps } from '../types';
const TextInputOTPContext = createContext({
- code: [],
+ code: '',
currentIndex: 0,
inputRef: { current: null },
- handleKeyPress: () => {},
handleChangeText: () => {},
handlePress: () => {},
setValue: () => {},
@@ -31,76 +25,50 @@ const TextInputOTPContext = createContext({
export function TextInputOTPProvider({
autoFocus = true,
maxLength,
+ value = '',
onFilled,
+ onChangeText,
children,
}: PropsWithChildren<
- Pick
+ Pick<
+ TextInputOTPProps,
+ 'autoFocus' | 'maxLength' | 'onFilled' | 'onChangeText' | 'value'
+ >
>) {
- const [code, setCode] = useState(Array(maxLength).fill(''));
- const [currentIndex, setCurrentIndex] = useState(autoFocus ? 0 : -1);
+ const [code, setCode] = useState(value);
+ const [currentIndex, setCurrentIndex] = useState(() => (autoFocus ? 0 : -1));
const inputRef = useRef(null);
- const updateCodeAtIndex = useCallback(
- (index: number, value: string) => {
- const newCode = [...code];
- newCode[index] = value;
- setCode(newCode);
-
- return newCode;
- },
- [code]
- );
-
- const handleKeyPress = useCallback(
- (event: NativeSyntheticEvent) => {
- const { key } = event.nativeEvent;
-
- if (key !== BACKSPACE_KEY) {
- return;
- }
-
- if (!code[currentIndex] && currentIndex > 0) {
- updateCodeAtIndex(currentIndex - 1, '');
- setCurrentIndex((prev) => prev - 1);
- return;
- }
-
- updateCodeAtIndex(currentIndex, '');
- },
- [code, currentIndex, updateCodeAtIndex]
- );
-
const handleChangeText = useCallback(
(text: string) => {
- if (text.length > 1) {
+ if (text.length > maxLength) {
return;
}
- const updatedCode = updateCodeAtIndex(currentIndex, text);
+ setCode(text);
+ onChangeText?.(text);
- if (currentIndex < maxLength - 1) {
- setCurrentIndex((prev) => prev + 1);
- return;
+ if (text.length === maxLength) {
+ onFilled?.(text);
}
- const finalCode = [...updatedCode].join('');
-
- if (finalCode.length === maxLength) {
- onFilled?.(finalCode);
+ if (text.length < maxLength) {
+ setCurrentIndex(text.length);
}
},
- [currentIndex, maxLength, onFilled, updateCodeAtIndex]
+ [maxLength, onChangeText, onFilled]
);
- function handlePress(index: number) {
- setCurrentIndex(index);
+ const handlePress = useCallback(() => {
+ setCurrentIndex(code.length);
inputRef.current?.focus();
- }
+ }, [code.length]);
const setValue = useCallback(
(text: string) => {
- const value = text.length > maxLength ? text.slice(0, maxLength) : text;
- setCode(Array.from(value));
+ const filledCode =
+ text.length > maxLength ? text.slice(0, maxLength) : text;
+ setCode(filledCode);
setCurrentIndex(maxLength - 1);
},
[maxLength]
@@ -115,16 +83,15 @@ export function TextInputOTPProvider({
}
const clear = useCallback(() => {
- setCode(Array(maxLength).fill(''));
+ setCode('');
setCurrentIndex(0);
- }, [maxLength]);
+ }, []);
const contextValue = useMemo(
() => ({
- code,
+ code: value || code,
currentIndex,
inputRef,
- handleKeyPress,
handleChangeText,
handlePress,
setValue,
@@ -132,7 +99,7 @@ export function TextInputOTPProvider({
blur,
clear,
}),
- [clear, code, currentIndex, handleChangeText, handleKeyPress, setValue]
+ [clear, code, currentIndex, handleChangeText, handlePress, setValue, value]
);
return (
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..0bd8351
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,64 @@
+import type { ReactNode, RefObject } from 'react';
+import type {
+ PressableProps,
+ StyleProp,
+ TextInput,
+ TextInputProps,
+ TextStyle,
+ ViewProps,
+ ViewStyle,
+} from 'react-native';
+
+export type TextInputOTPContextProps = {
+ code: string;
+ currentIndex: number;
+ inputRef: RefObject;
+ handleChangeText: (text: string) => void;
+ handlePress: () => void;
+ setValue: (text: string) => void;
+ focus: () => void;
+ blur: () => void;
+ clear: () => void;
+};
+
+export type TextInputOTPGroupProps = {
+ groupStyles?: StyleProp;
+} & Omit;
+
+export type TextInputOTPRef = {
+ setValue: (text: string) => void;
+ focus: () => void;
+ blur: () => void;
+ clear: () => void;
+};
+
+export type TextInputOTPSeparatorProps = {
+ separatorStyles?: StyleProp;
+} & Omit;
+
+export type TextInputOTPSlotProps = {
+ index: number;
+ focusedSlotStyles?: StyleProp;
+ slotStyles?: StyleProp;
+ focusedSlotTextStyles?: StyleProp;
+ slotTextStyles?: StyleProp;
+} & PressableProps;
+
+export type TextInputOTPSlotInternalProps = {
+ isFirst?: boolean;
+ isLast?: boolean;
+};
+
+export type UseSlotBorderStylesProps = {
+ isFocused: boolean;
+ isFirst?: boolean;
+ isLast?: boolean;
+};
+
+export type TextInputOTPProps = {
+ children: ReactNode;
+ autoFocus?: boolean;
+ maxLength: number;
+ onFilled?: (text: string) => void;
+ containerStyles?: StyleProp;
+} & Omit;
diff --git a/src/types/index.ts b/src/types/index.ts
deleted file mode 100644
index 3a7b32a..0000000
--- a/src/types/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export * from './text-input-otp';
-export * from './text-input-otp-context';
-export * from './text-input-otp-group';
-export * from './text-input-otp-ref';
-export * from './text-input-otp-separator';
-export * from './text-input-otp-slot';
diff --git a/src/types/text-input-otp-context.ts b/src/types/text-input-otp-context.ts
deleted file mode 100644
index f3c35f4..0000000
--- a/src/types/text-input-otp-context.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import type { RefObject } from 'react';
-import type {
- NativeSyntheticEvent,
- TextInput,
- TextInputKeyPressEventData,
-} from 'react-native';
-
-export type TextInputOTPContextProps = {
- code: string[];
- currentIndex: number;
- inputRef: RefObject;
- handleKeyPress: (
- event: NativeSyntheticEvent
- ) => void;
- handleChangeText: (text: string) => void;
- handlePress: (index: number) => void;
- setValue: (text: string) => void;
- focus: () => void;
- blur: () => void;
- clear: () => void;
-};
diff --git a/src/types/text-input-otp-group.ts b/src/types/text-input-otp-group.ts
deleted file mode 100644
index bee8046..0000000
--- a/src/types/text-input-otp-group.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
-
-export type TextInputOTPGroupProps = {
- groupStyles?: StyleProp;
-} & Omit;
diff --git a/src/types/text-input-otp-ref.ts b/src/types/text-input-otp-ref.ts
deleted file mode 100644
index 54250d4..0000000
--- a/src/types/text-input-otp-ref.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export type TextInputOTPRef = {
- setValue: (text: string) => void;
- focus: () => void;
- blur: () => void;
- clear: () => void;
-};
diff --git a/src/types/text-input-otp-separator.ts b/src/types/text-input-otp-separator.ts
deleted file mode 100644
index f1b3a54..0000000
--- a/src/types/text-input-otp-separator.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
-
-export type TextInputOTPSeparatorProps = {
- separatorStyles?: StyleProp;
-} & Omit;
diff --git a/src/types/text-input-otp-slot.ts b/src/types/text-input-otp-slot.ts
deleted file mode 100644
index df39ffc..0000000
--- a/src/types/text-input-otp-slot.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import type {
- PressableProps,
- StyleProp,
- TextStyle,
- ViewStyle,
-} from 'react-native';
-
-export type TextInputOTPSlotProps = {
- index: number;
- focusedSlotStyles?: StyleProp;
- slotStyles?: StyleProp;
- focusedSlotTextStyles?: StyleProp;
- slotTextStyles?: StyleProp;
-} & PressableProps;
-
-export type TextInputOTPSlotInternalProps = {
- isFirst?: boolean;
- isLast?: boolean;
-};
-
-export type UseSlotBorderStylesProps = {
- isFocused: boolean;
- isFirst?: boolean;
- isLast?: boolean;
-};
diff --git a/src/types/text-input-otp.ts b/src/types/text-input-otp.ts
deleted file mode 100644
index eebd0d8..0000000
--- a/src/types/text-input-otp.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { ReactNode } from 'react';
-import type { StyleProp, TextInputProps, ViewStyle } from 'react-native';
-
-export type TextInputOTPProps = {
- children: ReactNode;
- autoFocus?: boolean;
- maxLength: number;
- onFilled?: (text: string) => void;
- containerStyles?: StyleProp;
-} & Omit;