| import React from 'react'; | |
| import { StyleSheet, Text, Pressable } from 'react-native'; | |
| import Animated, { | |
| useSharedValue, | |
| useAnimatedStyle, | |
| withSpring, | |
| withSequence, | |
| withTiming, | |
| } from 'react-native-reanimated'; | |
| import { COLORS } from '../constants/colors'; | |
| interface AnswerButtonProps { | |
| answer: number; | |
| index: number; | |
| onPress: (answer: number) => void; | |
| disabled: boolean; | |
| isCorrect?: boolean | null; | |
| } | |
| export const AnswerButton: React.FC<AnswerButtonProps> = ({ | |
| answer, | |
| index, | |
| onPress, | |
| disabled, | |
| isCorrect, | |
| }) => { | |
| const scale = useSharedValue(1); | |
| const shake = useSharedValue(0); | |
| const handlePress = () => { | |
| if (disabled) return; | |
| scale.value = withSpring(0.94, { damping: 12, stiffness: 300 }, () => { | |
| scale.value = withSpring(1, { damping: 10, stiffness: 200 }); | |
| }); | |
| onPress(answer); | |
| }; | |
| React.useEffect(() => { | |
| if (isCorrect === false) { | |
| shake.value = withSequence( | |
| withTiming(-6, { duration: 40 }), | |
| withTiming(6, { duration: 40 }), | |
| withTiming(-6, { duration: 40 }), | |
| withTiming(6, { duration: 40 }), | |
| withTiming(0, { duration: 40 }) | |
| ); | |
| } else if (isCorrect === true) { | |
| scale.value = withSequence( | |
| withSpring(1.08, { damping: 6, stiffness: 200 }), | |
| withSpring(1, { damping: 10, stiffness: 200 }) | |
| ); | |
| } | |
| }, [isCorrect]); | |
| const animStyle = useAnimatedStyle(() => ({ | |
| transform: [ | |
| { scale: scale.value }, | |
| { translateX: shake.value }, | |
| ], | |
| })); | |
| const bgColor = | |
| isCorrect === true | |
| ? COLORS.success | |
| : isCorrect === false | |
| ? COLORS.error | |
| : COLORS.surfaceLight; | |
| const borderColor = | |
| isCorrect === true | |
| ? COLORS.success | |
| : isCorrect === false | |
| ? COLORS.error | |
| : COLORS.borderLight; | |
| const textColor = | |
| isCorrect === true | |
| ? COLORS.success | |
| : isCorrect === false | |
| ? COLORS.error | |
| : COLORS.text; | |
| return ( | |
| <Animated.View style={[styles.container, animStyle]}> | |
| <Pressable | |
| onPress={handlePress} | |
| disabled={disabled} | |
| style={[ | |
| styles.button, | |
| { | |
| backgroundColor: isCorrect !== null ? bgColor + '18' : COLORS.surface, | |
| borderColor: borderColor, | |
| }, | |
| ]} | |
| > | |
| <Text style={[styles.text, { color: textColor }]}>{answer}</Text> | |
| </Pressable> | |
| </Animated.View> | |
| ); | |
| }; | |
| const styles = StyleSheet.create({ | |
| container: { | |
| width: '48%', | |
| marginVertical: 5, | |
| }, | |
| button: { | |
| borderRadius: 12, | |
| borderWidth: 1.5, | |
| paddingVertical: 18, | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| }, | |
| text: { | |
| fontSize: 26, | |
| fontWeight: '800', | |
| fontVariant: ['tabular-nums'], | |
| }, | |
| }); | |