@react-navigation/drawer 6.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/lib/commonjs/index.js +104 -0
  4. package/lib/commonjs/index.js.map +1 -0
  5. package/lib/commonjs/navigators/createDrawerNavigator.js +95 -0
  6. package/lib/commonjs/navigators/createDrawerNavigator.js.map +1 -0
  7. package/lib/commonjs/types.js +6 -0
  8. package/lib/commonjs/types.js.map +1 -0
  9. package/lib/commonjs/utils/DrawerGestureContext.js +17 -0
  10. package/lib/commonjs/utils/DrawerGestureContext.js.map +1 -0
  11. package/lib/commonjs/utils/DrawerPositionContext.js +17 -0
  12. package/lib/commonjs/utils/DrawerPositionContext.js.map +1 -0
  13. package/lib/commonjs/utils/DrawerProgressContext.js +17 -0
  14. package/lib/commonjs/utils/DrawerProgressContext.js.map +1 -0
  15. package/lib/commonjs/utils/DrawerStatusContext.js +17 -0
  16. package/lib/commonjs/utils/DrawerStatusContext.js.map +1 -0
  17. package/lib/commonjs/utils/getDrawerStatusFromState.js +18 -0
  18. package/lib/commonjs/utils/getDrawerStatusFromState.js.map +1 -0
  19. package/lib/commonjs/utils/useDrawerProgress.js +27 -0
  20. package/lib/commonjs/utils/useDrawerProgress.js.map +1 -0
  21. package/lib/commonjs/utils/useDrawerStatus.js +31 -0
  22. package/lib/commonjs/utils/useDrawerStatus.js.map +1 -0
  23. package/lib/commonjs/views/DrawerContent.js +42 -0
  24. package/lib/commonjs/views/DrawerContent.js.map +1 -0
  25. package/lib/commonjs/views/DrawerContentScrollView.js +53 -0
  26. package/lib/commonjs/views/DrawerContentScrollView.js.map +1 -0
  27. package/lib/commonjs/views/DrawerItem.js +152 -0
  28. package/lib/commonjs/views/DrawerItem.js.map +1 -0
  29. package/lib/commonjs/views/DrawerItemList.js +70 -0
  30. package/lib/commonjs/views/DrawerItemList.js.map +1 -0
  31. package/lib/commonjs/views/DrawerToggleButton.js +64 -0
  32. package/lib/commonjs/views/DrawerToggleButton.js.map +1 -0
  33. package/lib/commonjs/views/DrawerView.js +282 -0
  34. package/lib/commonjs/views/DrawerView.js.map +1 -0
  35. package/lib/commonjs/views/GestureHandler.android.js +19 -0
  36. package/lib/commonjs/views/GestureHandler.android.js.map +1 -0
  37. package/lib/commonjs/views/GestureHandler.ios.js +19 -0
  38. package/lib/commonjs/views/GestureHandler.ios.js.map +1 -0
  39. package/lib/commonjs/views/GestureHandler.js +35 -0
  40. package/lib/commonjs/views/GestureHandler.js.map +1 -0
  41. package/lib/commonjs/views/GestureHandlerNative.js +50 -0
  42. package/lib/commonjs/views/GestureHandlerNative.js.map +1 -0
  43. package/lib/commonjs/views/ScreenFallback.js +62 -0
  44. package/lib/commonjs/views/ScreenFallback.js.map +1 -0
  45. package/lib/commonjs/views/assets/toggle-drawer-icon.png +0 -0
  46. package/lib/commonjs/views/assets/toggle-drawer-icon@1.5x.android.png +0 -0
  47. package/lib/commonjs/views/assets/toggle-drawer-icon@1.5x.ios.png +0 -0
  48. package/lib/commonjs/views/assets/toggle-drawer-icon@1x.android.png +0 -0
  49. package/lib/commonjs/views/assets/toggle-drawer-icon@1x.ios.png +0 -0
  50. package/lib/commonjs/views/assets/toggle-drawer-icon@2x.android.png +0 -0
  51. package/lib/commonjs/views/assets/toggle-drawer-icon@2x.ios.png +0 -0
  52. package/lib/commonjs/views/assets/toggle-drawer-icon@3x.android.png +0 -0
  53. package/lib/commonjs/views/assets/toggle-drawer-icon@3x.ios.png +0 -0
  54. package/lib/commonjs/views/assets/toggle-drawer-icon@4x.android.png +0 -0
  55. package/lib/commonjs/views/assets/toggle-drawer-icon@4x.ios.png +0 -0
  56. package/lib/commonjs/views/legacy/Drawer.js +455 -0
  57. package/lib/commonjs/views/legacy/Drawer.js.map +1 -0
  58. package/lib/commonjs/views/legacy/Overlay.js +79 -0
  59. package/lib/commonjs/views/legacy/Overlay.js.map +1 -0
  60. package/lib/commonjs/views/modern/Drawer.js +293 -0
  61. package/lib/commonjs/views/modern/Drawer.js.map +1 -0
  62. package/lib/commonjs/views/modern/Overlay.js +65 -0
  63. package/lib/commonjs/views/modern/Overlay.js.map +1 -0
  64. package/lib/module/index.js +27 -0
  65. package/lib/module/index.js.map +1 -0
  66. package/lib/module/navigators/createDrawerNavigator.js +77 -0
  67. package/lib/module/navigators/createDrawerNavigator.js.map +1 -0
  68. package/lib/module/types.js +2 -0
  69. package/lib/module/types.js.map +1 -0
  70. package/lib/module/utils/DrawerGestureContext.js +3 -0
  71. package/lib/module/utils/DrawerGestureContext.js.map +1 -0
  72. package/lib/module/utils/DrawerPositionContext.js +3 -0
  73. package/lib/module/utils/DrawerPositionContext.js.map +1 -0
  74. package/lib/module/utils/DrawerProgressContext.js +3 -0
  75. package/lib/module/utils/DrawerProgressContext.js.map +1 -0
  76. package/lib/module/utils/DrawerStatusContext.js +4 -0
  77. package/lib/module/utils/DrawerStatusContext.js.map +1 -0
  78. package/lib/module/utils/getDrawerStatusFromState.js +11 -0
  79. package/lib/module/utils/getDrawerStatusFromState.js.map +1 -0
  80. package/lib/module/utils/useDrawerProgress.js +12 -0
  81. package/lib/module/utils/useDrawerProgress.js.map +1 -0
  82. package/lib/module/utils/useDrawerStatus.js +17 -0
  83. package/lib/module/utils/useDrawerStatus.js.map +1 -0
  84. package/lib/module/views/DrawerContent.js +26 -0
  85. package/lib/module/views/DrawerContent.js.map +1 -0
  86. package/lib/module/views/DrawerContentScrollView.js +34 -0
  87. package/lib/module/views/DrawerContentScrollView.js.map +1 -0
  88. package/lib/module/views/DrawerItem.js +132 -0
  89. package/lib/module/views/DrawerItem.js.map +1 -0
  90. package/lib/module/views/DrawerItemList.js +55 -0
  91. package/lib/module/views/DrawerItemList.js.map +1 -0
  92. package/lib/module/views/DrawerToggleButton.js +48 -0
  93. package/lib/module/views/DrawerToggleButton.js.map +1 -0
  94. package/lib/module/views/DrawerView.js +256 -0
  95. package/lib/module/views/DrawerView.js.map +1 -0
  96. package/lib/module/views/GestureHandler.android.js +2 -0
  97. package/lib/module/views/GestureHandler.android.js.map +1 -0
  98. package/lib/module/views/GestureHandler.ios.js +2 -0
  99. package/lib/module/views/GestureHandler.ios.js.map +1 -0
  100. package/lib/module/views/GestureHandler.js +19 -0
  101. package/lib/module/views/GestureHandler.js.map +1 -0
  102. package/lib/module/views/GestureHandlerNative.js +11 -0
  103. package/lib/module/views/GestureHandlerNative.js.map +1 -0
  104. package/lib/module/views/ScreenFallback.js +44 -0
  105. package/lib/module/views/ScreenFallback.js.map +1 -0
  106. package/lib/module/views/assets/toggle-drawer-icon.png +0 -0
  107. package/lib/module/views/assets/toggle-drawer-icon@1.5x.android.png +0 -0
  108. package/lib/module/views/assets/toggle-drawer-icon@1.5x.ios.png +0 -0
  109. package/lib/module/views/assets/toggle-drawer-icon@1x.android.png +0 -0
  110. package/lib/module/views/assets/toggle-drawer-icon@1x.ios.png +0 -0
  111. package/lib/module/views/assets/toggle-drawer-icon@2x.android.png +0 -0
  112. package/lib/module/views/assets/toggle-drawer-icon@2x.ios.png +0 -0
  113. package/lib/module/views/assets/toggle-drawer-icon@3x.android.png +0 -0
  114. package/lib/module/views/assets/toggle-drawer-icon@3x.ios.png +0 -0
  115. package/lib/module/views/assets/toggle-drawer-icon@4x.android.png +0 -0
  116. package/lib/module/views/assets/toggle-drawer-icon@4x.ios.png +0 -0
  117. package/lib/module/views/legacy/Drawer.js +430 -0
  118. package/lib/module/views/legacy/Drawer.js.map +1 -0
  119. package/lib/module/views/legacy/Overlay.js +59 -0
  120. package/lib/module/views/legacy/Overlay.js.map +1 -0
  121. package/lib/module/views/modern/Drawer.js +272 -0
  122. package/lib/module/views/modern/Drawer.js.map +1 -0
  123. package/lib/module/views/modern/Overlay.js +47 -0
  124. package/lib/module/views/modern/Overlay.js.map +1 -0
  125. package/lib/typescript/src/index.d.ts +25 -0
  126. package/lib/typescript/src/navigators/createDrawerNavigator.d.ts +7 -0
  127. package/lib/typescript/src/types.d.ts +235 -0
  128. package/lib/typescript/src/utils/DrawerGestureContext.d.ts +3 -0
  129. package/lib/typescript/src/utils/DrawerPositionContext.d.ts +3 -0
  130. package/lib/typescript/src/utils/DrawerProgressContext.d.ts +4 -0
  131. package/lib/typescript/src/utils/DrawerStatusContext.d.ts +3 -0
  132. package/lib/typescript/src/utils/getDrawerStatusFromState.d.ts +2 -0
  133. package/lib/typescript/src/utils/useDrawerProgress.d.ts +2 -0
  134. package/lib/typescript/src/utils/useDrawerStatus.d.ts +5 -0
  135. package/lib/typescript/src/views/DrawerContent.d.ts +3 -0
  136. package/lib/typescript/src/views/DrawerContentScrollView.d.ts +6 -0
  137. package/lib/typescript/src/views/DrawerItem.d.ts +74 -0
  138. package/lib/typescript/src/views/DrawerItemList.d.ts +13 -0
  139. package/lib/typescript/src/views/DrawerToggleButton.d.ts +9 -0
  140. package/lib/typescript/src/views/DrawerView.d.ts +10 -0
  141. package/lib/typescript/src/views/GestureHandler.android.d.ts +1 -0
  142. package/lib/typescript/src/views/GestureHandler.d.ts +14 -0
  143. package/lib/typescript/src/views/GestureHandler.ios.d.ts +1 -0
  144. package/lib/typescript/src/views/GestureHandlerNative.d.ts +4 -0
  145. package/lib/typescript/src/views/ScreenFallback.d.ts +14 -0
  146. package/lib/typescript/src/views/legacy/Drawer.d.ts +44 -0
  147. package/lib/typescript/src/views/legacy/Overlay.d.ts +68 -0
  148. package/lib/typescript/src/views/modern/Drawer.d.ts +3 -0
  149. package/lib/typescript/src/views/modern/Overlay.d.ts +68 -0
  150. package/package.json +87 -0
  151. package/src/index.tsx +34 -0
  152. package/src/navigators/createDrawerNavigator.tsx +134 -0
  153. package/src/types.tsx +300 -0
  154. package/src/utils/DrawerGestureContext.tsx +3 -0
  155. package/src/utils/DrawerPositionContext.tsx +3 -0
  156. package/src/utils/DrawerProgressContext.tsx +6 -0
  157. package/src/utils/DrawerStatusContext.tsx +6 -0
  158. package/src/utils/getDrawerStatusFromState.tsx +20 -0
  159. package/src/utils/useDrawerProgress.tsx +18 -0
  160. package/src/utils/useDrawerStatus.tsx +19 -0
  161. package/src/views/DrawerContent.tsx +27 -0
  162. package/src/views/DrawerContentScrollView.tsx +52 -0
  163. package/src/views/DrawerItem.tsx +227 -0
  164. package/src/views/DrawerItemList.tsx +80 -0
  165. package/src/views/DrawerToggleButton.tsx +54 -0
  166. package/src/views/DrawerView.tsx +313 -0
  167. package/src/views/GestureHandler.android.tsx +1 -0
  168. package/src/views/GestureHandler.ios.tsx +1 -0
  169. package/src/views/GestureHandler.tsx +29 -0
  170. package/src/views/GestureHandlerNative.tsx +24 -0
  171. package/src/views/ScreenFallback.tsx +48 -0
  172. package/src/views/assets/toggle-drawer-icon.png +0 -0
  173. package/src/views/assets/toggle-drawer-icon@1.5x.android.png +0 -0
  174. package/src/views/assets/toggle-drawer-icon@1.5x.ios.png +0 -0
  175. package/src/views/assets/toggle-drawer-icon@1x.android.png +0 -0
  176. package/src/views/assets/toggle-drawer-icon@1x.ios.png +0 -0
  177. package/src/views/assets/toggle-drawer-icon@2x.android.png +0 -0
  178. package/src/views/assets/toggle-drawer-icon@2x.ios.png +0 -0
  179. package/src/views/assets/toggle-drawer-icon@3x.android.png +0 -0
  180. package/src/views/assets/toggle-drawer-icon@3x.ios.png +0 -0
  181. package/src/views/assets/toggle-drawer-icon@4x.android.png +0 -0
  182. package/src/views/assets/toggle-drawer-icon@4x.ios.png +0 -0
  183. package/src/views/legacy/Drawer.tsx +659 -0
  184. package/src/views/legacy/Overlay.tsx +74 -0
  185. package/src/views/modern/Drawer.tsx +385 -0
  186. package/src/views/modern/Overlay.tsx +56 -0
