Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"react-dom": "19.0.0",
"react-native": "0.79.2",
"react-native-reanimated": "~3.17.4",
"react-native-safe-area-context": "5.4.0",
"react-native-web": "^0.20.0",
"tailwind-merge": "^2.6.0",
"tailwindcss": "^3.4.17"
Expand Down
270 changes: 135 additions & 135 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import {
View,
ScrollView,
SafeAreaView,
Text,
TouchableOpacity,
} from 'react-native';
import { useState } from 'react';
import { FlatList, Pressable, View, Text } from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { useCallback, useState } from 'react';
import StripeOTPInput from './examples/stripe';
import AppleOTPInput from './examples/apple';
import RevoltOTPInput from './examples/revolt';
Expand All @@ -20,144 +15,149 @@ import FocusSlotExample from './examples/focus-slot';

type TabType = 'regular' | 'nativewind';

const EXAMPLES = [
{
title: 'Stripe',
description: 'Clean, minimal design with subtle borders',
regular: StripeOTPInput,
nativewind: StripeNativewind,
color: '#6772E5',
},
{
title: 'Animated Stripe',
description: 'Stripe style with slide-in animation',
regular: AnimatedStripeOTPInput,
nativewind: AnimatedStripeOTPInput,
color: '#8B5CF6',
},
{
title: 'Apple',
description: 'iOS-style with rounded corners and shadows',
regular: AppleOTPInput,
nativewind: AppleNativewind,
color: '#007AFF',
},
{
title: 'Revolt',
description: 'Modern design with gradient backgrounds',
regular: RevoltOTPInput,
nativewind: RevoltNativewind,
color: '#FF6B6B',
},
{
title: 'Dashed',
description: 'Simple dashed border style',
regular: DashedOTPInput,
nativewind: DashedNativewind,
color: '#4ECDC4',
},
{
title: 'Animated Dashed',
description: 'Dashed style with slide-in animation',
regular: AnimatedDashedNativewind,
nativewind: AnimatedDashedNativewind,
color: '#8B5CF6',
},
{
title: 'Focus Slot',
description: 'Tap any slot to focus it using slot.focus()',
regular: FocusSlotExample,
nativewind: FocusSlotExample,
color: '#10B981',
},
];

type Example = (typeof EXAMPLES)[number];

