refactor(components): simplify SwipeableRow gesture handling and add vertical scroll conflict prevention

This commit is contained in:
2026-08-08 23:43:34 +02:00
parent 819d26e0f6
commit df3030ae72
+15 -18
View File
@@ -1,6 +1,6 @@
import { useRef } from "react";
import { View, Text, Pressable, Animated, StyleSheet } from "react-native";
import { PanGestureHandler, State } from "react-native-gesture-handler";
import { PanGestureHandler, State, PanGestureHandlerGestureEvent } from "react-native-gesture-handler";
type SwipeableRowProps = {
children: React.ReactNode;
@@ -8,37 +8,33 @@ type SwipeableRowProps = {
deleteLabel?: string;
};
const MAX_LEFT = -80;
export function SwipeableRow({ children, onDelete, deleteLabel = "Delete" }: SwipeableRowProps) {
const translateX = useRef(new Animated.Value(0)).current;
const lastOffset = useRef(0);
const offset = useRef(0);
const onGestureEvent = Animated.event([{ nativeEvent: { translationX: translateX } }], {
useNativeDriver: true,
});
const onGestureEvent = (event: PanGestureHandlerGestureEvent) => {
const raw = event.nativeEvent.translationX;
const clamped = Math.max(MAX_LEFT, Math.min(0, offset.current + raw));
translateX.setValue(clamped);
};
const onHandlerStateChange = (event: any) => {
const onHandlerStateChange = (event: PanGestureHandlerGestureEvent) => {
if (event.oldState === State.ACTIVE) {
const { translationX } = event.nativeEvent;
const clampedTranslation = Math.min(0, translationX);
const newOffset = lastOffset.current + clampedTranslation;
const final = Math.max(MAX_LEFT, Math.min(0, offset.current + translationX));
if (newOffset < -80) {
Animated.spring(translateX, {
toValue: -80,
toValue: final,
useNativeDriver: true,
}).start();
lastOffset.current = -80;
} else {
Animated.spring(translateX, {
toValue: 0,
useNativeDriver: true,
}).start();
lastOffset.current = 0;
}
offset.current = final;
}
};
const handleDelete = () => {
offset.current = -400;
Animated.timing(translateX, {
toValue: -400,
duration: 200,
@@ -58,6 +54,7 @@ export function SwipeableRow({ children, onDelete, deleteLabel = "Delete" }: Swi
<PanGestureHandler
onGestureEvent={onGestureEvent}
onHandlerStateChange={onHandlerStateChange}
failOffsetY={[-5, 5]}
>
<Animated.View style={[styles.foreground, { transform: [{ translateX }] }]}>
{children}