license import React from 'react' ; import { Animated , Easing , GestureResponderEvent , NativeSyntheticEvent , PanResponder , PanResponderCallbacks , PanResponderGestureState , PanResponderInstance , StyleSheet , TargetedEvent , View , ViewProps , } from 'react-native' ; import { EvaStatus , FalsyText , RenderProp , RTLService , TouchableWeb , TouchableWebProps , Overwrite , LiteralUnion , } from '../../devsupport' ; import { Interaction , styled , StyledComponentProps , StyleType , } from '../../theme' ; import { TextProps } from '../text/text.component' ; import { CheckMark } from '../shared/checkmark.component' ; type ToggleStyledProps = Overwrite < StyledComponentProps , { appearance ? : LiteralUnion < 'default' > ; } > ; export interface ToggleProps extends TouchableWebProps , ToggleStyledProps { children ? : RenderProp < TextProps > | React . ReactText ; checked ? : boolean ; onChange ? : ( checked : boolean ) => void ; status ? : EvaStatus ; } export type ToggleElement = React . ReactElement < ToggleProps > ; extends React . Component property property property property property overview-example overview-example overview-example overview-example overview-example @ styled ( 'Toggle' ) export class Toggle extends React . Component < ToggleProps > implements PanResponderCallbacks { private panResponder : PanResponderInstance ; private thumbWidthAnimation : Animated . Value ; private thumbTranslateAnimation : Animated . Value ; private ellipseScaleAnimation : Animated . Value ; private thumbTranslateAnimationActive : boolean ; constructor ( props ) { super ( props ) ; const { checked , eva } = props ; this . thumbWidthAnimation = new Animated . Value ( eva . style . thumbWidth ) ; this . thumbTranslateAnimation = new Animated . Value ( 0 ) ; this . ellipseScaleAnimation = new Animated . Value ( checked ? 0.01 : 1.0 ) ; this . thumbTranslateAnimationActive = false ; this . panResponder = PanResponder . create ( this ) ; } public onStartShouldSetPanResponder = ( ) : boolean => { return true ; } ; public onStartShouldSetPanResponderCapture = ( ) : boolean => { return true ; } ; public onMoveShouldSetPanResponder = ( ) : boolean => { return true ; } ; public onMoveShouldSetPanResponderCapture = ( ) : boolean => { return true ; } ; public onPanResponderTerminationRequest = ( ) : boolean => { return false ; } ; public onPanResponderGrant = ( event ) : void => { const { checked , disabled , eva } = this . props ; if ( disabled ) { return ; } this . onPressIn ( event ) ; if ( this . thumbTranslateAnimationActive ) { this . thumbTranslateAnimationActive = false ; this . stopAnimations ( ) ; return ; } this . animateThumbWidth ( eva . style . thumbWidth * 1.2 ) ; this . animateEllipseScale ( checked ? 1 : 0.01 ) ; } ; public onPanResponderMove : ( ) => boolean = ( ) : boolean => { return true ; } ; public onPanResponderRelease = ( event , gestureState ) : void => { const { checked , disabled , eva } = this . props ; if ( ! disabled ) { if ( ( ! checked && gestureState . dx > - 5 ) || ( checked && gestureState . dx < 5 ) ) { this . toggle ( this . onPress ) ; } else { this . animateEllipseScale ( checked ? 0.01 : 1 ) ; } } this . animateThumbWidth ( eva . style . thumbWidth ) ; this . onPressOut ( event ) ; } ; public onMouseLeave = ( event ) : void => { if ( this . props . disabled ) { return ; } this . props . eva . dispatch ( [ ] ) ; if ( this . props . onMouseLeave ) { this . props . onMouseLeave ( event ) ; } } ; private onMouseEnter = ( event ) : void => { if ( this . props . disabled ) { return ; } this . props . eva . dispatch ( [ Interaction . HOVER ] ) ; if ( this . props . onMouseEnter ) { this . props . onMouseEnter ( event ) ; } } ; private onFocus = ( event ) : void => { if ( this . props . disabled ) { return ; } this . props . eva . dispatch ( [ Interaction . FOCUSED ] ) ; if ( this . props . onFocus ) { this . props . onFocus ( event ) ; } } ; private onBlur = ( event ) : void => { if ( this . props . disabled ) { return ; } this . props . eva . dispatch ( [ ] ) ; if ( this . props . onBlur ) { this . props . onBlur ( event ) ; } } ; private onPressIn = ( event ) : void => { this . props . eva . dispatch ( [ Interaction . ACTIVE ] ) ; if ( this . props . onPressIn ) { this . props . onPressIn ( event ) ; } } ; private onPressOut = ( event ) : void => { this . props . eva . dispatch ( [ ] ) ; if ( this . props . onPressOut ) { this . props . onPressOut ( event ) ; } } ; private onPress = ( ) : void => { if ( this . props . onChange ) { this . props . onChange ( ! this . props . checked ) ; } } ; private getComponentStyle = ( source ) => { const { checked , disabled } = this . props ; const { outlineWidth , outlineHeight , outlineBorderRadius , outlineBackgroundColor , thumbWidth , thumbHeight , thumbBorderRadius , thumbBackgroundColor , textMarginHorizontal , textFontSize , textFontWeight , textFontFamily , textColor , iconWidth , iconHeight , iconTintColor , backgroundColor , borderColor , ... containerParameters } = source ; return { ellipseContainer : { borderColor : borderColor , backgroundColor : backgroundColor , ... containerParameters , } , highlight : { width : outlineWidth , height : outlineHeight , borderRadius : outlineBorderRadius , backgroundColor : outlineBackgroundColor , } , ellipse : { width : containerParameters . width - ( containerParameters . borderWidth * 2 ) , height : containerParameters . height - ( containerParameters . borderWidth * 2 ) , borderRadius : ( source . height - ( source . borderWidth * 2 ) ) / 2 , backgroundColor : backgroundColor , } , thumb : { alignSelf : checked ? 'flex-end' : 'flex-start' , width : this . thumbWidthAnimation , height : thumbHeight , borderRadius : thumbBorderRadius , backgroundColor : thumbBackgroundColor , elevation : disabled ? 0 : 5 , transform : [ { translateX : this . thumbTranslateAnimation } ] , } , text : { marginHorizontal : textMarginHorizontal , fontSize : textFontSize , fontWeight : textFontWeight , fontFamily : textFontFamily , color : textColor , } , icon : { width : source . iconWidth , height : source . iconHeight , fill : iconTintColor , stroke : iconTintColor , strokeWidth : 3 , } , } ; } ; private animateThumbTranslate = ( value , callback = ( ) => null ) : void => { this . thumbTranslateAnimationActive = true ; Animated . timing ( this . thumbTranslateAnimation , { toValue : RTLService . select ( value , - value ) , duration : 150 , easing : Easing . linear , useNativeDriver : false , } ) . start ( ( ) => { this . thumbTranslateAnimationActive = false ; callback ( ) ; } ) ; } ; private animateThumbWidth = ( value , callback = ( ) => null ) : void => { Animated . timing ( this . thumbWidthAnimation , { toValue : value , duration : 150 , easing : Easing . linear , useNativeDriver : false , } ) . start ( callback ) ; } ; private animateEllipseScale = ( value , callback = ( ) => null ) : void => { Animated . timing ( this . ellipseScaleAnimation , { toValue : value , duration : 200 , easing : Easing . linear , useNativeDriver : false , } ) . start ( callback ) ; } ; private stopAnimations = ( ) : void => { const value = this . props . checked ? 0.01 : 1 ; this . thumbTranslateAnimation . stopAnimation ( ) ; this . ellipseScaleAnimation . stopAnimation ( ) ; this . thumbWidthAnimation . stopAnimation ( ) ; this . ellipseScaleAnimation . setValue ( value ) ; } ; private toggle = ( callback = ( nextValue ) => null ) : void => { const value = this . props . checked ? - 20 : 20 ; this . animateThumbTranslate ( value , ( ) => { this . thumbTranslateAnimation . setValue ( 0 ) ; callback ( ! this . props . checked ) ; } ) ; this . animateThumbWidth ( this . props . eva . style . thumbWidth ) ; } ; public render ( ) { const { eva , style , checked , children , testID , ... touchableProps } = this . props ; const evaStyle = this . getComponentStyle ( eva . style ) ; return ( < View testID = { testID } { ... this . panResponder . panHandlers } style = { [ styles . container , style ] } > < TouchableWeb { ... touchableProps } style = { styles . toggleContainer } onMouseEnter = { this . onMouseEnter } onMouseLeave = { this . onMouseLeave } onFocus = { this . onFocus } onBlur = { this . onBlur } > < View style = { [ evaStyle . highlight , styles . highlight ] } /> < Animated . View style = { [ evaStyle . ellipseContainer , styles . ellipseContainer ] } > < Animated . View style = { [ evaStyle . ellipse , styles . ellipse ] } /> < Animated . View style = { [ evaStyle . thumb , styles . thumb ] } > < CheckMark { ... evaStyle . icon } / > < /Animated.View> < /Animated.View> < /TouchableWeb> < FalsyText style = { evaStyle . text } component = { children } / > < /View>); } } const styles = StyleSheet . create ( { container : { flexDirection : 'row' , alignItems : 'center' , justifyContent : 'center' , } , toggleContainer : { alignItems : 'center' , justifyContent : 'center' , } , ellipseContainer : { justifyContent : 'center' , alignSelf : 'center' , overflow : 'hidden' , } , ellipse : { alignSelf : 'center' , position : 'absolute' , } , highlight : { alignSelf : 'center' , position : 'absolute' , } , thumb : { justifyContent : 'center' , alignItems : 'center' , } , } ) ;