@@ -0,0 +1,74 @@
1
+ import * as React from 'react';
2
+ import { Platform, Pressable, StyleSheet } from 'react-native';
3
+ import Animated from 'react-native-reanimated';
4
+
5
+ const {
6
+ interpolate: interpolateDeprecated,
7
+ interpolateNode,
8
+ cond,
9
+ greaterThan,
10
+ } = Animated;
11
+
12
+ const interpolate: typeof interpolateNode =
13
+ interpolateNode ?? interpolateDeprecated;
14
+
15
+ const PROGRESS_EPSILON = 0.05;
16
+
17
+ type Props = React.ComponentProps<typeof Animated.View> & {
18
+ progress: Animated.Node<number>;
19
+ onPress: () => void;
20
+ };
21
+
22
+ const Overlay = React.forwardRef(function Overlay(
23
+ { progress, onPress, style, ...props }: Props,
24
+ ref: React.Ref<Animated.View>
25
+ ) {
26
+ const animatedStyle = {
27
+ opacity: interpolate(progress, {
28
+ // Default input range is [PROGRESS_EPSILON, 1]
29
+ // On Windows, the output value is 1 when input value is out of range for some reason
30
+ // The default value 0 will be interpolated to 1 in this case, which is not what we want.
31
+ // Therefore changing input range on Windows to [0,1] instead.
32
+ inputRange:
33
+ Platform.OS === 'windows' || Platform.OS === 'macos'
34
+ ? [0, 1]
35
+ : [PROGRESS_EPSILON, 1],
36
+ outputRange: [0, 1],
37
+ }),
38
+ // We don't want the user to be able to press through the overlay when drawer is open
39
+ // One approach is to adjust the pointerEvents based on the progress
40
+ // But we can also send the overlay behind the screen, which works, and is much less code
41
+ zIndex: cond(greaterThan(progress, PROGRESS_EPSILON), 0, -1),
42
+ };
43
+
44
+ return (
45
+ <Animated.View
46
+ {...props}
47
+ ref={ref}
48
+ style={[styles.overlay, overlayStyle, animatedStyle, style]}
49
+ >
50
+ <Pressable onPress={onPress} style={styles.pressable} />
51
+ </Animated.View>
52
+ );
53
+ });
54
+
55
+ const overlayStyle = Platform.select<Record<string, string>>({
56
+ web: {
57
+ // Disable touch highlight on mobile Safari.
58
+ // WebkitTapHighlightColor must be used outside of StyleSheet.create because react-native-web will omit the property.
59
+ WebkitTapHighlightColor: 'transparent',
60
+ },
61
+ default: {},
62
+ });
63
+
64
+ const styles = StyleSheet.create({
65
+ overlay: {
66
+ ...StyleSheet.absoluteFillObject,
67
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
68
+ },
69
+ pressable: {
70
+ flex: 1,
71
+ },
72
+ });
73
+
74
+ export default Overlay;
@@ -0,0 +1,385 @@
1
+ import * as React from 'react';
2
+ import {
3
+ InteractionManager,
4
+ Keyboard,
5
+ Platform,
6
+ StatusBar,
7
+ StyleSheet,
8
+ View,
9
+ } from 'react-native';
10
+ import {
11
+ PanGestureHandler,
12
+ PanGestureHandlerGestureEvent,
13
+ State as GestureState,
14
+ } from 'react-native-gesture-handler';
15
+ import Animated, {
16
+ interpolate,
17
+ runOnJS,
18
+ useAnimatedGestureHandler,
19
+ useAnimatedStyle,
20
+ useDerivedValue,
21
+ useSharedValue,
22
+ withSpring,
23
+ } from 'react-native-reanimated';
24
+
25
+ import type { DrawerProps } from '../../types';
26
+ import DrawerProgressContext from '../../utils/DrawerProgressContext';
27
+ import Overlay from './Overlay';
28
+
29
+ const SWIPE_DISTANCE_MINIMUM = 5;
30
+ const DEFAULT_DRAWER_WIDTH = '80%';
31
+
32
+ const minmax = (value: number, start: number, end: number) => {
33
+ 'worklet';
34
+
35
+ return Math.min(Math.max(value, start), end);
36
+ };
37
+
38
+ export default function Drawer({
39
+ dimensions,
40
+ drawerPosition,
41
+ drawerStyle,
42
+ drawerType,
43
+ gestureHandlerProps,
44
+ hideStatusBarOnOpen,
45
+ keyboardDismissMode,
46
+ onClose,
47
+ onOpen,
48
+ open,
49
+ overlayStyle,
50
+ renderDrawerContent,
51
+ renderSceneContent,
52
+ statusBarAnimation,
53
+ swipeDistanceThreshold,
54
+ swipeEdgeWidth,
55
+ swipeEnabled,
56
+ swipeVelocityThreshold,
57
+ }: DrawerProps) {
58
+ const getDrawerWidth = (): number => {
59
+ const { width = DEFAULT_DRAWER_WIDTH } =
60
+ StyleSheet.flatten(drawerStyle) || {};
61
+
62
+ if (typeof width === 'string' && width.endsWith('%')) {
63
+ // Try to calculate width if a percentage is given
64
+ const percentage = Number(width.replace(/%$/, ''));
65
+
66
+ if (Number.isFinite(percentage)) {
67
+ return dimensions.width * (percentage / 100);
68
+ }
69
+ }
70
+
71
+ return typeof width === 'number' ? width : 0;
72
+ };
73
+
74
+ const drawerWidth = getDrawerWidth();
75
+
76
+ const isOpen = drawerType === 'permanent' ? true : open;
77
+ const isRight = drawerPosition === 'right';
78
+
79
+ const getDrawerTranslationX = React.useCallback(
80
+ (open: boolean) => {
81
+ 'worklet';
82
+
83
+ if (drawerPosition === 'left') {
84
+ return open ? 0 : -drawerWidth;
85
+ }
86
+
87
+ return open ? 0 : drawerWidth;
88
+ },
89
+ [drawerPosition, drawerWidth]
90
+ );
91
+
92
+ const hideStatusBar = React.useCallback(
93
+ (hide: boolean) => {
94
+ if (hideStatusBarOnOpen) {
95
+ StatusBar.setHidden(hide, statusBarAnimation);
96
+ }
97
+ },
98
+ [hideStatusBarOnOpen, statusBarAnimation]
99
+ );
100
+
101
+ React.useEffect(() => {
102
+ hideStatusBar(isOpen);
103
+
104
+ return () => hideStatusBar(false);
105
+ }, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]);
106
+
107
+ const interactionHandleRef = React.useRef<number | null>(null);
108
+
109
+ const startInteraction = () => {
110
+ interactionHandleRef.current = InteractionManager.createInteractionHandle();
111
+ };
112
+
113
+ const endInteraction = () => {
114
+ if (interactionHandleRef.current != null) {
115
+ InteractionManager.clearInteractionHandle(interactionHandleRef.current);
116
+ interactionHandleRef.current = null;
117
+ }
118
+ };
119
+
120
+ const hideKeyboard = () => {
121
+ if (keyboardDismissMode === 'on-drag') {
122
+ Keyboard.dismiss();
123
+ }
124
+ };
125
+
126
+ const onGestureStart = () => {
127
+ startInteraction();
128
+ hideKeyboard();
129
+ hideStatusBar(true);
130
+ };
131
+
132
+ const onGestureEnd = () => {
133
+ endInteraction();
134
+ };
135
+
136
+ // FIXME: Currently hitSlop is broken when on Android when drawer is on right
137
+ // https://github.com/software-mansion/react-native-gesture-handler/issues/569
138
+ const hitSlop = isRight
139
+ ? // Extend hitSlop to the side of the screen when drawer is closed
140
+ // This lets the user drag the drawer from the side of the screen
141
+ { right: 0, width: isOpen ? undefined : swipeEdgeWidth }
142
+ : { left: 0, width: isOpen ? undefined : swipeEdgeWidth };
143
+
144
+ const touchStartX = useSharedValue(0);
145
+ const touchX = useSharedValue(0);
146
+ const translationX = useSharedValue(getDrawerTranslationX(open));
147
+ const gestureState = useSharedValue<GestureState>(GestureState.UNDETERMINED);
148
+
149
+ const toggleDrawer = React.useCallback(
150
+ (open: boolean, velocity?: number) => {
151
+ 'worklet';
152
+
153
+ const translateX = getDrawerTranslationX(open);
154
+
155
+ touchStartX.value = 0;
156
+ touchX.value = 0;
157
+ translationX.value = withSpring(
158
+ translateX,
159
+ {
160
+ velocity,
161
+ stiffness: 1000,
162
+ damping: 500,
163
+ mass: 3,
164
+ overshootClamping: true,
165
+ restDisplacementThreshold: 0.01,
166
+ restSpeedThreshold: 0.01,
167
+ },
168
+ () => {
169
+ if (translationX.value === getDrawerTranslationX(true)) {
170
+ runOnJS(onOpen)();
171
+ } else if (translationX.value === getDrawerTranslationX(false)) {
172
+ runOnJS(onClose)();
173
+ }
174
+ }
175
+ );
176
+ },
177
+ [getDrawerTranslationX, onClose, onOpen, touchStartX, touchX, translationX]
178
+ );
179
+
180
+ React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);
181
+
182
+ const onGestureEvent = useAnimatedGestureHandler<
183
+ PanGestureHandlerGestureEvent,
184
+ { startX: number }
185
+ >({
186
+ onStart: (event, ctx) => {
187
+ ctx.startX = translationX.value;
188
+ gestureState.value = event.state;
189
+ touchStartX.value = event.x;
190
+
191
+ runOnJS(onGestureStart)();
192
+ },
193
+ onActive: (event, ctx) => {
194
+ touchX.value = event.x;
195
+ translationX.value = ctx.startX + event.translationX;
196
+ gestureState.value = event.state;
197
+ },
198
+ onEnd: (event) => {
199
+ gestureState.value = event.state;
200
+
201
+ const nextOpen =
202
+ (Math.abs(event.translationX) > SWIPE_DISTANCE_MINIMUM &&
203
+ Math.abs(event.translationX) > swipeVelocityThreshold) ||
204
+ Math.abs(event.translationX) > swipeDistanceThreshold
205
+ ? drawerPosition === 'left'
206
+ ? // If swiped to right, open the drawer, otherwise close it
207
+ (event.velocityX === 0 ? event.translationX : event.velocityX) > 0
208
+ : // If swiped to left, open the drawer, otherwise close it
209
+ (event.velocityX === 0 ? event.translationX : event.velocityX) < 0
210
+ : open;
211
+
212
+ toggleDrawer(nextOpen, event.velocityX);
213
+ runOnJS(onGestureEnd)();
214
+ },
215
+ });
216
+
217
+ const translateX = useDerivedValue(() => {
218
+ // Comment stolen from react-native-gesture-handler/DrawerLayout
219
+ //
220
+ // While closing the drawer when user starts gesture outside of its area (in greyed
221
+ // out part of the window), we want the drawer to follow only once finger reaches the
222
+ // edge of the drawer.
223
+ // E.g. on the diagram below drawer is illustrate by X signs and the greyed out area by
224
+ // dots. The touch gesture starts at '*' and moves left, touch path is indicated by
225
+ // an arrow pointing left
226
+ // 1) +---------------+ 2) +---------------+ 3) +---------------+ 4) +---------------+
227
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
228
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
229
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
230
+ // |XXXXXXXX|......| |XXXXXXXX|.<-*..| |XXXXXXXX|<--*..| |XXXXX|<-----*..|
231
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
232
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
233
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
234
+ // +---------------+ +---------------+ +---------------+ +---------------+
235
+ //
236
+ // For the above to work properly we define animated value that will keep start position
237
+ // of the gesture. Then we use that value to calculate how much we need to subtract from
238
+ // the translationX. If the gesture started on the greyed out area we take the distance from the
239
+ // edge of the drawer to the start position. Otherwise we don't subtract at all and the
240
+ // drawer be pulled back as soon as you start the pan.
241
+ //
242
+ // This is used only when drawerType is "front"
243
+ const touchDistance =
244
+ drawerType === 'front' && gestureState.value === GestureState.ACTIVE
245
+ ? minmax(
246
+ drawerPosition === 'left'
247
+ ? touchStartX.value - drawerWidth
248
+ : dimensions.width - drawerWidth - touchStartX.value,
249
+ 0,
250
+ dimensions.width
251
+ )
252
+ : 0;
253
+
254
+ const translateX =
255
+ drawerPosition === 'left'
256
+ ? minmax(translationX.value + touchDistance, -drawerWidth, 0)
257
+ : minmax(translationX.value - touchDistance, 0, drawerWidth);
258
+
259
+ return translateX;
260
+ });
261
+
262
+ const drawerAnimatedStyle = useAnimatedStyle(() => {
263
+ if (drawerType === 'permanent') {
264
+ return {};
265
+ }
266
+
267
+ return {
268
+ transform: [
269
+ {
270
+ translateX: drawerType === 'back' ? 0 : translateX.value,
271
+ },
272
+ ],
273
+ };
274
+ });
275
+
276
+ const contentAnimatedStyle = useAnimatedStyle(() => {
277
+ if (drawerType === 'permanent') {
278
+ return {};
279
+ }
280
+
281
+ return {
282
+ transform: [
283
+ {
284
+ translateX:
285
+ drawerType === 'front'
286
+ ? 0
287
+ : drawerPosition === 'left'
288
+ ? drawerWidth + translateX.value
289
+ : translateX.value - drawerWidth,
290
+ },
291
+ ],
292
+ };
293
+ });
294
+
295
+ const progress = useDerivedValue(() => {
296
+ return drawerType === 'permanent'
297
+ ? 1
298
+ : interpolate(
299
+ translateX.value,
300
+ [getDrawerTranslationX(false), getDrawerTranslationX(true)],
301
+ [0, 1]
302
+ );
303
+ });
304
+
305
+ return (
306
+ <DrawerProgressContext.Provider value={progress}>
307
+ <PanGestureHandler
308
+ activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
309
+ failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
310
+ hitSlop={hitSlop}
311
+ enabled={drawerType !== 'permanent' && swipeEnabled}
312
+ onGestureEvent={onGestureEvent}
313
+ {...gestureHandlerProps}
314
+ >
315
+ {/* Immediate child of gesture handler needs to be an Animated.View */}
316
+ <Animated.View
317
+ style={[
318
+ styles.main,
319
+ {
320
+ flexDirection:
321
+ drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
322
+ },
323
+ ]}
324
+ >
325
+ <Animated.View style={[styles.content, contentAnimatedStyle]}>
326
+ <View
327
+ accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
328
+ importantForAccessibility={
329
+ isOpen && drawerType !== 'permanent'
330
+ ? 'no-hide-descendants'
331
+ : 'auto'
332
+ }
333
+ style={styles.content}
334
+ >
335
+ {renderSceneContent()}
336
+ </View>
337
+ {drawerType !== 'permanent' ? (
338
+ <Overlay
339
+ progress={progress}
340
+ onPress={() => toggleDrawer(false)}
341
+ style={overlayStyle}
342
+ />
343
+ ) : null}
344
+ </Animated.View>
345
+ <Animated.View
346
+ accessibilityViewIsModal={isOpen && drawerType !== 'permanent'}
347
+ removeClippedSubviews={Platform.OS !== 'ios'}
348
+ style={[
349
+ styles.container,
350
+ {
351
+ position: drawerType === 'permanent' ? 'relative' : 'absolute',
352
+ zIndex: drawerType === 'back' ? -1 : 0,
353
+ },
354
+ drawerAnimatedStyle,
355
+ drawerStyle as any,
356
+ ]}
357
+ >
358
+ {renderDrawerContent()}
359
+ </Animated.View>
360
+ </Animated.View>
361
+ </PanGestureHandler>
362
+ </DrawerProgressContext.Provider>
363
+ );
364
+ }
365
+
366
+ const styles = StyleSheet.create({
367
+ container: {
368
+ top: 0,
369
+ bottom: 0,
370
+ maxWidth: '100%',
371
+ width: DEFAULT_DRAWER_WIDTH,
372
+ },
373
+ content: {
374
+ flex: 1,
375
+ },
376
+ main: {
377
+ flex: 1,
378
+ ...Platform.select({
379
+ // FIXME: We need to hide `overflowX` on Web so the translated content doesn't show offscreen.
380
+ // But adding `overflowX: 'hidden'` prevents content from collapsing the URL bar.
381
+ web: null,
382
+ default: { overflow: 'hidden' },
383
+ }),
384
+ },
385
+ });
@@ -0,0 +1,56 @@
1
+ import * as React from 'react';
2
+ import { Platform, Pressable, StyleSheet } from 'react-native';
3
+ import Animated, { useAnimatedStyle } from 'react-native-reanimated';
4
+
5
+ const PROGRESS_EPSILON = 0.05;
6
+
7
+ type Props = React.ComponentProps<typeof Animated.View> & {
8
+ progress: Animated.SharedValue<number>;
9
+ onPress: () => void;
10
+ };
11
+
12
+ const Overlay = React.forwardRef(function Overlay(
13
+ { progress, onPress, style, ...props }: Props,
14
+ ref: React.Ref<Animated.View>
15
+ ) {
16
+ const animatedStyle = useAnimatedStyle(() => {
17
+ return {
18
+ opacity: progress.value,
19
+ // We don't want the user to be able to press through the overlay when drawer is open
20
+ // One approach is to adjust the pointerEvents based on the progress
21
+ // But we can also send the overlay behind the screen
22
+ zIndex: progress.value > PROGRESS_EPSILON ? 0 : -1,
23
+ };
24
+ });
25
+
26
+ return (
27
+ <Animated.View
28
+ {...props}
29
+ ref={ref}
30
+ style={[styles.overlay, overlayStyle, animatedStyle, style]}
31
+ >
32
+ <Pressable onPress={onPress} style={styles.pressable} />
33
+ </Animated.View>
34
+ );
35
+ });
36
+
37
+ const overlayStyle = Platform.select<Record<string, string>>({
38
+ web: {
39
+ // Disable touch highlight on mobile Safari.
40
+ // WebkitTapHighlightColor must be used outside of StyleSheet.create because react-native-web will omit the property.
41
+ WebkitTapHighlightColor: 'transparent',
42
+ },
43
+ default: {},
44
+ });
45
+
46
+ const styles = StyleSheet.create({
47
+ overlay: {
48
+ ...StyleSheet.absoluteFillObject,
49
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
50
+ },
51
+ pressable: {
52
+ flex: 1,
53
+ },
54
+ });
55
+
56
+ export default Overlay;