react-native-gesture-handler 2.20.1 → 2.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. package/RNGestureHandler.podspec +9 -0
  2. package/android/build.gradle +19 -0
  3. package/android/paper/src/main/java/com/swmansion/gesturehandler/ReactContextExtensions.kt +1 -1
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +6 -2
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/RotationGestureDetector.kt +53 -21
  6. package/apple/Handlers/RNPanHandler.m +5 -1
  7. package/apple/RNGestureHandlerButton.h +17 -1
  8. package/apple/{RNGestureHandlerButton.m → RNGestureHandlerButton.mm} +64 -0
  9. package/apple/RNGestureHandlerButtonComponentView.h +4 -0
  10. package/apple/RNGestureHandlerButtonComponentView.mm +12 -3
  11. package/apple/{RNGestureHandlerButtonManager.m → RNGestureHandlerButtonManager.mm} +1 -1
  12. package/apple/RNGestureHandlerPointerTracker.m +4 -2
  13. package/lib/commonjs/RNGestureHandlerModule.web.js +15 -2
  14. package/lib/commonjs/RNGestureHandlerModule.web.js.map +1 -1
  15. package/lib/commonjs/components/Pressable/Pressable.js +2 -6
  16. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  17. package/lib/commonjs/components/ReanimatedDrawerLayout.js +389 -0
  18. package/lib/commonjs/components/ReanimatedDrawerLayout.js.map +1 -0
  19. package/lib/commonjs/components/ReanimatedSwipeable.js +117 -148
  20. package/lib/commonjs/components/ReanimatedSwipeable.js.map +1 -1
  21. package/lib/commonjs/findNodeHandle.js +12 -0
  22. package/lib/commonjs/findNodeHandle.js.map +1 -0
  23. package/lib/commonjs/findNodeHandle.web.js +40 -0
  24. package/lib/commonjs/findNodeHandle.web.js.map +1 -0
  25. package/lib/commonjs/handlers/createHandler.js +4 -2
  26. package/lib/commonjs/handlers/createHandler.js.map +1 -1
  27. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js +51 -0
  28. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
  29. package/lib/commonjs/handlers/gestures/GestureDetector/index.js +3 -1
  30. package/lib/commonjs/handlers/gestures/GestureDetector/index.js.map +1 -1
  31. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js +5 -3
  32. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  33. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js +4 -2
  34. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  35. package/lib/commonjs/web/handlers/GestureHandler.js +4 -0
  36. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  37. package/lib/commonjs/web/handlers/PanGestureHandler.js +59 -0
  38. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  39. package/lib/commonjs/web/interfaces.js +10 -1
  40. package/lib/commonjs/web/interfaces.js.map +1 -1
  41. package/lib/commonjs/web/tools/EventManager.js +6 -0
  42. package/lib/commonjs/web/tools/EventManager.js.map +1 -1
  43. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js +1 -3
  44. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  45. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +5 -2
  46. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  47. package/lib/commonjs/web/tools/KeyboardEventManager.js +2 -2
  48. package/lib/commonjs/web/tools/KeyboardEventManager.js.map +1 -1
  49. package/lib/commonjs/web/tools/PointerTracker.js +6 -30
  50. package/lib/commonjs/web/tools/PointerTracker.js.map +1 -1
  51. package/lib/commonjs/web/tools/WheelEventManager.js +74 -0
  52. package/lib/commonjs/web/tools/WheelEventManager.js.map +1 -0
  53. package/lib/commonjs/web/utils.js +16 -0
  54. package/lib/commonjs/web/utils.js.map +1 -1
  55. package/lib/module/RNGestureHandlerModule.web.js +16 -3
  56. package/lib/module/RNGestureHandlerModule.web.js.map +1 -1
  57. package/lib/module/components/Pressable/Pressable.js +2 -6
  58. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  59. package/lib/module/components/ReanimatedDrawerLayout.js +365 -0
  60. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -0
  61. package/lib/module/components/ReanimatedSwipeable.js +119 -145
  62. package/lib/module/components/ReanimatedSwipeable.js.map +1 -1
  63. package/lib/module/findNodeHandle.js +3 -0
  64. package/lib/module/findNodeHandle.js.map +1 -0
  65. package/lib/module/findNodeHandle.web.js +32 -0
  66. package/lib/module/findNodeHandle.web.js.map +1 -0
  67. package/lib/module/handlers/createHandler.js +2 -1
  68. package/lib/module/handlers/createHandler.js.map +1 -1
  69. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js +34 -0
  70. package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
  71. package/lib/module/handlers/gestures/GestureDetector/index.js +2 -1
  72. package/lib/module/handlers/gestures/GestureDetector/index.js.map +1 -1
  73. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js +2 -2
  74. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  75. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js +1 -1
  76. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  77. package/lib/module/web/handlers/GestureHandler.js +4 -0
  78. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  79. package/lib/module/web/handlers/PanGestureHandler.js +58 -0
  80. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  81. package/lib/module/web/interfaces.js +8 -0
  82. package/lib/module/web/interfaces.js.map +1 -1
  83. package/lib/module/web/tools/EventManager.js +6 -0
  84. package/lib/module/web/tools/EventManager.js.map +1 -1
  85. package/lib/module/web/tools/GestureHandlerOrchestrator.js +1 -3
  86. package/lib/module/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  87. package/lib/module/web/tools/GestureHandlerWebDelegate.js +3 -1
  88. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  89. package/lib/module/web/tools/KeyboardEventManager.js +2 -2
  90. package/lib/module/web/tools/KeyboardEventManager.js.map +1 -1
  91. package/lib/module/web/tools/PointerTracker.js +6 -30
  92. package/lib/module/web/tools/PointerTracker.js.map +1 -1
  93. package/lib/module/web/tools/WheelEventManager.js +60 -0
  94. package/lib/module/web/tools/WheelEventManager.js.map +1 -0
  95. package/lib/module/web/utils.js +15 -0
  96. package/lib/module/web/utils.js.map +1 -1
  97. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +162 -0
  98. package/lib/typescript/components/ReanimatedSwipeable.d.ts +22 -16
  99. package/lib/typescript/findNodeHandle.d.ts +2 -0
  100. package/lib/typescript/findNodeHandle.web.d.ts +2 -0
  101. package/lib/typescript/handlers/gestures/GestureDetector/Wrap.web.d.ts +7 -0
  102. package/lib/typescript/web/handlers/GestureHandler.d.ts +2 -1
  103. package/lib/typescript/web/handlers/PanGestureHandler.d.ts +5 -0
  104. package/lib/typescript/web/interfaces.d.ts +16 -0
  105. package/lib/typescript/web/tools/EventManager.d.ts +2 -0
  106. package/lib/typescript/web/tools/PointerTracker.d.ts +2 -8
  107. package/lib/typescript/web/tools/WheelEventManager.d.ts +11 -0
  108. package/lib/typescript/web/utils.d.ts +2 -1
  109. package/package.json +2 -2
  110. package/src/RNGestureHandlerModule.web.ts +23 -4
  111. package/src/components/Pressable/Pressable.tsx +2 -6
  112. package/src/components/ReanimatedDrawerLayout.tsx +741 -0
  113. package/src/components/ReanimatedSwipeable.tsx +361 -305
  114. package/src/findNodeHandle.ts +3 -0
  115. package/src/findNodeHandle.web.ts +35 -0
  116. package/src/handlers/createHandler.tsx +2 -1
  117. package/src/handlers/gestures/GestureDetector/Wrap.web.tsx +44 -0
  118. package/src/handlers/gestures/GestureDetector/index.tsx +2 -1
  119. package/src/handlers/gestures/GestureDetector/useDetectorUpdater.ts +1 -1
  120. package/src/handlers/gestures/GestureDetector/useViewRefHandler.ts +1 -1
  121. package/src/web/handlers/GestureHandler.ts +5 -1
  122. package/src/web/handlers/PanGestureHandler.ts +69 -1
  123. package/src/web/interfaces.ts +17 -0
  124. package/src/web/tools/EventManager.ts +4 -0
  125. package/src/web/tools/GestureHandlerOrchestrator.ts +1 -7
  126. package/src/web/tools/GestureHandlerWebDelegate.ts +3 -1
  127. package/src/web/tools/KeyboardEventManager.ts +2 -2
  128. package/src/web/tools/PointerTracker.ts +6 -28
  129. package/src/web/tools/WheelEventManager.ts +48 -0
  130. package/src/web/utils.ts +47 -1
