-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
290 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { View, Text } from 'react-native'; | ||
import { OTPInput, type SlotProps } from 'input-otp-native'; | ||
import type { OTPInputRef } from 'input-otp-native'; | ||
import { useRef } from 'react'; | ||
|
||
import Animated, { | ||
useAnimatedStyle, | ||
withRepeat, | ||
withTiming, | ||
withSequence, | ||
useSharedValue, | ||
} from 'react-native-reanimated'; | ||
import { useEffect } from 'react'; | ||
import { cn } from '../lib/utils'; | ||
|
||
export default function AppleOTPInput() { | ||
const ref = useRef<OTPInputRef>(null); | ||
|
||
return ( | ||
<View> | ||
<OTPInput | ||
ref={ref} | ||
maxLength={5} | ||
render={({ slots }) => ( | ||
<View className="flex-row gap-2 items-center justify-center my-4"> | ||
{slots.map((slot, idx) => ( | ||
<Slot key={idx} {...slot} /> | ||
))} | ||
</View> | ||
)} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function Slot({ char, isActive, hasFakeCaret }: SlotProps) { | ||
return ( | ||
<View | ||
className={cn( | ||
'w-[50px] h-[50px] items-center justify-center border border-gray-200 rounded-lg bg-white', | ||
{ | ||
'border-black border-2': isActive, | ||
} | ||
)} | ||
> | ||
{char !== null && ( | ||
<Text className="text-2xl font-medium text-gray-900">{char}</Text> | ||
)} | ||
{hasFakeCaret && <FakeCaret />} | ||
</View> | ||
); | ||
} | ||
|
||
function FakeCaret() { | ||
const opacity = useSharedValue(1); | ||
|
||
useEffect(() => { | ||
opacity.value = withRepeat( | ||
withSequence( | ||
withTiming(0, { duration: 500 }), | ||
withTiming(1, { duration: 500 }) | ||
), | ||
-1, | ||
true | ||
); | ||
}, [opacity]); | ||
|
||
const animatedStyle = useAnimatedStyle(() => ({ | ||
opacity: opacity.value, | ||
})); | ||
|
||
const baseStyle = { | ||
width: 2, | ||
height: 28, | ||
backgroundColor: '#000', | ||
borderRadius: 1, | ||
}; | ||
|
||
return ( | ||
<View className="absolute w-full h-full items-center justify-center"> | ||
<Animated.View style={[baseStyle, animatedStyle]} /> | ||
</View> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { View, Text } from 'react-native'; | ||
import { OTPInput, type SlotProps } from 'input-otp-native'; | ||
import type { OTPInputRef } from 'input-otp-native'; | ||
import { useRef } from 'react'; | ||
|
||
import Animated, { | ||
useAnimatedStyle, | ||
withRepeat, | ||
withTiming, | ||
withSequence, | ||
useSharedValue, | ||
} from 'react-native-reanimated'; | ||
import { useEffect } from 'react'; | ||
import { cn } from '../lib/utils'; | ||
|
||
export default function DashedOTPInput() { | ||
const ref = useRef<OTPInputRef>(null); | ||
|
||
return ( | ||
<View> | ||
<OTPInput | ||
ref={ref} | ||
maxLength={5} | ||
render={({ slots }) => ( | ||
<View className="flex-row items-center justify-center my-4"> | ||
{slots.map((slot, idx) => ( | ||
<Slot key={idx} {...slot} /> | ||
))} | ||
</View> | ||
)} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function Slot({ char, isActive, hasFakeCaret }: SlotProps) { | ||
return ( | ||
<View className="w-12 h-12 mx-2 items-center justify-center"> | ||
{char !== null && ( | ||
<Text className="text-3xl font-medium text-gray-900">{char}</Text> | ||
)} | ||
{hasFakeCaret && <FakeCaret />} | ||
<View | ||
className={cn('absolute bottom-0 w-full h-[1px] bg-gray-200', { | ||
'bg-gray-900 h-0.5': isActive, | ||
})} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function FakeCaret() { | ||
const opacity = useSharedValue(1); | ||
|
||
useEffect(() => { | ||
opacity.value = withRepeat( | ||
withSequence( | ||
withTiming(0, { duration: 500 }), | ||
withTiming(1, { duration: 500 }) | ||
), | ||
-1, | ||
true | ||
); | ||
}, [opacity]); | ||
|
||
const animatedStyle = useAnimatedStyle(() => ({ | ||
opacity: opacity.value, | ||
})); | ||
|
||
const baseStyle = { | ||
width: 2, | ||
height: 24, | ||
backgroundColor: '#111827', | ||
borderRadius: 1, | ||
}; | ||
|
||
return ( | ||
<View className="absolute w-full h-full items-center justify-center"> | ||
<Animated.View style={[baseStyle, animatedStyle]} /> | ||
</View> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { View, Text } from 'react-native'; | ||
import { OTPInput, type SlotProps } from 'input-otp-native'; | ||
import type { OTPInputRef } from 'input-otp-native'; | ||
import React, { useRef } from 'react'; | ||
|
||
import Animated, { | ||
useAnimatedStyle, | ||
withRepeat, | ||
withTiming, | ||
withSequence, | ||
useSharedValue, | ||
} from 'react-native-reanimated'; | ||
import { useEffect } from 'react'; | ||
import { cn } from '../lib/utils'; | ||
|
||
export default function RevoltOTPInput() { | ||
const ref = useRef<OTPInputRef>(null); | ||
|
||
return ( | ||
<View> | ||
<OTPInput | ||
ref={ref} | ||
maxLength={6} | ||
render={({ slots }) => ( | ||
<View className="flex-row gap-3 justify-center my-4"> | ||
{slots.map((slot, idx) => ( | ||
<React.Fragment key={idx}> | ||
<Slot {...slot} /> | ||
{idx === 2 && <FakeDash />} | ||
</React.Fragment> | ||
))} | ||
</View> | ||
)} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function Slot({ char, isActive, hasFakeCaret }: SlotProps) { | ||
return ( | ||
<View | ||
className={cn( | ||
'w-12 h-12 items-center justify-center border border-gray-200 rounded-lg bg-gray-50', | ||
{ | ||
'border-blue-600 border-2': isActive, | ||
} | ||
)} | ||
> | ||
{char !== null && ( | ||
<Text className="text-xl font-medium text-gray-900">{char}</Text> | ||
)} | ||
{hasFakeCaret && <FakeCaret />} | ||
</View> | ||
); | ||
} | ||
|
||
function FakeDash() { | ||
return ( | ||
<View className="w-2 items-center justify-center"> | ||
<View className="w-2 h-0.5 bg-gray-200 rounded-sm" /> | ||
</View> | ||
); | ||
} | ||
|
||
function FakeCaret() { | ||
const opacity = useSharedValue(1); | ||
|
||
useEffect(() => { | ||
opacity.value = withRepeat( | ||
withSequence( | ||
withTiming(0, { duration: 500 }), | ||
withTiming(1, { duration: 500 }) | ||
), | ||
-1, | ||
true | ||
); | ||
}, [opacity]); | ||
|
||
const animatedStyle = useAnimatedStyle(() => ({ | ||
opacity: opacity.value, | ||
})); | ||
|
||
const baseStyle = { | ||
width: 2, | ||
height: 24, | ||
backgroundColor: '#2563EB', | ||
borderRadius: 1, | ||
}; | ||
|
||
return ( | ||
<View className="absolute w-full h-full items-center justify-center"> | ||
<Animated.View style={[baseStyle, animatedStyle]} /> | ||
</View> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters