Skip to content

Instantly share code, notes, and snippets.

@tanner-west
Last active September 11, 2023 00:00
Show Gist options
  • Save tanner-west/04aae400b3d957b57734b59259f09791 to your computer and use it in GitHub Desktop.
Save tanner-west/04aae400b3d957b57734b59259f09791 to your computer and use it in GitHub Desktop.
/*
**
** GitHub Gist: https://rnrocket.dev/monday1
**
*/
import "react-native-gesture-handler";
import { useState, useRef } from "react";
import {
LayoutRectangle,
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import { StatusBar } from "expo-status-bar";
import Animated, {
useSharedValue,
withTiming,
useAnimatedStyle,
Easing,
} from "react-native-reanimated";
const choices = ["cats", "dogs", "penguins", "turtles"];
const Choice = ({ text }: { text: string }) => (
<Text style={styles.choiceText}>{text}</Text>
);
export default function App() {
const [selectedIndex, setSelectedIndex] = useState(-1);
const layouts = useRef<Record<number, LayoutRectangle> | null>(null);
const indicatorPositionTop = useSharedValue(-1000);
const onChoiceLayout = (layout: LayoutRectangle, index: number) => {
layouts.current = { ...layouts.current, [index]: layout };
};
const animatedStyle = useAnimatedStyle(() => {
return {
top: withTiming(indicatorPositionTop.value, {
duration: 250,
easing: Easing.bezier(0.33, 1, 0.68, 1),
}),
};
});
const targetLayout = layouts?.current?.[selectedIndex];
const targetPosition =
(targetLayout && targetLayout?.y + targetLayout?.height / 2 - 5) || -1000;
indicatorPositionTop.value = targetPosition;
return (
<SafeAreaView style={styles.safeAreaView}>
<StatusBar style="light" />
<View style={styles.headingContainer}>
<Text style={styles.heading}>choose one</Text>
</View>
<View style={styles.choicesContainer}>
<Animated.View
style={[styles.indicator, animatedStyle]}
></Animated.View>
{choices.map((choice, index) => (
<TouchableOpacity
onPressIn={() => {
setSelectedIndex(index);
}}
key={choice}
onLayout={({ nativeEvent }) => {
const layout = nativeEvent.layout;
onChoiceLayout(layout, index);
}}
>
<Choice text={choice} />
</TouchableOpacity>
))}
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeAreaView: {
flex: 1,
justifyContent: "center",
backgroundColor: "#0B3142",
},
choiceText: {
fontSize: 48,
fontWeight: "bold",
color: "white",
marginHorizontal: 30,
},
indicator: {
height: 20,
width: 20,
backgroundColor: "white",
position: "absolute",
left: 15,
},
heading: { fontSize: 60, fontWeight: "bold", color: "white" },
headingContainer: {
flex: 1,
justifyContent: "flex-end",
paddingLeft: 10,
marginBottom: 10,
},
choicesContainer: { flex: 2, paddingLeft: 20 },
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment