/ components / SwipeBackIndicator.tsx
SwipeBackIndicator.tsx
1 import React from 'react'; 2 import { 3 Animated, 4 StyleSheet, 5 Dimensions, 6 useColorScheme, 7 View, 8 } from 'react-native'; 9 import { Ionicons } from '@expo/vector-icons'; 10 import { BlurView } from 'expo-blur'; 11 12 import { Colors, ColorScheme } from '@/constants/Colors'; 13 14 const { height } = Dimensions.get('window'); 15 16 interface SwipeBackIndicatorProps { 17 swipeProgress?: Animated.Value; 18 swipeOpacity?: Animated.Value; 19 isVisible?: boolean; 20 customStyles?: any; 21 size?: 'small' | 'medium' | 'large'; 22 showText?: boolean; 23 } 24 25 const SwipeBackIndicator: React.FC<SwipeBackIndicatorProps> = ({ 26 swipeProgress, 27 swipeOpacity, 28 isVisible = false, 29 customStyles, 30 size = 'medium', 31 showText = false, 32 }) => { 33 const colorScheme = useColorScheme() as ColorScheme; 34 const arrowColor = Colors[colorScheme].primary; 35 const textColor = Colors[colorScheme].text; 36 37 const sizeConfig = { 38 small: { iconSize: 20, containerSize: 40 }, 39 medium: { iconSize: 30, containerSize: 60 }, 40 large: { iconSize: 40, containerSize: 80 }, 41 }; 42 43 const config = sizeConfig[size]; 44 45 const animatedStyle = 46 swipeProgress && swipeOpacity 47 ? { 48 transform: [ 49 { 50 translateX: swipeProgress.interpolate({ 51 inputRange: [0, 1], 52 outputRange: [-config.containerSize, 20], 53 }), 54 }, 55 { 56 scale: swipeProgress.interpolate({ 57 inputRange: [0, 0.5, 1], 58 outputRange: [0.8, 1.1, 1], 59 }), 60 }, 61 ], 62 opacity: swipeOpacity, 63 } 64 : {}; 65 66 const staticStyle = isVisible 67 ? { 68 opacity: 1, 69 transform: [{ translateX: 0 }], 70 } 71 : { 72 opacity: 0, 73 transform: [{ translateX: -config.containerSize }], 74 }; 75 76 const finalStyle = swipeProgress ? animatedStyle : staticStyle; 77 78 return ( 79 <Animated.View 80 style={[ 81 indicatorStyles.container, 82 { 83 width: config.containerSize, 84 height: config.containerSize, 85 borderRadius: config.containerSize / 2, 86 }, 87 finalStyle, 88 customStyles, 89 ]} 90 > 91 <BlurView 92 intensity={80} 93 tint={colorScheme === 'dark' ? 'dark' : 'light'} 94 style={indicatorStyles.blurContainer} 95 > 96 <View style={indicatorStyles.iconContainer}> 97 <Ionicons 98 name="arrow-back" 99 size={config.iconSize} 100 color={arrowColor} 101 /> 102 </View> 103 {showText && ( 104 <Animated.Text 105 style={[ 106 indicatorStyles.text, 107 { color: textColor }, 108 swipeProgress 109 ? { 110 opacity: swipeProgress.interpolate({ 111 inputRange: [0, 0.5, 1], 112 outputRange: [0, 1, 0], 113 }), 114 } 115 : {}, 116 ]} 117 > 118 Go Back 119 </Animated.Text> 120 )} 121 </BlurView> 122 </Animated.View> 123 ); 124 }; 125 126 interface SwipeGestureOverlayProps { 127 enabled?: boolean; 128 children: React.ReactNode; 129 panResponder?: any; 130 swipeProgress?: Animated.Value; 131 swipeOpacity?: Animated.Value; 132 isSwipingBack?: boolean; 133 } 134 135 export const SwipeGestureOverlay: React.FC<SwipeGestureOverlayProps> = ({ 136 enabled = true, 137 children, 138 panResponder, 139 swipeProgress, 140 swipeOpacity, 141 isSwipingBack = false, 142 }) => { 143 if (!enabled || !panResponder) { 144 return <>{children}</>; 145 } 146 147 return ( 148 <View style={indicatorStyles.overlay} {...panResponder.panHandlers}> 149 {children} 150 {isSwipingBack && swipeProgress && swipeOpacity && ( 151 <> 152 <SwipeBackIndicator 153 swipeProgress={swipeProgress} 154 swipeOpacity={swipeOpacity} 155 size="medium" 156 /> 157 <Animated.View 158 style={[ 159 indicatorStyles.screenOverlay, 160 { 161 opacity: swipeProgress.interpolate({ 162 inputRange: [0, 1], 163 outputRange: [0, 0.1], 164 }), 165 }, 166 ]} 167 /> 168 </> 169 )} 170 </View> 171 ); 172 }; 173 174 const indicatorStyles = StyleSheet.create({ 175 container: { 176 position: 'absolute', 177 top: height / 2 - 40, 178 left: 20, 179 justifyContent: 'center', 180 alignItems: 'center', 181 elevation: 10, 182 shadowColor: '#000', 183 shadowOffset: { 184 width: 0, 185 height: 2, 186 }, 187 shadowOpacity: 0.25, 188 shadowRadius: 3.84, 189 zIndex: 1000, 190 }, 191 blurContainer: { 192 width: '100%', 193 height: '100%', 194 borderRadius: 30, 195 justifyContent: 'center', 196 alignItems: 'center', 197 overflow: 'hidden', 198 }, 199 iconContainer: { 200 justifyContent: 'center', 201 alignItems: 'center', 202 }, 203 text: { 204 fontSize: 12, 205 fontWeight: '600', 206 marginTop: 4, 207 textAlign: 'center', 208 }, 209 overlay: { 210 flex: 1, 211 }, 212 screenOverlay: { 213 position: 'absolute', 214 top: 0, 215 left: 0, 216 right: 0, 217 bottom: 0, 218 backgroundColor: '#000', 219 pointerEvents: 'none', 220 }, 221 }); 222 223 export default SwipeBackIndicator;