From df3030ae725d821fdc8e836e003bcfc89b90cf29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=AB?= Date: Sat, 8 Aug 2026 23:43:34 +0200 Subject: [PATCH] refactor(components): simplify SwipeableRow gesture handling and add vertical scroll conflict prevention --- src/components/SwipeableRow.tsx | 39 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/components/SwipeableRow.tsx b/src/components/SwipeableRow.tsx index 7efa41b..ba58587 100644 --- a/src/components/SwipeableRow.tsx +++ b/src/components/SwipeableRow.tsx @@ -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, - useNativeDriver: true, - }).start(); - lastOffset.current = -80; - } else { - Animated.spring(translateX, { - toValue: 0, - useNativeDriver: true, - }).start(); - lastOffset.current = 0; - } + Animated.spring(translateX, { + toValue: final, + useNativeDriver: true, + }).start(); + 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 {children}