UNPKG

react-native-gesture-bottom-sheet

Version:

A simple and easy to use bottom sheet component with drag gestures for React Native

147 lines (137 loc) 3.79 kB
import React, { Component } from "react"; import { View, Modal, TouchableOpacity, Animated, PanResponder, } from "react-native"; import styles from "./styles"; class BottomSheet extends Component { constructor(props) { super(props); this.state = { modalVisible: false, animatedHeight: new Animated.Value(0), pan: new Animated.ValueXY(), }; this.createPanResponder(props); } setModalVisible(visible) { const { closeFunction, height } = this.props; const { animatedHeight, pan } = this.state; if (visible) { this.setState({ modalVisible: visible }); Animated.timing(animatedHeight, { toValue: height, duration: 300, useNativeDriver: false, }).start(); } else { Animated.timing(animatedHeight, { toValue: 0, duration: 400, useNativeDriver: false, }).start(() => { pan.setValue({ x: 0, y: 0 }); this.setState({ modalVisible: visible, animatedHeight: new Animated.Value(0), }); if (typeof closeFunction === "function") closeFunction(); }); } } createPanResponder(props) { const { height } = props; const { pan } = this.state; this.panResponder = PanResponder.create({ onStartShouldSetPanResponder: () => true, onPanResponderMove: (e, gestureState) => { if (gestureState.dy > 0) { Animated.event([null, { dy: pan.y }], { useNativeDriver: false, })(e, gestureState); } }, onPanResponderRelease: (e, gestureState) => { const gestureLimitArea = height / 3; const gestureDistance = gestureState.dy; if (gestureDistance > gestureLimitArea) { this.setModalVisible(false); } else { Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false, }).start(); } }, }); } show() { this.setModalVisible(true); } close() { this.setModalVisible(false); } render() { const { children, hasDraggableIcon, backgroundColor, sheetBackgroundColor, dragIconColor, dragIconStyle, draggable = true, onRequestClose, onClose = () => this.close(), radius, } = this.props; const { animatedHeight, pan, modalVisible } = this.state; const panStyle = { transform: pan.getTranslateTransform(), }; return ( <Modal transparent visible={modalVisible} onRequestClose={onRequestClose}> <View style={[ styles.wrapper, { backgroundColor: backgroundColor || "#25252599" }, ]} > <TouchableOpacity style={styles.background} activeOpacity={1} onPress={onClose} /> <Animated.View {...(draggable && this.panResponder.panHandlers)} style={[ panStyle, styles.container, { height: animatedHeight, borderTopRightRadius: radius || 10, borderTopLeftRadius: radius || 10, backgroundColor: sheetBackgroundColor || "#F3F3F3", }, ]} > {hasDraggableIcon && ( <View style={styles.draggableContainer}> <View style={[ styles.draggableIcon, dragIconStyle, { backgroundColor: dragIconColor || "#A3A3A3", }, ]} /> </View> )} {children} </Animated.View> </View> </Modal> ); } } export default BottomSheet;