11import { forwardRef , useCallback , useMemo , type FC } from 'react' ;
2- import {
3- Pressable ,
4- StyleSheet ,
5- type GestureResponderEvent ,
6- type LayoutChangeEvent ,
7- } from 'react-native' ;
2+ import { Pressable , StyleSheet , type LayoutChangeEvent } from 'react-native' ;
83
94import Animated , {
105 interpolate ,
116 useAnimatedStyle ,
12- useSharedValue ,
137 withTiming ,
148} from 'react-native-reanimated' ;
9+ import Svg , { Defs , RadialGradient , Rect , Stop } from 'react-native-svg' ;
1510
11+ import { GestureDetector } from 'react-native-gesture-handler' ;
1612import { useThemeColor } from '../../helpers/theme' ;
1713import type { PressableRef } from '../../helpers/types' ;
1814import {
1915 PressableFeedbackAnimationProvider ,
2016 usePressableFeedbackAnimation ,
2117 usePressableFeedbackHighlightAnimation ,
18+ usePressableFeedbackRootAnimation ,
2219} from './pressable-feedback.animation' ;
2320import { DISPLAY_NAME } from './pressable-feedback.constants' ;
2421import pressableFeedbackStyles from './pressable-feedback.styles' ;
@@ -34,46 +31,21 @@ const PressableFeedback = forwardRef<PressableRef, PressableFeedbackProps>(
3431 isDisabled = false ,
3532 className,
3633 children,
37- onPress,
38- onPressIn,
39- onPressOut,
4034 onLayout,
4135 ...restProps
4236 } = props ;
4337
4438 const tvStyles = pressableFeedbackStyles ( { className } ) ;
4539
46- const isPressed = useSharedValue ( false ) ;
47- const pressedCenterX = useSharedValue ( 0 ) ;
48- const pressedCenterY = useSharedValue ( 0 ) ;
49- const containerWidth = useSharedValue ( 0 ) ;
50- const containerHeight = useSharedValue ( 0 ) ;
51- const rippleProgress = useSharedValue ( 0 ) ;
52-
53- const handlePressIn = useCallback (
54- ( event : GestureResponderEvent ) => {
55- rippleProgress . set ( 0 ) ;
56- isPressed . set ( true ) ;
57- pressedCenterX . set ( event . nativeEvent . locationX ) ;
58- pressedCenterY . set ( event . nativeEvent . locationY ) ;
59- rippleProgress . set ( withTiming ( 1 , { duration : 200 } ) ) ;
60- // @ts -ignore
61- onPressIn ?.( event ) ;
62- } ,
63- // eslint-disable-next-line react-hooks/exhaustive-deps
64- [ isPressed , onPressIn ]
65- ) ;
66-
67- const handlePressOut = useCallback (
68- ( event : GestureResponderEvent ) => {
69- rippleProgress . set ( withTiming ( 2 , { duration : 400 } ) ) ;
70- isPressed . set ( false ) ;
71- // @ts -ignore
72- onPressOut ?.( event ) ;
73- } ,
74- // eslint-disable-next-line react-hooks/exhaustive-deps
75- [ isPressed , onPressOut ]
76- ) ;
40+ const {
41+ isPressed,
42+ pressedCenterX,
43+ pressedCenterY,
44+ containerWidth,
45+ containerHeight,
46+ rippleProgress,
47+ gesture,
48+ } = usePressableFeedbackRootAnimation ( ) ;
7749
7850 const handleLayout = useCallback (
7951 ( event : LayoutChangeEvent ) => {
@@ -106,20 +78,19 @@ const PressableFeedback = forwardRef<PressableRef, PressableFeedbackProps>(
10678
10779 return (
10880 < PressableFeedbackAnimationProvider value = { animationContextValue } >
109- < AnimatedPressable
110- ref = { ref }
111- disabled = { isDisabled }
112- className = { tvStyles }
113- onPress = { onPress }
114- onPressIn = { handlePressIn }
115- onPressOut = { handlePressOut }
116- onLayout = { handleLayout }
117- { ...restProps }
118- >
119- { /* <PressableFeedbackHighlight animation={animation} /> */ }
120- < PressableFeedbackRipple />
121- { children }
122- </ AnimatedPressable >
81+ < GestureDetector gesture = { gesture } >
82+ < AnimatedPressable
83+ ref = { ref }
84+ disabled = { isDisabled }
85+ className = { tvStyles }
86+ onLayout = { handleLayout }
87+ { ...restProps }
88+ >
89+ { /* <PressableFeedbackHighlight animation={animation} /> */ }
90+ < PressableFeedbackRipple />
91+ { children }
92+ </ AnimatedPressable >
93+ </ GestureDetector >
12394 </ PressableFeedbackAnimationProvider >
12495 ) ;
12596 }
@@ -156,9 +127,8 @@ const PressableFeedbackRipple: FC<{}> = () => {
156127 const themeColorSurfaceSecondary = useThemeColor ( 'on-surface-hover' ) ;
157128
158129 const rContainerStyle = useAnimatedStyle ( ( ) => {
159- const circleRadius = Math . sqrt (
160- containerWidth . get ( ) ** 2 + containerHeight . get ( ) ** 2
161- ) ;
130+ const circleRadius =
131+ Math . sqrt ( containerWidth . get ( ) ** 2 + containerHeight . get ( ) ** 2 ) * 1.25 ;
162132
163133 const translateX = pressedCenterX . get ( ) - circleRadius ;
164134 const translateY = pressedCenterY . get ( ) - circleRadius ;
@@ -169,9 +139,8 @@ const PressableFeedbackRipple: FC<{}> = () => {
169139 borderRadius : circleRadius ,
170140 opacity : withTiming (
171141 interpolate ( rippleProgress . get ( ) , [ 0 , 1 , 2 ] , [ 0 , 1 , 0 ] ) ,
172- { duration : 50 }
142+ { duration : 40 }
173143 ) ,
174- backgroundColor : themeColorSurfaceSecondary ,
175144 transform : [
176145 {
177146 translateX,
@@ -182,7 +151,7 @@ const PressableFeedbackRipple: FC<{}> = () => {
182151 {
183152 scale : withTiming (
184153 interpolate ( rippleProgress . get ( ) , [ 0 , 1 , 2 ] , [ 0 , 1 , 1 ] ) ,
185- { duration : 50 }
154+ { duration : 40 }
186155 ) ,
187156 } ,
188157 ] ,
@@ -194,7 +163,36 @@ const PressableFeedbackRipple: FC<{}> = () => {
194163 pointerEvents = "none"
195164 className = "absolute top-0 left-0"
196165 style = { rContainerStyle }
197- />
166+ >
167+ < Svg width = "100%" height = "100%" >
168+ < Defs >
169+ < RadialGradient id = "rippleGradient" cx = "50%" cy = "50%" r = "50%" >
170+ < Stop
171+ offset = "0%"
172+ stopColor = { themeColorSurfaceSecondary }
173+ stopOpacity = "1"
174+ />
175+ < Stop
176+ offset = "75%"
177+ stopColor = { themeColorSurfaceSecondary }
178+ stopOpacity = "0.75"
179+ />
180+ < Stop
181+ offset = "100%"
182+ stopColor = { themeColorSurfaceSecondary }
183+ stopOpacity = "0"
184+ />
185+ </ RadialGradient >
186+ </ Defs >
187+ < Rect
188+ x = "0"
189+ y = "0"
190+ width = "100%"
191+ height = "100%"
192+ fill = "url(#rippleGradient)"
193+ />
194+ </ Svg >
195+ </ Animated . View >
198196 ) ;
199197} ;
200198
0 commit comments