import { useRef, useState } from 'react'; import { View, Text, Animated, StyleSheet } from 'react-native'; const TRACK_HEIGHT = 60; const HANDLE_SIZE = 48; const PADDING = 6; const THRESHOLD = 0.82; interface Props { label?: string; color?: string; onConfirm: () => void; disabled?: boolean; } export function SlideToConfirm({ label = 'Slide to confirm', color = '#059669', onConfirm, disabled = false }: Props) { const [trackW, setTrackW] = useState(0); const [done, setDone] = useState(false); const translateX = useRef(new Animated.Value(0)).current; const startX = useRef(0); const currentX = useRef(0); const maxX = Math.max(trackW - HANDLE_SIZE - PADDING * 2, 1); const onTouchStart = (e: any) => { if (disabled || done) return; startX.current = e.nativeEvent.pageX; }; const onTouchMove = (e: any) => { if (disabled || done) return; const dx = e.nativeEvent.pageX - startX.current; const x = Math.max(0, Math.min(dx, maxX)); currentX.current = x; translateX.setValue(x); }; const onTouchEnd = () => { if (disabled || done) return; if (maxX > 0 && currentX.current / maxX >= THRESHOLD) { Animated.timing(translateX, { toValue: maxX, duration: 100, useNativeDriver: false }).start(() => { setDone(true); onConfirm(); }); } else { Animated.spring(translateX, { toValue: 0, useNativeDriver: false, speed: 25, bounciness: 4 }).start(); currentX.current = 0; } }; // Fade label as handle moves right const safeMax = Math.max(maxX * 0.5, 1); const labelOpacity = translateX.interpolate({ inputRange: [0, safeMax], outputRange: [1, 0], extrapolate: 'clamp', }); return ( setTrackW(e.nativeEvent.layout.width)} style={[styles.track, { backgroundColor: done ? color : '#F1F5F9', borderColor: done ? color : '#E2E8F0', }]} > {/* Label */} {done ? '✓ Confirmed!' : label} {/* Handle — touch events here */} {!done && ( )} ); } const styles = StyleSheet.create({ track: { height: TRACK_HEIGHT, borderRadius: TRACK_HEIGHT / 2, borderWidth: 1.5, justifyContent: 'center', alignItems: 'center', overflow: 'hidden', }, label: { fontSize: 15, fontWeight: '700', letterSpacing: 0.3, textAlign: 'center', paddingLeft: HANDLE_SIZE + PADDING * 2, paddingRight: 16, }, handle: { position: 'absolute', left: PADDING, top: PADDING, width: HANDLE_SIZE, height: HANDLE_SIZE, borderRadius: HANDLE_SIZE / 2, alignItems: 'center', justifyContent: 'center', elevation: 4, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, }, });