import React , { FunctionComponent , useEffect , useMemo , useState } from "react" ; import { Keyboard , KeyboardEvent , Platform , StyleSheet , Text , View , ViewStyle , } from "react-native" ; import { useStyle } from "../../styles" ; import { useSafeAreaInsets } from "react-native-safe-area-context" ; import { PanGestureHandler } from "react-native-gesture-handler" ; import { useModalState , useModalTransision } from "../base" ; import Animated , { Easing } from "react-native-reanimated" ; import { DefaultAcceleration , DefaultCloseVelocity , DefaultOpenVelocity , } from "../base/const" ; const useAnimatedValueSet = ( ) => { const [ state ] = useState ( ( ) => { return { clock : new Animated . Clock ( ) , finished : new Animated . Value ( 0 ) , time : new Animated . Value ( 0 ) , frameTime : new Animated . Value ( 0 ) , value : new Animated . Value ( 0 ) , } ; } ) ; return state ; } ; export const CardModal = ( { title , right , children , childrenContainerStyle , disableGesture = false , } ) => { const style = useStyle ( ) ; const safeAreaInsets = useSafeAreaInsets ( ) ; const [ softwareKeyboardBottomPadding , setSoftwareKeyboardBottomPadding , ] = useState ( 0 ) ; useEffect ( ( ) => { const onKeyboarFrame = ( e ) => { setSoftwareKeyboardBottomPadding ( e . endCoordinates . height - safeAreaInsets . bottom ) ; } ; const onKeyboardClearFrame = ( ) => { setSoftwareKeyboardBottomPadding ( 0 ) ; } ; if ( Platform . OS !== "android" ) { Keyboard . addListener ( "keyboardWillShow" , onKeyboarFrame ) ; Keyboard . addListener ( "keyboardWillChangeFrame" , onKeyboarFrame ) ; Keyboard . addListener ( "keyboardWillHide" , onKeyboardClearFrame ) ; return ( ) => { Keyboard . removeListener ( "keyboardWillShow" , onKeyboarFrame ) ; Keyboard . removeListener ( "keyboardWillChangeFrame" , onKeyboarFrame ) ; Keyboard . removeListener ( "keyboardWillHide" , onKeyboardClearFrame ) ; } ; } } , [ safeAreaInsets . bottom ] ) ; const animatedValueSet = useAnimatedValueSet ( ) ; const modal = useModalState ( ) ; const modalTransition = useModalTransision ( ) ; const animatedKeyboardPaddingBottom = useMemo ( ( ) => { return Animated . block ( [ Animated . cond ( Animated . and ( Animated . neq ( animatedValueSet . value , softwareKeyboardBottomPadding ) , Animated . not ( Animated . clockRunning ( animatedValueSet . clock ) ) ) , [ Animated . debug ( "start clock for keyboard avoiding" , animatedValueSet . value ) , Animated . set ( animatedValueSet . finished , 0 ) , Animated . set ( animatedValueSet . time , 0 ) , Animated . set ( animatedValueSet . frameTime , 0 ) , Animated . startClock ( animatedValueSet . clock ) , ] ) , Animated . timing ( animatedValueSet . clock , { finished : animatedValueSet . finished , position : animatedValueSet . value , time : animatedValueSet . time , frameTime : animatedValueSet . frameTime , } , { toValue : softwareKeyboardBottomPadding , duration : 175 , easing : Easing . linear , } ) , Animated . cond ( animatedValueSet . finished , Animated . stopClock ( animatedValueSet . clock ) ) , animatedValueSet . value , ] ) ; } , [ animatedValueSet . clock , animatedValueSet . finished , animatedValueSet . frameTime , animatedValueSet . time , animatedValueSet . value , softwareKeyboardBottomPadding , ] ) ; const [ startTranslateY ] = useState ( ( ) => new Animated . Value ( 0 ) ) ; const [ openVelocityValue ] = useState ( ( ) => new Animated . Value ( 0 ) ) ; const [ closeVelocityValue ] = useState ( ( ) => new Animated . Value ( 0 ) ) ; const [ velocityYAcceleration ] = useState ( ( ) => new Animated . Value ( 1 ) ) ; const onGestureEvent = useMemo ( ( ) => { const openVelocity = modal . openTransitionVelocity ?? modal . transitionVelocity ?? DefaultOpenVelocity ; const closeVelocity = modal . closeTransitionVelocity ?? modal . transitionVelocity ?? DefaultCloseVelocity ; const acceleration = modal . transitionAcceleration ?? DefaultAcceleration ; return Animated . event ( [ { nativeEvent : ( { velocityY , translationY , state , } ) => { return Animated . block ( [ Animated . cond ( Animated . and ( Animated . or ( Animated . eq ( state , 2 ) , Animated . eq ( state , 4 ) ) , modalTransition . isOpen ) , [ Animated . cond ( modalTransition . isPaused , 0 , Animated . set ( startTranslateY , modalTransition . translateY ) ) , Animated . set ( modalTransition . isPaused , 1 ) , Animated . set ( modalTransition . translateY , Animated . add ( startTranslateY , translationY ) ) , Animated . cond ( Animated . lessOrEq ( modalTransition . translateY , 0 ) , Animated . set ( modalTransition . translateY , 0 ) ) , Animated . set ( velocityYAcceleration , Animated . max ( 1 , Animated . min ( 2 , Animated . divide ( Animated . abs ( velocityY ) , 1750 ) ) ) ) , ] , Animated . cond ( modalTransition . isPaused , [ Animated . cond ( Animated . not ( Animated . or ( Animated . and ( Animated . lessOrEq ( Animated . abs ( Animated . sub ( modalTransition . translateY , modalTransition . startY ) ) , 250 ) , Animated . greaterOrEq ( velocityY , - 30 ) ) , Animated . greaterOrEq ( velocityY , 100 ) ) ) , [ [ Animated . stopClock ( modalTransition . clock ) , Animated . set ( modalTransition . finished , 0 ) , Animated . set ( modalTransition . time , 0 ) , Animated . set ( modalTransition . frameTime , 0 ) , Animated . startClock ( modalTransition . clock ) , ] , Animated . cond ( Animated . greaterThan ( openVelocity , 0 ) , [ Animated . set ( openVelocityValue , Animated . max ( openVelocity , Animated . multiply ( openVelocity , Animated . pow ( acceleration , Animated . divide ( Animated . abs ( modalTransition . translateY ) , 100 ) ) ) ) ) , Animated . set ( openVelocityValue , Animated . multiply ( openVelocityValue , velocityYAcceleration ) ) , Animated . debug ( "velocityYAcceleration" , velocityYAcceleration ) , Animated . set ( modalTransition . duration , Animated . multiply ( Animated . divide ( Animated . abs ( modalTransition . translateY ) , openVelocityValue ) , 1000 ) ) , ] , Animated . set ( modalTransition . duration , 0 ) ) , Animated . set ( modalTransition . durationSetOnExternal , 1 ) , Animated . set ( modalTransition . isOpen , 1 ) , ] , [ [ Animated . stopClock ( modalTransition . clock ) , Animated . set ( modalTransition . finished , 0 ) , Animated . set ( modalTransition . time , 0 ) , Animated . set ( modalTransition . frameTime , 0 ) , Animated . startClock ( modalTransition . clock ) , ] , Animated . cond ( Animated . greaterThan ( closeVelocity , 0 ) , [ Animated . set ( closeVelocityValue , Animated . max ( closeVelocity , Animated . multiply ( closeVelocity , Animated . pow ( acceleration , Animated . divide ( Animated . abs ( Animated . sub ( modalTransition . translateY , modalTransition . startY ) ) , 100 ) ) ) ) ) , Animated . set ( closeVelocityValue , Animated . multiply ( closeVelocityValue , velocityYAcceleration ) ) , Animated . debug ( "velocityYAcceleration" , velocityYAcceleration ) , Animated . set ( modalTransition . duration , Animated . multiply ( Animated . divide ( Animated . abs ( Animated . sub ( modalTransition . translateY , modalTransition . startY ) ) , closeVelocityValue ) , 1000 ) ) , ] , Animated . set ( modalTransition . duration , 0 ) ) , Animated . set ( modalTransition . durationSetOnExternal , 1 ) , Animated . set ( modalTransition . isOpen , 0 ) , Animated . call ( [ ] , ( ) => { modal . close ( ) ; } ) , ] ) , Animated . set ( modalTransition . isPaused , 0 ) , ] ) ) , ] ) ; } , } , ] ) ; } , [ modal . close , modal . closeTransitionVelocity , modal . openTransitionVelocity , modal . transitionVelocity , modalTransition . clock , modalTransition . duration , modalTransition . finished , modalTransition . frameTime , modalTransition . isOpen , modalTransition . isPaused , modalTransition . time , modalTransition . translateY , modalTransition . startY , startTranslateY , ] ) ; return ( < Animated . View style = { StyleSheet . flatten ( [ style . flatten ( [ "background-color-white" , "border-radius-top-left-8" , "border-radius-top-right-8" , "overflow-hidden" , ] ) , { paddingBottom : Animated . add ( safeAreaInsets . bottom , animatedKeyboardPaddingBottom ) , } , ] ) } > < PanGestureHandler onGestureEvent = { onGestureEvent } onHandlerStateChange = { onGestureEvent } enabled = { ! disableGesture } > { } < Animated . View style = { style . flatten ( [ "padding-x-page" ] ) } > < View style = { style . flatten ( [ "items-center" , "margin-bottom-16" ] ) } > { ! disableGesture ? ( < View style = { style . flatten ( [ "margin-top-8" , "width-58" , "height-5" , "border-radius-16" , "background-color-card-modal-handle" , ] ) } / > ) : null } < /View> { title ? ( < React . Fragment > < View style = { style . flatten ( [ "flex-row" , "items-center" , "margin-bottom-16" , ] ) } > < Text style = { style . flatten ( [ "h4" , "color-text-black-high" ] ) } > { title } < /Text> { right } < /View> < View style = { style . flatten ( [ "height-1" , "background-color-border-white" , ] ) } / > < /React.Fragment>) : null} < /Animated.View> < /PanGestureHandler> < View style = { StyleSheet . flatten ( [ style . flatten ( [ "padding-page" , "padding-top-16" ] ) , childrenContainerStyle , ] ) } > { children } < /View> < /Animated.View>); } ;