UNPKG

@lunalee/react-native-raw-bottom-sheet

Version:

Add Your Own Component To Bottom Sheet Whatever You Want (Android & iOS)

181 lines (167 loc) 4.95 kB
import React, { Component } from "react" import PropTypes from "prop-types" import { View, KeyboardAvoidingView, Modal, TouchableOpacity, Animated, PanResponder, Platform } from "react-native" import styles from "./style" const SUPPORTED_ORIENTATIONS = [ "portrait", "portrait-upside-down", "landscape", "landscape-left", "landscape-right" ] class RBSheet extends Component { constructor ( props ) { super( props ) this.state = { modalVisible: false, animatedHeight: new Animated.Value( 0 ), pan: new Animated.ValueXY() } this.createPanResponder( props ) } setModalVisible( visible, props ) { const { height, minClosingHeight, openDuration, closeDuration, onClose, onOpen } = this.props const { animatedHeight, pan } = this.state if ( visible ) { this.setState( { modalVisible: visible } ) if ( typeof onOpen === "function" ) onOpen( props ) Animated.timing( animatedHeight, { useNativeDriver: false, toValue: height, duration: openDuration } ).start() } else { Animated.timing( animatedHeight, { useNativeDriver: false, toValue: minClosingHeight, duration: closeDuration } ).start( () => { pan.setValue( { x: 0, y: 0 } ) this.setState( { modalVisible: visible, animatedHeight: new Animated.Value( 0 ) } ) if ( typeof onClose === "function" ) onClose( props ) } ) } } createPanResponder( props ) { const { closeOnDragDown, height } = props const { pan } = this.state this.panResponder = PanResponder.create( { onStartShouldSetPanResponder: () => closeOnDragDown, onPanResponderMove: ( e, gestureState ) => { if ( gestureState.dy > 0 ) { Animated.event( [ null, { dy: pan.y } ], { useNativeDriver: false } )( e, gestureState ) } }, onPanResponderRelease: ( e, gestureState ) => { if ( height / 4 - gestureState.dy < 0 ) { this.setModalVisible( false ) } else { Animated.spring( pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false } ).start() } } } ) } open( props ) { this.setModalVisible( true, props ) } close( props ) { this.setModalVisible( false, props ) } render() { const { animationType, closeOnDragDown, dragFromTopOnly, closeOnPressMask, closeOnPressBack, children, customStyles, keyboardAvoidingViewEnabled } = this.props const { animatedHeight, pan, modalVisible } = this.state const panStyle = { transform: pan.getTranslateTransform() } return ( <Modal transparent animationType={ animationType } visible={ modalVisible } supportedOrientations={ SUPPORTED_ORIENTATIONS } onRequestClose={ () => { if ( closeOnPressBack ) this.setModalVisible( false ) } } > <KeyboardAvoidingView enabled={ keyboardAvoidingViewEnabled } behavior="padding" style={ [ styles.wrapper, customStyles.wrapper ] } > <TouchableOpacity style={ styles.mask } activeOpacity={ 1 } onPress={ () => ( closeOnPressMask ? this.close() : null ) } /> <Animated.View { ...( !dragFromTopOnly && this.panResponder.panHandlers ) } style={ [ panStyle, styles.container, { height: animatedHeight }, customStyles.container ] } > { closeOnDragDown && ( <View { ...( dragFromTopOnly && this.panResponder.panHandlers ) } style={ styles.draggableContainer } > <View style={ [ styles.draggableIcon, customStyles.draggableIcon ] } /> </View> ) } { children } </Animated.View> </KeyboardAvoidingView> </Modal> ) } } RBSheet.propTypes = { animationType: PropTypes.oneOf( [ "none", "slide", "fade" ] ), height: PropTypes.number, minClosingHeight: PropTypes.number, openDuration: PropTypes.number, closeDuration: PropTypes.number, closeOnDragDown: PropTypes.bool, closeOnPressMask: PropTypes.bool, dragFromTopOnly: PropTypes.bool, closeOnPressBack: PropTypes.bool, keyboardAvoidingViewEnabled: PropTypes.bool, customStyles: PropTypes.objectOf( PropTypes.object ), onClose: PropTypes.func, onOpen: PropTypes.func, children: PropTypes.node } RBSheet.defaultProps = { animationType: "none", height: 260, minClosingHeight: 0, openDuration: 300, closeDuration: 200, closeOnDragDown: false, dragFromTopOnly: false, closeOnPressMask: true, closeOnPressBack: true, keyboardAvoidingViewEnabled: Platform.OS === "ios", customStyles: {}, onClose: null, onOpen: null, children: <View /> } export default RBSheet