export default function App() {
const [activeTab, setActiveTab] = useState<TabType>('regular');

const examples = [
{
title: 'Stripe',
description: 'Clean, minimal design with subtle borders',
regular: StripeOTPInput,
nativewind: StripeNativewind,
color: '#6772E5',
},
{
title: 'Animated Stripe',
description: 'Stripe style with slide-in animation',
regular: AnimatedStripeOTPInput,
nativewind: AnimatedStripeOTPInput,
color: '#8B5CF6',
},
{
title: 'Apple',
description: 'iOS-style with rounded corners and shadows',
regular: AppleOTPInput,
nativewind: AppleNativewind,
color: '#007AFF',
},
{
title: 'Revolt',
description: 'Modern design with gradient backgrounds',
regular: RevoltOTPInput,
nativewind: RevoltNativewind,
color: '#FF6B6B',
},
{
title: 'Dashed',
description: 'Simple dashed border style',
regular: DashedOTPInput,
nativewind: DashedNativewind,
color: '#4ECDC4',
},
{
title: 'Animated Dashed',
description: 'Dashed style with slide-in animation',
regular: AnimatedDashedNativewind,
nativewind: AnimatedDashedNativewind,
color: '#8B5CF6',
},
{
title: 'Focus Slot',
description: 'Tap any slot to focus it using slot.focus()',
regular: FocusSlotExample,
nativewind: FocusSlotExample,
color: '#10B981',
},
];
const renderExample = useCallback(
({ item: example }: { item: Example }) => (
<View className="mx-5 mt-5 bg-white rounded-2xl shadow-sm">
<View className="flex-row items-center px-4 py-2 border-b border-slate-200">
<View className="flex-1">
<Text className="text-lg font-semibold text-slate-800 mb-1">
{example.title}
</Text>
<Text className="text-sm text-slate-500 leading-5">
{example.description}
</Text>
</View>
</View>

return (
<SafeAreaView className="flex-1 bg-white">
<View className="px-5 pt-5 pb-4 bg-white">
<Text className="text-3xl font-bold text-slate-800 mb-1">
OTP Input Examples
</Text>
<Text className="text-base text-slate-500 leading-6">
Beautiful, customizable one-time password inputs
</Text>
<View className="bg-slate-50 rounded-b-2xl p-4 items-center h-[120px] justify-center">
{activeTab === 'regular' ? (
<example.regular />
) : (
<example.nativewind />
)}
</View>
</View>
),
[activeTab]
);

<View className="flex-row bg-white px-5 pb-4 border-b border-slate-200">
<TouchableOpacity
className={`flex-1 py-3 px-4 mx-1 rounded-lg ${
activeTab === 'regular' ? 'bg-blue-500' : 'bg-slate-100'
}`}
onPress={() => setActiveTab('regular')}
>
<Text
className={`text-center text-sm font-semibold ${
activeTab === 'regular' ? 'text-white' : 'text-slate-500'
}`}
>
StyleSheet
return (
<SafeAreaProvider>
<SafeAreaView className="flex-1 bg-white">
<View className="px-5 pt-5 pb-4 bg-white">
<Text className="text-3xl font-bold text-slate-800 mb-1">
OTP Input Examples
</Text>
</TouchableOpacity>
<TouchableOpacity
className={`flex-1 py-3 px-4 mx-1 rounded-lg ${
activeTab === 'nativewind' ? 'bg-blue-500' : 'bg-slate-100'
}`}
onPress={() => setActiveTab('nativewind')}
>
<Text
className={`text-center text-sm font-semibold ${
activeTab === 'nativewind' ? 'text-white' : 'text-slate-500'
}`}
>
NativeWind
<Text className="text-base text-slate-500 leading-6">
Beautiful, customizable one-time password inputs
</Text>
</TouchableOpacity>
</View>
</View>

<ScrollView className="flex-1" showsVerticalScrollIndicator={false}>
{examples.map((example) => (
<View
key={example.title}
className="mx-5 mt-5 bg-white rounded-2xl shadow-sm"
<View className="flex-row bg-white px-5 pb-4 border-b border-slate-200">
<Pressable
className={`flex-1 py-3 px-4 mx-1 rounded-lg ${
activeTab === 'regular' ? 'bg-blue-500' : 'bg-slate-100'
}`}
onPress={() => setActiveTab('regular')}
style={({ pressed }) => ({ opacity: pressed ? 0.2 : 1 })}
>
<View className="flex-row items-center px-4 py-2 border-b border-slate-200">
{/* <View
className="w-10 h-10 rounded-full items-center justify-center mr-3"
style={{ backgroundColor: example.color }}
>
<Text className="text-white text-lg font-bold">
{example.title[0]}
</Text>
</View> */}
<View className="flex-1">
<Text className="text-lg font-semibold text-slate-800 mb-1">
{example.title}
</Text>
<Text className="text-sm text-slate-500 leading-5">
{example.description}
</Text>
</View>
</View>
<Text
className={`text-center text-sm font-semibold ${
activeTab === 'regular' ? 'text-white' : 'text-slate-500'
}`}
>
StyleSheet
</Text>
</Pressable>
<Pressable
className={`flex-1 py-3 px-4 mx-1 rounded-lg ${
activeTab === 'nativewind' ? 'bg-blue-500' : 'bg-slate-100'
}`}
onPress={() => setActiveTab('nativewind')}
style={({ pressed }) => ({ opacity: pressed ? 0.2 : 1 })}
>
<Text
className={`text-center text-sm font-semibold ${
activeTab === 'nativewind' ? 'text-white' : 'text-slate-500'
}`}
>
NativeWind
</Text>
</Pressable>
</View>

<View className="bg-slate-50 rounded-b-2xl p-4 items-center h-[120px] justify-center">
{activeTab === 'regular' ? (
<example.regular />
) : (
<example.nativewind />
)}
<FlatList
className="flex-1"
data={EXAMPLES}
keyExtractor={(example) => example.title}
renderItem={renderExample}
showsVerticalScrollIndicator={false}
ListFooterComponent={
<View className="py-10 items-center">
<Text className="text-sm text-slate-400 text-center">
Built with ❤️ using input-otp-native
</Text>
</View>
</View>
))}

<View className="py-10 items-center">
<Text className="text-sm text-slate-400 text-center">
Built with ❤️ using input-otp-native
</Text>
</View>
</ScrollView>
</SafeAreaView>
}
/>
</SafeAreaView>
</SafeAreaProvider>
);
}
29 changes: 14 additions & 15 deletions example/src/examples/animated-dashed-nativewind.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import Animated, {
withRepeat,
withTiming,
withSequence,
useSharedValue,
useDerivedValue,
FadeInDown,
} from 'react-native-reanimated';
import { useEffect } from 'react';
import { cn } from './utils';

export default function AnimatedDashedOTPInput() {
Expand All @@ -29,8 +28,8 @@ export default function AnimatedDashedOTPInput() {
maxLength={5}
render={({ slots }) => (
<View className="flex-row items-center justify-center my-4">
{slots.map((slot, idx) => (
<Slot key={idx} {...slot} />
{slots.map((slot, slotIndex) => (
<Slot key={`slot-${slotIndex}`} {...slot} />
))}
</View>
)}
Expand Down Expand Up @@ -67,18 +66,18 @@ function Slot({ char, isActive, hasFakeCaret, focus }: SlotProps) {
}

function FakeCaret() {
const opacity = useSharedValue(1);

useEffect(() => {
opacity.value = withRepeat(
withSequence(
withTiming(0, { duration: 500 }),
withTiming(1, { duration: 500 })
const opacity = useDerivedValue(
() =>
withRepeat(
withSequence(
withTiming(0, { duration: 500 }),
withTiming(1, { duration: 500 })
),
-1,
true
),
-1,
true
);
}, [opacity]);
[]
);

const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
Expand Down
Loading
Loading