aboutsummaryrefslogtreecommitdiff
path: root/src/components/common/BottomDrawer.tsx
blob: 3d9c04716d2a4bba1c1853027aea036992c0a45e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import React, {Fragment, ReactText, useEffect, useRef, useState} from 'react';
import {
  Modal,
  StyleSheet,
  TouchableWithoutFeedback,
  View,
  ViewProps,
} from 'react-native';
import Animated, {interpolateColors, useValue} from 'react-native-reanimated';
import BottomSheet from 'reanimated-bottom-sheet';
import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';

interface BottomDrawerProps extends ViewProps {
  initialSnapPosition?: ReactText;
  isOpen: boolean;
  setIsOpen: (open: boolean) => void;
  showHeader: boolean;
}

// More examples here:
// https://github.com/osdnk/react-native-reanimated-bottom-sheet/tree/master/Example
const BottomDrawer: React.FC<BottomDrawerProps> = (props) => {
  const {isOpen, setIsOpen, showHeader, initialSnapPosition} = props;
  const drawerRef = useRef<BottomSheet>(null);
  const [modalVisible, setModalVisible] = useState(isOpen);
  const bgAlpha = useValue(isOpen ? 1 : 0);

  useEffect(() => {
    if (isOpen) {
      setModalVisible(true);
    } else {
      bgAlpha.setValue(0);
      drawerRef.current && drawerRef.current.snapTo(1);
    }
  }, [isOpen]);

  const renderContent = () => {
    return <View>{props.children}</View>;
  };

  const renderHeader = () => {
    return showHeader ? (
      <View style={styles.header}>
        <View style={styles.panelHeader}>
          <View style={styles.panelHandle} />
        </View>
      </View>
    ) : (
      <Fragment />
    );
  };

  const backgroundColor = interpolateColors(bgAlpha, {
    inputRange: [0, 1],
    outputColorRange: ['rgba(0,0,0,0.3)', 'rgba(0,0,0,0)'],
  });

  return (
    <Modal
      transparent
      visible={modalVisible}
      onShow={() => {
        drawerRef.current && drawerRef.current.snapTo(0);
      }}>
      <BottomSheet
        ref={drawerRef}
        snapPoints={[initialSnapPosition ?? '30%', 0]}
        initialSnap={1}
        renderContent={renderContent}
        renderHeader={renderHeader}
        enabledContentGestureInteraction={false}
        callbackNode={bgAlpha}
        onCloseEnd={() => {
          setModalVisible(false);
          setIsOpen(false);
        }}
      />

      <TouchableWithoutFeedback
        onPress={() => {
          setIsOpen(false);
        }}>
        <Animated.View style={[styles.backgroundView, {backgroundColor}]} />
      </TouchableWithoutFeedback>
    </Modal>
  );
};

const styles = StyleSheet.create({
  header: {
    backgroundColor: '#f7f5eee8',
    shadowColor: '#000000',
    paddingTop: 20,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
  },
  panelHeader: {
    alignItems: 'center',
  },
  panelHandle: {
    width: 40,
    height: 8,
    borderRadius: 4,
    backgroundColor: '#00000040',
    marginBottom: 10,
  },
  backgroundView: {
    height: SCREEN_HEIGHT,
    width: SCREEN_WIDTH,
  },
});

export default BottomDrawer;