@@ -0,0 +1,741 @@
1
+ // This component is based on RN's DrawerLayoutAndroid API
2
+ // It's cross-compatible with all platforms despite
3
+ // `DrawerLayoutAndroid` only being available on android
4
+
5
+ import React, {
6
+ ReactNode,
7
+ forwardRef,
8
+ useCallback,
9
+ useEffect,
10
+ useImperativeHandle,
11
+ useMemo,
12
+ useState,
13
+ } from 'react';
14
+
15
+ import {
16
+ StyleSheet,
17
+ Keyboard,
18
+ StatusBar,
19
+ I18nManager,
20
+ StatusBarAnimation,
21
+ StyleProp,
22
+ ViewStyle,
23
+ LayoutChangeEvent,
24
+ Platform,
25
+ } from 'react-native';
26
+
27
+ import Animated, {
28
+ Extrapolation,
29
+ SharedValue,
30
+ interpolate,
31
+ runOnJS,
32
+ useAnimatedProps,
33
+ useAnimatedStyle,
34
+ useDerivedValue,
35
+ useSharedValue,
36
+ withSpring,
37
+ } from 'react-native-reanimated';
38
+
39
+ import { GestureObjects as Gesture } from '../handlers/gestures/gestureObjects';
40
+ import { GestureDetector } from '../handlers/gestures/GestureDetector';
41
+ import {
42
+ UserSelect,
43
+ ActiveCursor,
44
+ MouseButton,
45
+ HitSlop,
46
+ GestureStateChangeEvent,
47
+ } from '../handlers/gestureHandlerCommon';
48
+ import { PanGestureHandlerEventPayload } from '../handlers/GestureHandlerEventPayload';
49
+
50
+ const DRAG_TOSS = 0.05;
51
+
52
+ export enum DrawerPosition {
53
+ LEFT,
54
+ RIGHT,
55
+ }
56
+
57
+ export enum DrawerState {
58
+ IDLE,
59
+ DRAGGING,
60
+ SETTLING,
61
+ }
62
+
63
+ export enum DrawerType {
64
+ FRONT,
65
+ BACK,
66
+ SLIDE,
67
+ }
68
+
69
+ export enum DrawerLockMode {
70
+ UNLOCKED,
71
+ LOCKED_CLOSED,
72
+ LOCKED_OPEN,
73
+ }
74
+
75
+ export enum DrawerKeyboardDismissMode {
76
+ NONE,
77
+ ON_DRAG,
78
+ }
79
+
80
+ export interface DrawerLayoutProps {
81
+ /**
82
+ * This attribute is present in the native android implementation already and is one
83
+ * of the required params. The gesture handler version of DrawerLayout makes it
84
+ * possible for the function passed as `renderNavigationView` to take an
85
+ * Animated value as a parameter that indicates the progress of drawer
86
+ * opening/closing animation (progress value is 0 when closed and 1 when
87
+ * opened). This can be used by the drawer component to animated its children
88
+ * while the drawer is opening or closing.
89
+ */
90
+ renderNavigationView: (
91
+ progressAnimatedValue: SharedValue<number>
92
+ ) => ReactNode;
93
+
94
+ /**
95
+ * Determines the side from which the drawer will open.
96
+ */
97
+ drawerPosition?: DrawerPosition;
98
+
99
+ /**
100
+ * Width of the drawer.
101
+ */
102
+ drawerWidth?: number;
103
+
104
+ /**
105
+ * Background color of the drawer.
106
+ */
107
+ drawerBackgroundColor?: string;
108
+
109
+ /**
110
+ * Specifies the lock mode of the drawer.
111
+ * Programatic opening/closing isn't affected by the lock mode. Defaults to `UNLOCKED`.
112
+ * - `UNLOCKED` - the drawer will respond to gestures.
113
+ * - `LOCKED_CLOSED` - the drawer will move freely until it settles in a closed position, then the gestures will be disabled.
114
+ * - `LOCKED_OPEN` - the drawer will move freely until it settles in an opened position, then the gestures will be disabled.
115
+ */
116
+ drawerLockMode?: DrawerLockMode;
117
+
118
+ /**
119
+ * Determines if system keyboard should be closed upon dragging the drawer.
120
+ */
121
+ keyboardDismissMode?: DrawerKeyboardDismissMode;
122
+
123
+ /**
124
+ * Called when the drawer is closed.
125
+ */
126
+ onDrawerClose?: () => void;
127
+
128
+ /**
129
+ * Called when the drawer is opened.
130
+ */
131
+ onDrawerOpen?: () => void;
132
+
133
+ /**
134
+ * Called when the status of the drawer changes.
135
+ */
136
+ onDrawerStateChanged?: (
137
+ newState: DrawerState,
138
+ drawerWillShow: boolean
139
+ ) => void;
140
+
141
+ /**
142
+ * Type of animation that will play when opening the drawer.
143
+ */
144
+ drawerType?: DrawerType;
145
+
146
+ /**
147
+ * Defines how far from the edge of the content view the gesture should
148
+ * activate.
149
+ */
150
+ edgeWidth?: number;
151
+
152
+ /**
153
+ * Minimal distance to swipe before the drawer starts moving.
154
+ */
155
+ minSwipeDistance?: number;
156
+
157
+ /**
158
+ * When set to true Drawer component will use
159
+ * {@link https://reactnative.dev/docs/statusbar StatusBar} API to hide the OS
160
+ * status bar whenever the drawer is pulled or when its in an "open" state.
161
+ */
162
+ hideStatusBar?: boolean;
163
+
164
+ /**
165
+ * @default 'slide'
166
+ *
167
+ * Can be used when hideStatusBar is set to true and will select the animation
168
+ * used for hiding/showing the status bar. See
169
+ * {@link https://reactnative.dev/docs/statusbar StatusBar} documentation for
170
+ * more details
171
+ */
172
+ statusBarAnimation?: StatusBarAnimation;
173
+
174
+ /**
175
+ * @default 'rgba(0, 0, 0, 0.7)'
176
+ *
177
+ * Color of the background overlay.
178
+ * Animated from `0%` to `100%` as the drawer opens.
179
+ */
180
+ overlayColor?: string;
181
+
182
+ /**
183
+ * Style wrapping the content.
184
+ */
185
+ contentContainerStyle?: StyleProp<ViewStyle>;
186
+
187
+ /**
188
+ * Style wrapping the drawer.
189
+ */
190
+ drawerContainerStyle?: StyleProp<ViewStyle>;
191
+
192
+ /**
193
+ * Enables two-finger gestures on supported devices, for example iPads with
194
+ * trackpads. If not enabled the gesture will require click + drag, with
195
+ * `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger
196
+ * the gesture.
197
+ */
198
+ enableTrackpadTwoFingerGesture?: boolean;
199
+
200
+ onDrawerSlide?: (position: number) => void;
201
+
202
+ // Implicit `children` prop has been removed in @types/react^18.0.
203
+ /**
204
+ * Elements that will be rendered inside the content view.
205
+ */
206
+ children?: ReactNode | ((openValue?: SharedValue<number>) => ReactNode);
207
+
208
+ /**
209
+ * @default 'none'
210
+ * Sets whether the text inside both the drawer and the context window can be selected.
211
+ * Values: 'none' | 'text' | 'auto'
212
+ */
213
+ userSelect?: UserSelect;
214
+
215
+ /**
216
+ * @default 'auto'
217
+ * Sets the displayed cursor pictogram when the drawer is being dragged.
218
+ * Values: see CSS cursor values
219
+ */
220
+ activeCursor?: ActiveCursor;
221
+
222
+ /**
223
+ * @default 'MouseButton.LEFT'
224
+ * Allows to choose which mouse button should underlying pan handler react to.
225
+ */
226
+ mouseButton?: MouseButton;
227
+
228
+ /**
229
+ * @default 'false if MouseButton.RIGHT is specified'
230
+ * Allows to enable/disable context menu.
231
+ */
232
+ enableContextMenu?: boolean;
233
+ }
234
+
235
+ export type DrawerMovementOption = {
236
+ initialVelocity?: number;
237
+ animationSpeed?: number;
238
+ };
239
+
240
+ export interface DrawerLayoutMethods {
241
+ openDrawer: (options?: DrawerMovementOption) => void;
242
+ closeDrawer: (options?: DrawerMovementOption) => void;
243
+ }
244
+
245
+ const defaultProps = {
246
+ drawerWidth: 200,
247
+ drawerPosition: DrawerPosition.LEFT,
248
+ drawerType: DrawerType.FRONT,
249
+ edgeWidth: 20,
250
+ minSwipeDistance: 3,
251
+ overlayColor: 'rgba(0, 0, 0, 0.7)',
252
+ drawerLockMode: DrawerLockMode.UNLOCKED,
253
+ enableTrackpadTwoFingerGesture: false,
254
+ activeCursor: 'auto' as ActiveCursor,
255
+ mouseButton: MouseButton.LEFT,
256
+ statusBarAnimation: 'slide' as StatusBarAnimation,
257
+ };
258
+
259
+ const DrawerLayout = forwardRef<DrawerLayoutMethods, DrawerLayoutProps>(
260
+ function DrawerLayout(props: DrawerLayoutProps, ref) {
261
+ const [containerWidth, setContainerWidth] = useState(0);
262
+ const [drawerState, setDrawerState] = useState<DrawerState>(
263
+ DrawerState.IDLE
264
+ );
265
+ const [drawerOpened, setDrawerOpened] = useState(false);
266
+
267
+ const {
268
+ drawerPosition = defaultProps.drawerPosition,
269
+ drawerWidth = defaultProps.drawerWidth,
270
+ drawerType = defaultProps.drawerType,
271
+ drawerBackgroundColor,
272
+ drawerContainerStyle,
273
+ contentContainerStyle,
274
+ minSwipeDistance = defaultProps.minSwipeDistance,
275
+ edgeWidth = defaultProps.edgeWidth,
276
+ drawerLockMode = defaultProps.drawerLockMode,
277
+ overlayColor = defaultProps.overlayColor,
278
+ enableTrackpadTwoFingerGesture = defaultProps.enableTrackpadTwoFingerGesture,
279
+ activeCursor = defaultProps.activeCursor,
280
+ mouseButton = defaultProps.mouseButton,
281
+ statusBarAnimation = defaultProps.statusBarAnimation,
282
+ hideStatusBar,
283
+ keyboardDismissMode,
284
+ userSelect,
285
+ enableContextMenu,
286
+ renderNavigationView,
287
+ onDrawerSlide,
288
+ onDrawerClose,
289
+ onDrawerOpen,
290
+ onDrawerStateChanged,
291
+ } = props;
292
+
293
+ const isFromLeft = drawerPosition === DrawerPosition.LEFT;
294
+
295
+ const sideCorrection = isFromLeft ? 1 : -1;
296
+
297
+ // While closing the drawer when user starts gesture in the greyed out part of the window,
298
+ // we want the drawer to follow only once the finger reaches the edge of the drawer.
299
+ // See the diagram for reference. * = starting finger position, < = current finger position
300
+ // 1) +---------------+ 2) +---------------+ 3) +---------------+ 4) +---------------+
301
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
302
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
303
+ // |XXXXXXXX|..<*..| |XXXXXXXX|.<-*..| |XXXXXXXX|<--*..| |XXXXX|<-----*..|
304
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
305
+ // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
306
+ // +---------------+ +---------------+ +---------------+ +---------------+
307
+
308
+ const openValue = useSharedValue<number>(0);
309
+
310
+ useDerivedValue(() => {
311
+ onDrawerSlide && runOnJS(onDrawerSlide)(openValue.value);
312
+ }, []);
313
+
314
+ const isDrawerOpen = useSharedValue(false);
315
+
316
+ const handleContainerLayout = ({ nativeEvent }: LayoutChangeEvent) => {
317
+ setContainerWidth(nativeEvent.layout.width);
318
+ };
319
+
320
+ const emitStateChanged = useCallback(
321
+ (newState: DrawerState, drawerWillShow: boolean) => {
322
+ 'worklet';
323
+ onDrawerStateChanged &&
324
+ runOnJS(onDrawerStateChanged)?.(newState, drawerWillShow);
325
+ },
326
+ [onDrawerStateChanged]
327
+ );
328
+
329
+ const drawerAnimatedProps = useAnimatedProps(() => ({
330
+ accessibilityViewIsModal: isDrawerOpen.value,
331
+ }));
332
+
333
+ const overlayAnimatedProps = useAnimatedProps(() => ({
334
+ pointerEvents: isDrawerOpen.value ? ('auto' as const) : ('none' as const),
335
+ }));
336
+
337
+ // While the drawer is hidden, it's hitSlop overflows onto the main view by edgeWidth
338
+ // This way it can be swiped open even when it's hidden
339
+ const [edgeHitSlop, setEdgeHitSlop] = useState<HitSlop>(
340
+ isFromLeft
341
+ ? { left: 0, width: edgeWidth }
342
+ : { right: 0, width: edgeWidth }
343
+ );
344
+
345
+ // gestureOrientation is 1 if the gesture is expected to move from left to right and -1 otherwise
346
+ const gestureOrientation = useMemo(
347
+ () => sideCorrection * (drawerOpened ? -1 : 1),
348
+ [sideCorrection, drawerOpened]
349
+ );
350
+
351
+ useEffect(() => {
352
+ setEdgeHitSlop(
353
+ isFromLeft
354
+ ? { left: 0, width: edgeWidth }
355
+ : { right: 0, width: edgeWidth }
356
+ );
357
+ }, [isFromLeft, edgeWidth]);
358
+
359
+ const animateDrawer = useCallback(
360
+ (toValue: number, initialVelocity: number, animationSpeed?: number) => {
361
+ 'worklet';
362
+ const willShow = toValue !== 0;
363
+ isDrawerOpen.value = willShow;
364
+
365
+ emitStateChanged(DrawerState.SETTLING, willShow);
366
+ runOnJS(setDrawerState)(DrawerState.SETTLING);
367
+
368
+ if (hideStatusBar) {
369
+ runOnJS(StatusBar.setHidden)(willShow, statusBarAnimation);
370
+ }
371
+
372
+ const normalizedToValue = interpolate(
373
+ toValue,
374
+ [0, drawerWidth],
375
+ [0, 1],
376
+ Extrapolation.CLAMP
377
+ );
378
+
379
+ const normalizedInitialVelocity = interpolate(
380
+ initialVelocity,
381
+ [0, drawerWidth],
382
+ [0, 1],
383
+ Extrapolation.CLAMP
384
+ );
385
+
386
+ openValue.value = withSpring(
387
+ normalizedToValue,
388
+ {
389
+ overshootClamping: true,
390
+ velocity: normalizedInitialVelocity,
391
+ mass: animationSpeed ? 1 / animationSpeed : 1,
392
+ damping: 40,
393
+ stiffness: 500,
394
+ },
395
+ (finished) => {
396
+ if (finished) {
397
+ emitStateChanged(DrawerState.IDLE, willShow);
398
+ runOnJS(setDrawerOpened)(willShow);
399
+ runOnJS(setDrawerState)(DrawerState.IDLE);
400
+ if (willShow) {
401
+ onDrawerOpen && runOnJS(onDrawerOpen)?.();
402
+ } else {
403
+ onDrawerClose && runOnJS(onDrawerClose)?.();
404
+ }
405
+ }
406
+ }
407
+ );
408
+ },
409
+ [
410
+ openValue,
411
+ emitStateChanged,
412
+ isDrawerOpen,
413
+ hideStatusBar,
414
+ onDrawerClose,
415
+ onDrawerOpen,
416
+ drawerWidth,
417
+ statusBarAnimation,
418
+ ]
419
+ );
420
+
421
+ const handleRelease = useCallback(
422
+ (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
423
+ 'worklet';
424
+ let { translationX: dragX, velocityX, x: touchX } = event;
425
+
426
+ if (drawerPosition !== DrawerPosition.LEFT) {
427
+ // See description in _updateAnimatedEvent about why events are flipped
428
+ // for right-side drawer
429
+ dragX = -dragX;
430
+ touchX = containerWidth - touchX;
431
+ velocityX = -velocityX;
432
+ }
433
+
434
+ const gestureStartX = touchX - dragX;
435
+ let dragOffsetBasedOnStart = 0;
436
+
437
+ if (drawerType === DrawerType.FRONT) {
438
+ dragOffsetBasedOnStart =
439
+ gestureStartX > drawerWidth ? gestureStartX - drawerWidth : 0;
440
+ }
441
+
442
+ const startOffsetX =
443
+ dragX +
444
+ dragOffsetBasedOnStart +
445
+ (isDrawerOpen.value ? drawerWidth : 0);
446
+
447
+ const projOffsetX = startOffsetX + DRAG_TOSS * velocityX;
448
+
449
+ const shouldOpen = projOffsetX > drawerWidth / 2;
450
+
451
+ if (shouldOpen) {
452
+ animateDrawer(drawerWidth, velocityX);
453
+ } else {
454
+ animateDrawer(0, velocityX);
455
+ }
456
+ },
457
+ [
458
+ animateDrawer,
459
+ containerWidth,
460
+ drawerPosition,
461
+ drawerType,
462
+ drawerWidth,
463
+ isDrawerOpen,
464
+ ]
465
+ );
466
+
467
+ const openDrawer = useCallback(
468
+ (options: DrawerMovementOption = {}) => {
469
+ 'worklet';
470
+ animateDrawer(
471
+ drawerWidth,
472
+ options.initialVelocity ?? 0,
473
+ options.animationSpeed
474
+ );
475
+ },
476
+ [animateDrawer, drawerWidth]
477
+ );
478
+
479
+ const closeDrawer = useCallback(
480
+ (options: DrawerMovementOption = {}) => {
481
+ 'worklet';
482
+ animateDrawer(0, options.initialVelocity ?? 0, options.animationSpeed);
483
+ },
484
+ [animateDrawer]
485
+ );
486
+
487
+ const overlayDismissGesture = useMemo(
488
+ () =>
489
+ Gesture.Tap()
490
+ .maxDistance(25)
491
+ .onEnd(() => {
492
+ if (
493
+ isDrawerOpen.value &&
494
+ drawerLockMode !== DrawerLockMode.LOCKED_OPEN
495
+ ) {
496
+ closeDrawer();
497
+ }
498
+ }),
499
+ [closeDrawer, isDrawerOpen, drawerLockMode]
500
+ );
501
+
502
+ const overlayAnimatedStyle = useAnimatedStyle(() => ({
503
+ opacity: openValue.value,
504
+ backgroundColor: overlayColor,
505
+ }));
506
+
507
+ const fillHitSlop = useMemo(
508
+ () => (isFromLeft ? { left: drawerWidth } : { right: drawerWidth }),
509
+ [drawerWidth, isFromLeft]
510
+ );
511
+
512
+ const panGesture = useMemo(() => {
513
+ return Gesture.Pan()
514
+ .activeCursor(activeCursor)
515
+ .mouseButton(mouseButton)
516
+ .hitSlop(drawerOpened ? fillHitSlop : edgeHitSlop)
517
+ .minDistance(drawerOpened ? 100 : 0)
518
+ .activeOffsetX(gestureOrientation * minSwipeDistance)
519
+ .failOffsetY([-15, 15])
520
+ .simultaneousWithExternalGesture(overlayDismissGesture)
521
+ .enableTrackpadTwoFingerGesture(enableTrackpadTwoFingerGesture)
522
+ .enabled(
523
+ drawerState !== DrawerState.SETTLING &&
524
+ (drawerOpened
525
+ ? drawerLockMode !== DrawerLockMode.LOCKED_OPEN
526
+ : drawerLockMode !== DrawerLockMode.LOCKED_CLOSED)
527
+ )
528
+ .onStart(() => {
529
+ emitStateChanged(DrawerState.DRAGGING, false);
530
+ runOnJS(setDrawerState)(DrawerState.DRAGGING);
531
+ if (keyboardDismissMode === DrawerKeyboardDismissMode.ON_DRAG) {
532
+ runOnJS(Keyboard.dismiss)();
533
+ }
534
+ if (hideStatusBar) {
535
+ runOnJS(StatusBar.setHidden)(true, statusBarAnimation);
536
+ }
537
+ })
538
+ .onUpdate((event) => {
539
+ const startedOutsideTranslation = isFromLeft
540
+ ? interpolate(
541
+ event.x,
542
+ [0, drawerWidth, drawerWidth + 1],
543
+ [0, drawerWidth, drawerWidth]
544
+ )
545
+ : interpolate(
546
+ event.x - containerWidth,
547
+ [-drawerWidth - 1, -drawerWidth, 0],
548
+ [drawerWidth, drawerWidth, 0]
549
+ );
550
+
551
+ const startedInsideTranslation =
552
+ sideCorrection *
553
+ (event.translationX +
554
+ (drawerOpened ? drawerWidth * -gestureOrientation : 0));
555
+
556
+ const adjustedTranslation = Math.max(
557
+ drawerOpened ? startedOutsideTranslation : 0,
558
+ startedInsideTranslation
559
+ );
560
+
561
+ openValue.value = interpolate(
562
+ adjustedTranslation,
563
+ [-drawerWidth, 0, drawerWidth],
564
+ [1, 0, 1],
565
+ Extrapolation.CLAMP
566
+ );
567
+ })
568
+ .onEnd(handleRelease);
569
+ }, [
570
+ drawerLockMode,
571
+ openValue,
572
+ drawerWidth,
573
+ emitStateChanged,
574
+ gestureOrientation,
575
+ handleRelease,
576
+ edgeHitSlop,
577
+ fillHitSlop,
578
+ minSwipeDistance,
579
+ hideStatusBar,
580
+ keyboardDismissMode,
581
+ overlayDismissGesture,
582
+ drawerOpened,
583
+ isFromLeft,
584
+ containerWidth,
585
+ sideCorrection,
586
+ drawerState,
587
+ activeCursor,
588
+ enableTrackpadTwoFingerGesture,
589
+ mouseButton,
590
+ statusBarAnimation,
591
+ ]);
592
+
593
+ // When using RTL, row and row-reverse flex directions are flipped.
594
+ const reverseContentDirection = I18nManager.isRTL
595
+ ? isFromLeft
596
+ : !isFromLeft;
597
+
598
+ const dynamicDrawerStyles = {
599
+ backgroundColor: drawerBackgroundColor,
600
+ width: drawerWidth,
601
+ };
602
+
603
+ const containerStyles = useAnimatedStyle(() => {
604
+ if (drawerType === DrawerType.FRONT) {
605
+ return {};
606
+ }
607
+
608
+ return {
609
+ transform: [
610
+ {
611
+ translateX: interpolate(
612
+ openValue.value,
613
+ [0, 1],
614
+ [0, drawerWidth * sideCorrection],
615
+ Extrapolation.CLAMP
616
+ ),
617
+ },
618
+ ],
619
+ };
620
+ });
621
+
622
+ const drawerAnimatedStyle = useAnimatedStyle(() => {
623
+ const closedDrawerOffset = drawerWidth * -sideCorrection;
624
+ const isBack = drawerType === DrawerType.BACK;
625
+ const isIdle = drawerState === DrawerState.IDLE;
626
+
627
+ if (isBack) {
628
+ return {
629
+ transform: [{ translateX: 0 }],
630
+ flexDirection: reverseContentDirection ? 'row-reverse' : 'row',
631
+ };
632
+ }
633
+
634
+ let translateX = 0;
635
+
636
+ if (isIdle) {
637
+ translateX = drawerOpened ? 0 : closedDrawerOffset;
638
+ } else {
639
+ translateX = interpolate(
640
+ openValue.value,
641
+ [0, 1],
642
+ [closedDrawerOffset, 0],
643
+ Extrapolation.CLAMP
644
+ );
645
+ }
646
+
647
+ return {
648
+ transform: [{ translateX }],
649
+ flexDirection: reverseContentDirection ? 'row-reverse' : 'row',
650
+ };
651
+ });
652
+
653
+ const containerAnimatedProps = useAnimatedProps(() => ({
654
+ importantForAccessibility:
655
+ Platform.OS === 'android'
656
+ ? isDrawerOpen.value
657
+ ? ('no-hide-descendants' as const)
658
+ : ('yes' as const)
659
+ : undefined,
660
+ }));
661
+
662
+ const children =
663
+ typeof props.children === 'function'
664
+ ? props.children(openValue) // renderer function
665
+ : props.children;
666
+
667
+ useImperativeHandle(
668
+ ref,
669
+ () => ({
670
+ openDrawer,
671
+ closeDrawer,
672
+ }),
673
+ [openDrawer, closeDrawer]
674
+ );
675
+
676
+ return (
677
+ <GestureDetector
678
+ gesture={panGesture}
679
+ userSelect={userSelect}
680
+ enableContextMenu={enableContextMenu}>
681
+ <Animated.View style={styles.main} onLayout={handleContainerLayout}>
682
+ <GestureDetector gesture={overlayDismissGesture}>
683
+ <Animated.View
684
+ style={[
685
+ drawerType === DrawerType.FRONT
686
+ ? styles.containerOnBack
687
+ : styles.containerInFront,
688
+ containerStyles,
689
+ contentContainerStyle,
690
+ ]}
691
+ animatedProps={containerAnimatedProps}>
692
+ {children}
693
+ <Animated.View
694
+ animatedProps={overlayAnimatedProps}
695
+ style={[styles.overlay, overlayAnimatedStyle]}
696
+ />
697
+ </Animated.View>
698
+ </GestureDetector>
699
+ <Animated.View
700
+ pointerEvents="box-none"
701
+ animatedProps={drawerAnimatedProps}
702
+ style={[
703
+ styles.drawerContainer,
704
+ drawerAnimatedStyle,
705
+ drawerContainerStyle,
706
+ ]}>
707
+ <Animated.View style={dynamicDrawerStyles}>
708
+ {renderNavigationView(openValue)}
709
+ </Animated.View>
710
+ </Animated.View>
711
+ </Animated.View>
712
+ </GestureDetector>
713
+ );
714
+ }
715
+ );
716
+
717
+ export default DrawerLayout;
718
+
719
+ const styles = StyleSheet.create({
720
+ drawerContainer: {
721
+ ...StyleSheet.absoluteFillObject,
722
+ zIndex: 1001,
723
+ flexDirection: 'row',
724
+ },
725
+ containerInFront: {
726
+ ...StyleSheet.absoluteFillObject,
727
+ zIndex: 1002,
728
+ },
729
+ containerOnBack: {
730
+ ...StyleSheet.absoluteFillObject,
731
+ },
732
+ main: {
733
+ flex: 1,
734
+ zIndex: 0,
735
+ overflow: 'hidden',
736
+ },
737
+ overlay: {
738
+ ...StyleSheet.absoluteFillObject,
739
+ zIndex: 1000,
740
+ },
741
+ });