1- import { useContext , useMemo , useRef , useState , type MutableRefObject } from 'react' ;
1+ import { forwardRef , useMemo , useRef , useState } from 'react' ;
22import {
33 Animated ,
44 PanResponder ,
55 StyleSheet ,
66 View ,
77 type PanResponderGestureState ,
8+ type StyleProp ,
9+ type ViewStyle ,
810} from 'react-native' ;
9- import { MainContext } from '../../../contexts ' ;
11+ import refs , { DebuggerVisibility } from '../../../core/refs ' ;
1012import { clamp , getVerticalSafeMargin } from '../../../core/utils' ;
1113import colors from '../../../theme/colors' ;
1214import icons from '../../../theme/icons' ;
@@ -15,104 +17,106 @@ import Icon from '../common/Icon';
1517interface BubbleProps {
1618 bubbleSize : number ;
1719 idleBubbleOpacity : number ;
18- pan : MutableRefObject < Animated . ValueXY > ;
1920 screenWidth : number ;
2021 screenHeight : number ;
22+ style ?: StyleProp < ViewStyle > ;
2123}
2224
23- export default function Bubble ( {
24- bubbleSize,
25- idleBubbleOpacity,
26- pan,
27- screenWidth,
28- screenHeight,
29- } : BubbleProps ) {
30- const { setDebuggerState } = useContext ( MainContext ) ! ;
31- const [ idleOpacity , setIdleOpacity ] = useState ( idleBubbleOpacity ) ;
32- const opacityTimer = useRef < NodeJS . Timeout | null > ( null ) ;
33-
34- const panResponder = useMemo ( ( ) => {
35- const clearTimer = ( ) => {
36- if ( opacityTimer . current ) clearTimeout ( opacityTimer . current ) ;
37- opacityTimer . current = null ;
38- } ;
39-
40- return PanResponder . create ( {
41- onStartShouldSetPanResponder : ( ) => true ,
42- onMoveShouldSetPanResponder : ( ) => true ,
43- onPanResponderGrant : ( ) => {
44- pan . current . setOffset ( {
45- // @ts -ignore
46- x : pan . current . x . _value ,
47- // @ts -ignore
48- y : pan . current . y . _value ,
49- } ) ;
50-
51- pan . current . setValue ( { x : 0 , y : 0 } ) ;
52-
53- clearTimer ( ) ;
54- setIdleOpacity ( 1 ) ;
55- } ,
56- onPanResponderMove : Animated . event ( [ null , { dx : pan . current . x , dy : pan . current . y } ] , {
57- useNativeDriver : false ,
58- } ) ,
59- onPanResponderRelease : ( _ , gesture : PanResponderGestureState ) => {
60- const isTapGesture =
61- gesture . dx > - 10 && gesture . dx < 10 && gesture . dy > - 10 && gesture . dy < 10 ;
62-
63- if ( isTapGesture )
64- setDebuggerState ( draft => {
65- draft . visibility = 'panel' ;
66- } ) ;
25+ const Bubble = forwardRef < View , BubbleProps > (
26+ ( { bubbleSize, idleBubbleOpacity, screenWidth, screenHeight, style } , ref ) => {
27+ const [ idleOpacity , setIdleOpacity ] = useState ( idleBubbleOpacity ) ;
28+ const pan = useRef ( new Animated . ValueXY ( { x : 0 , y : getVerticalSafeMargin ( screenHeight ) } ) ) ;
29+ const opacityTimer = useRef < NodeJS . Timeout | null > ( null ) ;
6730
68- pan . current . flattenOffset ( ) ;
31+ const panResponder = useMemo ( ( ) => {
32+ const clearTimer = ( ) => {
33+ if ( opacityTimer . current ) clearTimeout ( opacityTimer . current ) ;
34+ opacityTimer . current = null ;
35+ } ;
6936
70- const finalX =
71- gesture . moveX < ( screenWidth - bubbleSize ) / 2 ? 0 : screenWidth - bubbleSize ;
37+ const blur = ( ) => {
38+ opacityTimer . current = setTimeout ( ( ) => {
39+ setIdleOpacity ( 0.5 ) ;
40+ clearTimer ( ) ;
41+ } , 1000 ) ;
42+ } ;
7243
73- const verticalSafeMargin = getVerticalSafeMargin ( screenHeight ) ;
44+ return PanResponder . create ( {
45+ onStartShouldSetPanResponder : ( ) => true ,
46+ onMoveShouldSetPanResponder : ( ) => true ,
47+ onPanResponderGrant : ( ) => {
48+ pan . current . setOffset ( {
49+ // @ts -ignore
50+ x : pan . current . x . _value ,
51+ // @ts -ignore
52+ y : pan . current . y . _value ,
53+ } ) ;
7454
75- const finalY = clamp (
76- gesture . moveY ,
77- verticalSafeMargin ,
78- screenHeight - verticalSafeMargin - bubbleSize ,
79- ) ;
55+ pan . current . setValue ( { x : 0 , y : 0 } ) ;
8056
81- Animated . spring ( pan . current , {
82- toValue : { x : finalX , y : finalY } ,
57+ clearTimer ( ) ;
58+ setIdleOpacity ( 1 ) ;
59+ } ,
60+ onPanResponderMove : Animated . event ( [ null , { dx : pan . current . x , dy : pan . current . y } ] , {
8361 useNativeDriver : false ,
84- } ) . start ( ( { finished } ) => {
85- if ( ! finished ) return ;
86-
87- opacityTimer . current = setTimeout ( ( ) => {
88- setIdleOpacity ( 0.5 ) ;
89- clearTimer ( ) ;
90- } , 1000 ) ;
91- } ) ;
92- } ,
93- } ) ;
94- } , [ bubbleSize , pan , screenHeight , screenWidth , setDebuggerState ] ) ;
95-
96- return (
97- < View style = { styles . bubbleBackdrop } >
98- < Animated . View
99- { ...panResponder . panHandlers }
100- style = { [
101- styles . bubble ,
102- {
103- width : bubbleSize ,
104- height : bubbleSize ,
105- borderRadius : bubbleSize / 2 ,
106- transform : pan . current . getTranslateTransform ( ) ,
107- opacity : idleOpacity ,
108- } ,
109- ] }
110- >
111- < Icon source = { icons . bug } size = { bubbleSize * 0.65 } />
112- </ Animated . View >
113- </ View >
114- ) ;
115- }
62+ } ) ,
63+ onPanResponderRelease : ( _ , gesture : PanResponderGestureState ) => {
64+ pan . current . flattenOffset ( ) ;
65+
66+ const isTapGesture =
67+ gesture . dx > - 10 && gesture . dx < 10 && gesture . dy > - 10 && gesture . dy < 10 ;
68+ if ( isTapGesture ) {
69+ refs . debugger . current ?. setCurrentIndex ( DebuggerVisibility . Panel ) ;
70+ }
71+
72+ if ( gesture . moveX === 0 && gesture . moveY === 0 ) {
73+ blur ( ) ;
74+ return ;
75+ }
76+
77+ const finalX =
78+ gesture . moveX < ( screenWidth - bubbleSize ) / 2 ? 0 : screenWidth - bubbleSize ;
79+
80+ const verticalSafeMargin = getVerticalSafeMargin ( screenHeight ) ;
81+
82+ const finalY = clamp (
83+ gesture . moveY ,
84+ verticalSafeMargin ,
85+ screenHeight - verticalSafeMargin - bubbleSize ,
86+ ) ;
87+
88+ Animated . spring ( pan . current , {
89+ toValue : { x : finalX , y : finalY } ,
90+ useNativeDriver : false ,
91+ } ) . start ( ( { finished } ) => {
92+ if ( ! finished ) return ;
93+ blur ( ) ;
94+ } ) ;
95+ } ,
96+ } ) ;
97+ } , [ bubbleSize , pan , screenHeight , screenWidth ] ) ;
98+
99+ return (
100+ < View ref = { ref } style = { [ styles . bubbleBackdrop , style ] } >
101+ < Animated . View
102+ { ...panResponder . panHandlers }
103+ style = { [
104+ styles . bubble ,
105+ {
106+ width : bubbleSize ,
107+ height : bubbleSize ,
108+ borderRadius : bubbleSize / 2 ,
109+ transform : pan . current . getTranslateTransform ( ) ,
110+ opacity : idleOpacity ,
111+ } ,
112+ ] }
113+ >
114+ < Icon source = { icons . bug } size = { bubbleSize * 0.65 } />
115+ </ Animated . View >
116+ </ View >
117+ ) ;
118+ } ,
119+ ) ;
116120
117121const styles = StyleSheet . create ( {
118122 bubbleBackdrop : {
@@ -127,3 +131,5 @@ const styles = StyleSheet.create({
127131 alignItems : 'center' ,
128132 } ,
129133} ) ;
134+
135+ export default Bubble ;
0 commit comments