react-native-gesture-handler 2.20.2 → 2.21.1
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.
- package/RNGestureHandler.podspec +9 -0
- package/ReanimatedDrawerLayout/package.json +6 -0
- package/android/build.gradle +19 -0
- package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +6 -2
- package/android/src/main/java/com/swmansion/gesturehandler/core/RotationGestureDetector.kt +53 -21
- package/apple/RNGestureHandlerPointerTracker.m +4 -2
- package/lib/commonjs/RNGestureHandlerModule.web.js +15 -2
- package/lib/commonjs/RNGestureHandlerModule.web.js.map +1 -1
- package/lib/commonjs/components/Pressable/Pressable.js +2 -6
- package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
- package/lib/commonjs/components/ReanimatedDrawerLayout.js +389 -0
- package/lib/commonjs/components/ReanimatedDrawerLayout.js.map +1 -0
- package/lib/commonjs/components/ReanimatedSwipeable.js +117 -148
- package/lib/commonjs/components/ReanimatedSwipeable.js.map +1 -1
- package/lib/commonjs/findNodeHandle.js +12 -0
- package/lib/commonjs/findNodeHandle.js.map +1 -0
- package/lib/commonjs/findNodeHandle.web.js +40 -0
- package/lib/commonjs/findNodeHandle.web.js.map +1 -0
- package/lib/commonjs/handlers/createHandler.js +4 -2
- package/lib/commonjs/handlers/createHandler.js.map +1 -1
- package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js +51 -0
- package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
- package/lib/commonjs/handlers/gestures/GestureDetector/index.js +3 -1
- package/lib/commonjs/handlers/gestures/GestureDetector/index.js.map +1 -1
- package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js +5 -3
- package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
- package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js +4 -2
- package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
- package/lib/commonjs/web/handlers/GestureHandler.js +4 -0
- package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
- package/lib/commonjs/web/handlers/PanGestureHandler.js +59 -0
- package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
- package/lib/commonjs/web/interfaces.js +10 -1
- package/lib/commonjs/web/interfaces.js.map +1 -1
- package/lib/commonjs/web/tools/EventManager.js +6 -0
- package/lib/commonjs/web/tools/EventManager.js.map +1 -1
- package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js +1 -3
- package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js.map +1 -1
- package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +5 -2
- package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
- package/lib/commonjs/web/tools/KeyboardEventManager.js +2 -2
- package/lib/commonjs/web/tools/KeyboardEventManager.js.map +1 -1
- package/lib/commonjs/web/tools/PointerTracker.js +6 -30
- package/lib/commonjs/web/tools/PointerTracker.js.map +1 -1
- package/lib/commonjs/web/tools/WheelEventManager.js +74 -0
- package/lib/commonjs/web/tools/WheelEventManager.js.map +1 -0
- package/lib/commonjs/web/utils.js +16 -0
- package/lib/commonjs/web/utils.js.map +1 -1
- package/lib/module/RNGestureHandlerModule.web.js +16 -3
- package/lib/module/RNGestureHandlerModule.web.js.map +1 -1
- package/lib/module/components/Pressable/Pressable.js +2 -6
- package/lib/module/components/Pressable/Pressable.js.map +1 -1
- package/lib/module/components/ReanimatedDrawerLayout.js +365 -0
- package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -0
- package/lib/module/components/ReanimatedSwipeable.js +119 -145
- package/lib/module/components/ReanimatedSwipeable.js.map +1 -1
- package/lib/module/findNodeHandle.js +3 -0
- package/lib/module/findNodeHandle.js.map +1 -0
- package/lib/module/findNodeHandle.web.js +32 -0
- package/lib/module/findNodeHandle.web.js.map +1 -0
- package/lib/module/handlers/createHandler.js +2 -1
- package/lib/module/handlers/createHandler.js.map +1 -1
- package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js +34 -0
- package/lib/module/handlers/gestures/GestureDetector/Wrap.web.js.map +1 -0
- package/lib/module/handlers/gestures/GestureDetector/index.js +2 -1
- package/lib/module/handlers/gestures/GestureDetector/index.js.map +1 -1
- package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js +2 -2
- package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
- package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js +1 -1
- package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
- package/lib/module/web/handlers/GestureHandler.js +4 -0
- package/lib/module/web/handlers/GestureHandler.js.map +1 -1
- package/lib/module/web/handlers/PanGestureHandler.js +58 -0
- package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
- package/lib/module/web/interfaces.js +8 -0
- package/lib/module/web/interfaces.js.map +1 -1
- package/lib/module/web/tools/EventManager.js +6 -0
- package/lib/module/web/tools/EventManager.js.map +1 -1
- package/lib/module/web/tools/GestureHandlerOrchestrator.js +1 -3
- package/lib/module/web/tools/GestureHandlerOrchestrator.js.map +1 -1
- package/lib/module/web/tools/GestureHandlerWebDelegate.js +3 -1
- package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
- package/lib/module/web/tools/KeyboardEventManager.js +2 -2
- package/lib/module/web/tools/KeyboardEventManager.js.map +1 -1
- package/lib/module/web/tools/PointerTracker.js +6 -30
- package/lib/module/web/tools/PointerTracker.js.map +1 -1
- package/lib/module/web/tools/WheelEventManager.js +60 -0
- package/lib/module/web/tools/WheelEventManager.js.map +1 -0
- package/lib/module/web/utils.js +15 -0
- package/lib/module/web/utils.js.map +1 -1
- package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +162 -0
- package/lib/typescript/components/ReanimatedSwipeable.d.ts +22 -16
- package/lib/typescript/findNodeHandle.d.ts +2 -0
- package/lib/typescript/findNodeHandle.web.d.ts +2 -0
- package/lib/typescript/handlers/gestures/GestureDetector/Wrap.web.d.ts +7 -0
- package/lib/typescript/web/handlers/GestureHandler.d.ts +2 -1
- package/lib/typescript/web/handlers/PanGestureHandler.d.ts +5 -0
- package/lib/typescript/web/interfaces.d.ts +16 -0
- package/lib/typescript/web/tools/EventManager.d.ts +2 -0
- package/lib/typescript/web/tools/PointerTracker.d.ts +2 -8
- package/lib/typescript/web/tools/WheelEventManager.d.ts +11 -0
- package/lib/typescript/web/utils.d.ts +2 -1
- package/package.json +3 -2
- package/src/RNGestureHandlerModule.web.ts +23 -4
- package/src/components/Pressable/Pressable.tsx +2 -6
- package/src/components/ReanimatedDrawerLayout.tsx +741 -0
- package/src/components/ReanimatedSwipeable.tsx +361 -305
- package/src/findNodeHandle.ts +3 -0
- package/src/findNodeHandle.web.ts +35 -0
- package/src/handlers/createHandler.tsx +2 -1
- package/src/handlers/gestures/GestureDetector/Wrap.web.tsx +44 -0
- package/src/handlers/gestures/GestureDetector/index.tsx +2 -1
- package/src/handlers/gestures/GestureDetector/useDetectorUpdater.ts +1 -1
- package/src/handlers/gestures/GestureDetector/useViewRefHandler.ts +1 -1
- package/src/web/handlers/GestureHandler.ts +5 -1
- package/src/web/handlers/PanGestureHandler.ts +69 -1
- package/src/web/interfaces.ts +17 -0
- package/src/web/tools/EventManager.ts +4 -0
- package/src/web/tools/GestureHandlerOrchestrator.ts +1 -7
- package/src/web/tools/GestureHandlerWebDelegate.ts +3 -1
- package/src/web/tools/KeyboardEventManager.ts +2 -2
- package/src/web/tools/PointerTracker.ts +6 -28
- package/src/web/tools/WheelEventManager.ts +48 -0
- 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
|
+
});
|