feat(workouts): add swipe-to-delete with gesture handler and pull-to-refresh

This commit is contained in:
2026-08-07 16:55:18 +02:00
parent 48286041c6
commit c14ccf1d2c
12 changed files with 365 additions and 110 deletions
+100
View File
@@ -0,0 +1,100 @@
import { useRef } from "react";
import { View, Text, Pressable, Animated, StyleSheet } from "react-native";
import { PanGestureHandler, State } from "react-native-gesture-handler";
type SwipeableRowProps = {
children: React.ReactNode;
onDelete: () => void;
};
export function SwipeableRow({ children, onDelete }: SwipeableRowProps) {
const translateX = useRef(new Animated.Value(0)).current;
const lastOffset = useRef(0);
const onGestureEvent = Animated.event([{ nativeEvent: { translationX: translateX } }], {
useNativeDriver: true,
});
const onHandlerStateChange = (event: any) => {
if (event.oldState === State.ACTIVE) {
const { translationX } = event.nativeEvent;
const newOffset = lastOffset.current + translationX;
if (newOffset < -80) {
// Swiped left enough — show delete
Animated.spring(translateX, {
toValue: -80,
useNativeDriver: true,
}).start();
lastOffset.current = -80;
} else {
// Snap back
Animated.spring(translateX, {
toValue: 0,
useNativeDriver: true,
}).start();
lastOffset.current = 0;
}
}
};
const handleDelete = () => {
Animated.timing(translateX, {
toValue: -400,
duration: 200,
useNativeDriver: true,
}).start(() => {
onDelete();
});
};
return (
<View style={styles.container}>
<View style={styles.deleteBackground}>
<Pressable onPress={handleDelete} style={styles.deleteButton}>
<Text style={styles.deleteText}>Delete</Text>
</Pressable>
</View>
<PanGestureHandler
onGestureEvent={onGestureEvent}
onHandlerStateChange={onHandlerStateChange}
>
<Animated.View style={[styles.foreground, { transform: [{ translateX }] }]}>
{children}
</Animated.View>
</PanGestureHandler>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginBottom: 12,
},
deleteBackground: {
position: "absolute",
right: 0,
top: 0,
bottom: 0,
width: 80,
borderRadius: 16,
backgroundColor: "#ef4444",
justifyContent: "center",
alignItems: "center",
},
deleteButton: {
flex: 1,
justifyContent: "center",
alignItems: "center",
width: "100%",
},
deleteText: {
color: "white",
fontWeight: "600",
fontSize: 13,
},
foreground: {
backgroundColor: "white",
borderRadius: 16,
},
});