trails/apps/mobile/lib/editor/WaypointSheet.tsx
Ullrich Schäfer bdde95e69b
Use @gorhom/bottom-sheet for waypoint sheet
Replace custom View-based bottom sheet with @gorhom/bottom-sheet:
- Native gesture-driven drag, snap points, pan-down-to-close
- Proper safe area handling built in
- Wrap app with GestureHandlerRootView
- Add react-native-reanimated plugin to Expo config

Adds react-native-reanimated and react-native-gesture-handler as
native dependencies (requires new EAS build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:09:13 +02:00

101 lines
3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useRef, useCallback, useMemo } from "react";
import { Text, TouchableOpacity, StyleSheet, Alert } from "react-native";
import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet";
import type { Waypoint } from "@trails-cool/types";
interface WaypointSheetProps {
waypoint: Waypoint;
index: number;
onClose: () => void;
onDelete: (index: number) => void;
onToggleOvernight: (index: number) => void;
}
export function WaypointSheet({
waypoint,
index,
onClose,
onDelete,
onToggleOvernight,
}: WaypointSheetProps) {
const bottomSheetRef = useRef<BottomSheet>(null);
const snapPoints = useMemo(() => ["35%"], []);
const handleDelete = useCallback(() => {
Alert.alert(
"Delete Waypoint",
`Remove waypoint ${index + 1}${waypoint.name ? ` (${waypoint.name})` : ""}?`,
[
{ text: "Cancel", style: "cancel" },
{
text: "Delete",
style: "destructive",
onPress: () => {
onDelete(index);
onClose();
},
},
],
);
}, [index, waypoint.name, onDelete, onClose]);
const handleSheetChange = useCallback((sheetIndex: number) => {
if (sheetIndex === -1) onClose();
}, [onClose]);
return (
<BottomSheet
ref={bottomSheetRef}
snapPoints={snapPoints}
enablePanDownToClose
onChange={handleSheetChange}
backgroundStyle={styles.background}
handleIndicatorStyle={styles.handle}
>
<BottomSheetView style={styles.content}>
<Text style={styles.title}>
Waypoint {index + 1}{waypoint.name ? `: ${waypoint.name}` : ""}
</Text>
<Text style={styles.coords}>
{waypoint.lat.toFixed(5)}, {waypoint.lon.toFixed(5)}
</Text>
<TouchableOpacity
style={styles.option}
onPress={() => {
onToggleOvernight(index);
onClose();
}}
>
<Text style={styles.optionIcon}>{waypoint.isDayBreak ? "☀️" : "🌙"}</Text>
<Text style={styles.optionText}>
{waypoint.isDayBreak ? "Remove overnight stop" : "Mark as overnight stop"}
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.option} onPress={handleDelete}>
<Text style={styles.optionIcon}>🗑</Text>
<Text style={[styles.optionText, styles.deleteText]}>Delete waypoint</Text>
</TouchableOpacity>
</BottomSheetView>
</BottomSheet>
);
}
const styles = StyleSheet.create({
background: { borderTopLeftRadius: 16, borderTopRightRadius: 16 },
handle: { backgroundColor: "#d1d5db", width: 36 },
content: { padding: 16 },
title: { fontSize: 16, fontWeight: "600", color: "#111" },
coords: { fontSize: 12, color: "#999", marginTop: 2, marginBottom: 16 },
option: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 14,
borderTopWidth: 1,
borderTopColor: "#f3f4f6",
},
optionIcon: { fontSize: 18, marginRight: 12 },
optionText: { fontSize: 15, color: "#333" },
deleteText: { color: "#dc2626" },
});