98 lines
2.5 KiB
TypeScript
98 lines
2.5 KiB
TypeScript
import { useRef } from "react";
|
|
import { View, Text, Pressable, Animated, StyleSheet } from "react-native";
|
|
import { PanGestureHandler, State, PanGestureHandlerGestureEvent } from "react-native-gesture-handler";
|
|
|
|
type SwipeableRowProps = {
|
|
children: React.ReactNode;
|
|
onDelete: () => void;
|
|
deleteLabel?: string;
|
|
};
|
|
|
|
const MAX_LEFT = -80;
|
|
|
|
export function SwipeableRow({ children, onDelete, deleteLabel = "Delete" }: SwipeableRowProps) {
|
|
const translateX = useRef(new Animated.Value(0)).current;
|
|
const offset = useRef(0);
|
|
|
|
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: PanGestureHandlerGestureEvent) => {
|
|
if (event.oldState === State.ACTIVE) {
|
|
const { translationX } = event.nativeEvent;
|
|
const final = Math.max(MAX_LEFT, Math.min(0, offset.current + translationX));
|
|
|
|
Animated.spring(translateX, {
|
|
toValue: final,
|
|
useNativeDriver: true,
|
|
}).start();
|
|
offset.current = final;
|
|
}
|
|
};
|
|
|
|
const handleDelete = () => {
|
|
offset.current = -400;
|
|
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}>{deleteLabel}</Text>
|
|
</Pressable>
|
|
</View>
|
|
<PanGestureHandler
|
|
onGestureEvent={onGestureEvent}
|
|
onHandlerStateChange={onHandlerStateChange}
|
|
failOffsetY={[-5, 5]}
|
|
>
|
|
<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,
|
|
},
|
|
});
|