UNPKG

5.64 kBJavaScriptView Raw
1import * as React from 'react';
2import { Animated, BackHandler, Easing, StyleSheet, TouchableWithoutFeedback, View } from 'react-native';
3import { getStatusBarHeight, getBottomSpace } from 'react-native-iphone-x-helper';
4import Surface from './Surface';
5import { withTheme } from '../core/theming';
6import useAnimatedValue from '../utils/useAnimatedValue';
7import { addEventListener } from '../utils/addEventListener';
8const DEFAULT_DURATION = 220;
9const TOP_INSET = getStatusBarHeight(true);
10const BOTTOM_INSET = getBottomSpace();
11/**
12 * The Modal component is a simple way to present content above an enclosing view.
13 * To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](portal.html) component.
14 *
15 * <div class="screenshots">
16 * <figure>
17 * <img class="medium" src="screenshots/modal.gif" />
18 * </figure>
19 * </div>
20 *
21 * ## Usage
22 * ```js
23 * import * as React from 'react';
24 * import { Modal, Portal, Text, Button, Provider } from 'react-native-paper';
25 *
26 * const MyComponent = () => {
27 * const [visible, setVisible] = React.useState(false);
28 *
29 * const showModal = () => setVisible(true);
30 * const hideModal = () => setVisible(false);
31 * const containerStyle = {backgroundColor: 'white', padding: 20};
32 *
33 * return (
34 * <Provider>
35 * <Portal>
36 * <Modal visible={visible} onDismiss={hideModal} contentContainerStyle={containerStyle}>
37 * <Text>Example Modal. Click outside this area to dismiss.</Text>
38 * </Modal>
39 * </Portal>
40 * <Button style={{marginTop: 30}} onPress={showModal}>
41 * Show
42 * </Button>
43 * </Provider>
44 * );
45 * };
46 *
47 * export default MyComponent;
48 * ```
49 */
50
51function Modal(_ref) {
52 let {
53 dismissable = true,
54 visible = false,
55 overlayAccessibilityLabel = 'Close modal',
56 onDismiss,
57 children,
58 contentContainerStyle,
59 style,
60 theme,
61 testID
62 } = _ref;
63 const visibleRef = React.useRef(visible);
64 React.useEffect(() => {
65 visibleRef.current = visible;
66 });
67 const {
68 colors,
69 animation
70 } = theme;
71 const opacity = useAnimatedValue(visible ? 1 : 0);
72 const [rendered, setRendered] = React.useState(visible);
73
74 if (visible && !rendered) {
75 setRendered(true);
76 }
77
78 const handleBack = () => {
79 if (dismissable) {
80 hideModal();
81 }
82
83 return true;
84 };
85
86 const subscription = React.useRef(undefined);
87
88 const showModal = () => {
89 var _subscription$current;
90
91 (_subscription$current = subscription.current) === null || _subscription$current === void 0 ? void 0 : _subscription$current.remove();
92 subscription.current = addEventListener(BackHandler, 'hardwareBackPress', handleBack);
93 const {
94 scale
95 } = animation;
96 Animated.timing(opacity, {
97 toValue: 1,
98 duration: scale * DEFAULT_DURATION,
99 easing: Easing.out(Easing.cubic),
100 useNativeDriver: true
101 }).start();
102 };
103
104 const removeListeners = () => {
105 var _subscription$current2;
106
107 if ((_subscription$current2 = subscription.current) !== null && _subscription$current2 !== void 0 && _subscription$current2.remove) {
108 var _subscription$current3;
109
110 (_subscription$current3 = subscription.current) === null || _subscription$current3 === void 0 ? void 0 : _subscription$current3.remove();
111 } else {
112 BackHandler.removeEventListener('hardwareBackPress', handleBack);
113 }
114 };
115
116 const hideModal = () => {
117 removeListeners();
118 const {
119 scale
120 } = animation;
121 Animated.timing(opacity, {
122 toValue: 0,
123 duration: scale * DEFAULT_DURATION,
124 easing: Easing.out(Easing.cubic),
125 useNativeDriver: true
126 }).start(_ref2 => {
127 let {
128 finished
129 } = _ref2;
130
131 if (!finished) {
132 return;
133 }
134
135 if (visible && onDismiss) {
136 onDismiss();
137 }
138
139 if (visibleRef.current) {
140 showModal();
141 } else {
142 setRendered(false);
143 }
144 });
145 };
146
147 const prevVisible = React.useRef(null);
148 React.useEffect(() => {
149 if (prevVisible.current !== visible) {
150 if (visible) {
151 showModal();
152 } else {
153 hideModal();
154 }
155 }
156
157 prevVisible.current = visible;
158 });
159 React.useEffect(() => {
160 return removeListeners;
161 }, []);
162 if (!rendered) return null;
163 return /*#__PURE__*/React.createElement(Animated.View, {
164 pointerEvents: visible ? 'auto' : 'none',
165 accessibilityViewIsModal: true,
166 accessibilityLiveRegion: "polite",
167 style: StyleSheet.absoluteFill,
168 onAccessibilityEscape: hideModal,
169 testID: testID
170 }, /*#__PURE__*/React.createElement(TouchableWithoutFeedback, {
171 accessibilityLabel: overlayAccessibilityLabel,
172 accessibilityRole: "button",
173 disabled: !dismissable,
174 onPress: dismissable ? hideModal : undefined,
175 importantForAccessibility: "no"
176 }, /*#__PURE__*/React.createElement(Animated.View, {
177 testID: `${testID}-backdrop`,
178 style: [styles.backdrop, {
179 backgroundColor: colors.backdrop,
180 opacity
181 }]
182 })), /*#__PURE__*/React.createElement(View, {
183 style: [styles.wrapper, {
184 marginTop: TOP_INSET,
185 marginBottom: BOTTOM_INSET
186 }, style],
187 pointerEvents: "box-none"
188 }, /*#__PURE__*/React.createElement(Surface, {
189 style: [{
190 opacity
191 }, styles.content, contentContainerStyle]
192 }, children)));
193}
194
195export default withTheme(Modal);
196const styles = StyleSheet.create({
197 backdrop: {
198 flex: 1
199 },
200 wrapper: { ...StyleSheet.absoluteFillObject,
201 justifyContent: 'center'
202 },
203 content: {
204 backgroundColor: 'transparent',
205 justifyContent: 'center'
206 }
207});
208//# sourceMappingURL=Modal.js.map
\No newline at end of file