react-native-drawer-layout 4.0.0-alpha.6 → 4.0.0-alpha.8

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 (54) hide show
  1. package/lib/commonjs/utils/getDrawerWidth.js +41 -0
  2. package/lib/commonjs/utils/getDrawerWidth.js.map +1 -0
  3. package/lib/commonjs/utils/useDrawerProgress.js.map +1 -1
  4. package/lib/commonjs/utils/useFakeSharedValue.js +46 -0
  5. package/lib/commonjs/utils/useFakeSharedValue.js.map +1 -0
  6. package/lib/commonjs/views/Drawer.js +51 -283
  7. package/lib/commonjs/views/Drawer.js.map +1 -1
  8. package/lib/commonjs/views/Drawer.native.js +313 -0
  9. package/lib/commonjs/views/Drawer.native.js.map +1 -0
  10. package/lib/commonjs/views/Overlay.js +22 -39
  11. package/lib/commonjs/views/Overlay.js.map +1 -1
  12. package/lib/commonjs/views/Overlay.native.js +58 -0
  13. package/lib/commonjs/views/Overlay.native.js.map +1 -0
  14. package/lib/module/utils/getDrawerWidth.js +35 -0
  15. package/lib/module/utils/getDrawerWidth.js.map +1 -0
  16. package/lib/module/utils/useDrawerProgress.js.map +1 -1
  17. package/lib/module/utils/useFakeSharedValue.js +38 -0
  18. package/lib/module/utils/useFakeSharedValue.js.map +1 -0
  19. package/lib/module/views/Drawer.js +52 -282
  20. package/lib/module/views/Drawer.js.map +1 -1
  21. package/lib/module/views/Drawer.native.js +304 -0
  22. package/lib/module/views/Drawer.native.js.map +1 -0
  23. package/lib/module/views/Overlay.js +22 -39
  24. package/lib/module/views/Overlay.js.map +1 -1
  25. package/lib/module/views/Overlay.native.js +50 -0
  26. package/lib/module/views/Overlay.native.js.map +1 -0
  27. package/lib/typescript/src/types.d.ts +9 -2
  28. package/lib/typescript/src/types.d.ts.map +1 -1
  29. package/lib/typescript/src/utils/DrawerProgressContext.d.ts +2 -2
  30. package/lib/typescript/src/utils/DrawerProgressContext.d.ts.map +1 -1
  31. package/lib/typescript/src/utils/getDrawerWidth.d.ts +9 -0
  32. package/lib/typescript/src/utils/getDrawerWidth.d.ts.map +1 -0
  33. package/lib/typescript/src/utils/useDrawerProgress.d.ts +2 -2
  34. package/lib/typescript/src/utils/useDrawerProgress.d.ts.map +1 -1
  35. package/lib/typescript/src/utils/useFakeSharedValue.d.ts +17 -0
  36. package/lib/typescript/src/utils/useFakeSharedValue.d.ts.map +1 -0
  37. package/lib/typescript/src/views/Drawer.d.ts +1 -6
  38. package/lib/typescript/src/views/Drawer.d.ts.map +1 -1
  39. package/lib/typescript/src/views/Drawer.native.d.ts +4 -0
  40. package/lib/typescript/src/views/Drawer.native.d.ts.map +1 -0
  41. package/lib/typescript/src/views/Overlay.d.ts +2 -204
  42. package/lib/typescript/src/views/Overlay.d.ts.map +1 -1
  43. package/lib/typescript/src/views/Overlay.native.d.ts +4 -0
  44. package/lib/typescript/src/views/Overlay.native.d.ts.map +1 -0
  45. package/package.json +2 -2
  46. package/src/types.tsx +10 -1
  47. package/src/utils/DrawerProgressContext.tsx +2 -2
  48. package/src/utils/getDrawerWidth.tsx +39 -0
  49. package/src/utils/useDrawerProgress.tsx +2 -2
  50. package/src/utils/useFakeSharedValue.tsx +49 -0
  51. package/src/views/Drawer.native.tsx +450 -0
  52. package/src/views/Drawer.tsx +102 -434
  53. package/src/views/Overlay.native.tsx +63 -0
  54. package/src/views/Overlay.tsx +26 -59
@@ -1,476 +1,150 @@
1
1
  import * as React from 'react';
2
- import {
3
- I18nManager,
4
- InteractionManager,
5
- Keyboard,
6
- Platform,
7
- StatusBar,
8
- StyleSheet,
9
- useWindowDimensions,
10
- View,
11
- } from 'react-native';
12
- import Animated, {
13
- interpolate,
14
- runOnJS,
15
- useAnimatedGestureHandler,
16
- useAnimatedStyle,
17
- useDerivedValue,
18
- useSharedValue,
19
- withSpring,
20
- } from 'react-native-reanimated';
2
+ import { StyleSheet, useWindowDimensions, View } from 'react-native';
21
3
  import useLatestCallback from 'use-latest-callback';
22
4
 
23
5
  import type { DrawerProps } from '../types';
24
6
  import { DrawerProgressContext } from '../utils/DrawerProgressContext';
25
- import {
26
- GestureHandlerRootView,
27
- GestureState,
28
- PanGestureHandler,
29
- type PanGestureHandlerGestureEvent,
30
- } from './GestureHandler';
7
+ import { getDrawerWidth } from '../utils/getDrawerWidth';
8
+ import { useFakeSharedValue } from '../utils/useFakeSharedValue';
31
9
  import { Overlay } from './Overlay';
32
10
 
33
- export const SWIPE_EDGE_WIDTH = 32;
34
- export const SWIPE_MIN_OFFSET = 5;
35
- export const SWIPE_MIN_DISTANCE = 60;
36
- export const SWIPE_MIN_VELOCITY = 500;
37
-
38
- export const DRAWER_BORDER_RADIUS = 16;
39
-
40
- const minmax = (value: number, start: number, end: number) => {
41
- 'worklet';
42
-
43
- return Math.min(Math.max(value, start), end);
44
- };
45
-
46
- const getDefaultDrawerWidth = ({
47
- height,
48
- width,
49
- }: {
50
- height: number;
51
- width: number;
52
- }) => {
53
- /*
54
- * Default drawer width is screen width - header height
55
- * with a max width of 280 on mobile and 320 on tablet
56
- * https://material.io/components/navigation-drawer
57
- */
58
- const smallerAxisSize = Math.min(height, width);
59
- const isLandscape = width > height;
60
- const isTablet = smallerAxisSize >= 600;
61
- const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
62
- const maxWidth = isTablet ? 320 : 280;
63
-
64
- return Math.min(smallerAxisSize - appBarHeight, maxWidth);
65
- };
66
-
67
11
  export function Drawer({
68
12
  layout: customLayout,
69
- drawerPosition = I18nManager.getConstants().isRTL ? 'right' : 'left',
13
+ drawerPosition = 'left',
70
14
  drawerStyle,
71
- drawerType = Platform.select({ ios: 'slide', default: 'front' }),
72
- gestureHandlerProps,
73
- hideStatusBarOnOpen = false,
74
- keyboardDismissMode = 'on-drag',
15
+ drawerType = 'front',
75
16
  onClose,
76
- onOpen,
77
- onGestureStart,
78
- onGestureCancel,
79
- onGestureEnd,
80
17
  onTransitionStart,
81
18
  onTransitionEnd,
82
19
  open,
83
20
  overlayStyle,
84
21
  overlayAccessibilityLabel,
85
- statusBarAnimation = 'slide',
86
- swipeEnabled = Platform.OS !== 'web' &&
87
- Platform.OS !== 'windows' &&
88
- Platform.OS !== 'macos',
89
- swipeEdgeWidth = SWIPE_EDGE_WIDTH,
90
- swipeMinDistance = SWIPE_MIN_DISTANCE,
91
- swipeMinVelocity = SWIPE_MIN_VELOCITY,
92
22
  renderDrawerContent,
93
23
  children,
94
24
  style,
95
25
  }: DrawerProps) {
96
- // FIXME: temporary workaround for useSafeAreaFrame not updating on Web
97
26
  const windowDimensions = useWindowDimensions();
98
- const layout = customLayout ?? windowDimensions;
99
-
100
- const getDrawerWidth = (): number => {
101
- const { width = getDefaultDrawerWidth(layout) } =
102
- StyleSheet.flatten(drawerStyle) || {};
103
-
104
- if (typeof width === 'string' && width.endsWith('%')) {
105
- // Try to calculate width if a percentage is given
106
- const percentage = Number(width.replace(/%$/, ''));
107
-
108
- if (Number.isFinite(percentage)) {
109
- return layout.width * (percentage / 100);
110
- }
111
- }
112
-
113
- return typeof width === 'number' ? width : 0;
114
- };
115
-
116
- const drawerWidth = getDrawerWidth();
117
-
118
- const isOpen = drawerType === 'permanent' ? true : open;
119
- const isRight = drawerPosition === 'right';
120
-
121
- const getDrawerTranslationX = React.useCallback(
122
- (open: boolean) => {
123
- 'worklet';
124
27
 
125
- if (drawerPosition === 'left') {
126
- return open ? 0 : -drawerWidth;
127
- }
128
-
129
- return open ? 0 : drawerWidth;
130
- },
131
- [drawerPosition, drawerWidth]
132
- );
28
+ const layout = customLayout ?? windowDimensions;
29
+ const drawerWidth = getDrawerWidth({ layout, drawerStyle });
133
30
 
134
- const hideStatusBar = React.useCallback(
135
- (hide: boolean) => {
136
- if (hideStatusBarOnOpen) {
137
- StatusBar.setHidden(hide, statusBarAnimation);
138
- }
139
- },
140
- [hideStatusBarOnOpen, statusBarAnimation]
141
- );
31
+ const progress = useFakeSharedValue(open ? 1 : 0);
142
32
 
143
33
  React.useEffect(() => {
144
- hideStatusBar(isOpen);
145
-
146
- return () => hideStatusBar(false);
147
- }, [isOpen, hideStatusBarOnOpen, statusBarAnimation, hideStatusBar]);
148
-
149
- const interactionHandleRef = React.useRef<number | null>(null);
150
-
151
- const startInteraction = () => {
152
- interactionHandleRef.current = InteractionManager.createInteractionHandle();
153
- };
154
-
155
- const endInteraction = () => {
156
- if (interactionHandleRef.current != null) {
157
- InteractionManager.clearInteractionHandle(interactionHandleRef.current);
158
- interactionHandleRef.current = null;
159
- }
160
- };
161
-
162
- const hideKeyboard = () => {
163
- if (keyboardDismissMode === 'on-drag') {
164
- Keyboard.dismiss();
165
- }
166
- };
167
-
168
- const onGestureBegin = () => {
169
- onGestureStart?.();
170
- startInteraction();
171
- hideKeyboard();
172
- hideStatusBar(true);
173
- };
174
-
175
- const onGestureFinish = () => {
176
- onGestureEnd?.();
177
- endInteraction();
178
- };
179
-
180
- const onGestureAbort = () => {
181
- onGestureCancel?.();
182
- endInteraction();
183
- };
184
-
185
- // FIXME: Currently hitSlop is broken when on Android when drawer is on right
186
- // https://github.com/software-mansion/react-native-gesture-handler/issues/569
187
- const hitSlop = isRight
188
- ? // Extend hitSlop to the side of the screen when drawer is closed
189
- // This lets the user drag the drawer from the side of the screen
190
- { right: 0, width: isOpen ? undefined : swipeEdgeWidth }
191
- : { left: 0, width: isOpen ? undefined : swipeEdgeWidth };
192
-
193
- const borderRadiiStyle = isRight
194
- ? {
195
- borderTopLeftRadius: DRAWER_BORDER_RADIUS,
196
- borderBottomLeftRadius: DRAWER_BORDER_RADIUS,
197
- }
198
- : {
199
- borderTopRightRadius: DRAWER_BORDER_RADIUS,
200
- borderBottomRightRadius: DRAWER_BORDER_RADIUS,
201
- };
34
+ progress.value = open ? 1 : 0;
35
+ }, [open, progress]);
202
36
 
203
- const touchStartX = useSharedValue(0);
204
- const touchX = useSharedValue(0);
205
- const translationX = useSharedValue(getDrawerTranslationX(open));
206
- const gestureState = useSharedValue<GestureState>(GestureState.UNDETERMINED);
37
+ const drawerRef = React.useRef<View>(null);
207
38
 
208
- const handleAnimationStart = useLatestCallback((open: boolean) => {
209
- onTransitionStart?.(!open);
39
+ const onTransitionStartLatest = useLatestCallback(() => {
40
+ onTransitionStart?.(open === false);
210
41
  });
211
42
 
212
- const handleAnimationEnd = useLatestCallback(
213
- (open: boolean, finished?: boolean) => {
214
- if (!finished) return;
215
- onTransitionEnd?.(!open);
216
- }
217
- );
218
-
219
- const toggleDrawer = React.useCallback(
220
- (open: boolean, velocity?: number) => {
221
- 'worklet';
222
-
223
- const translateX = getDrawerTranslationX(open);
224
-
225
- if (velocity === undefined) {
226
- runOnJS(handleAnimationStart)(open);
227
- }
228
-
229
- touchStartX.value = 0;
230
- touchX.value = 0;
231
- translationX.value = withSpring(
232
- translateX,
233
- {
234
- velocity,
235
- stiffness: 1000,
236
- damping: 500,
237
- mass: 3,
238
- overshootClamping: true,
239
- restDisplacementThreshold: 0.01,
240
- restSpeedThreshold: 0.01,
241
- },
242
- (finished) => runOnJS(handleAnimationEnd)(open, finished)
243
- );
244
-
245
- if (open) {
246
- runOnJS(onOpen)();
247
- } else {
248
- runOnJS(onClose)();
249
- }
250
- },
251
- [
252
- getDrawerTranslationX,
253
- handleAnimationEnd,
254
- handleAnimationStart,
255
- onClose,
256
- onOpen,
257
- touchStartX,
258
- touchX,
259
- translationX,
260
- ]
261
- );
262
-
263
- React.useEffect(() => toggleDrawer(open), [open, toggleDrawer]);
264
-
265
- const onGestureEvent = useAnimatedGestureHandler<
266
- PanGestureHandlerGestureEvent,
267
- { startX: number; hasCalledOnStart: boolean }
268
- >({
269
- onStart: (event, ctx) => {
270
- ctx.hasCalledOnStart = false;
271
- ctx.startX = translationX.value;
272
- gestureState.value = event.state;
273
- touchStartX.value = event.x;
274
- },
275
- onCancel: () => {
276
- runOnJS(onGestureAbort)();
277
- },
278
- onActive: (event, ctx) => {
279
- touchX.value = event.x;
280
- translationX.value = ctx.startX + event.translationX;
281
- gestureState.value = event.state;
282
-
283
- // onStart will _always_ be called, even when the activation
284
- // criteria isn't met yet. This makes sure onGestureBegin is only
285
- // called when the criteria is really met.
286
- if (!ctx.hasCalledOnStart) {
287
- ctx.hasCalledOnStart = true;
288
- runOnJS(onGestureBegin)();
289
- }
290
- },
291
- onEnd: (event) => {
292
- gestureState.value = event.state;
293
-
294
- const nextOpen =
295
- (Math.abs(event.translationX) > SWIPE_MIN_OFFSET &&
296
- Math.abs(event.translationX) > swipeMinVelocity) ||
297
- Math.abs(event.translationX) > swipeMinDistance
298
- ? drawerPosition === 'left'
299
- ? // If swiped to right, open the drawer, otherwise close it
300
- (event.velocityX === 0 ? event.translationX : event.velocityX) > 0
301
- : // If swiped to left, open the drawer, otherwise close it
302
- (event.velocityX === 0 ? event.translationX : event.velocityX) < 0
303
- : open;
304
-
305
- toggleDrawer(nextOpen, event.velocityX);
306
- },
307
- onFinish: () => {
308
- runOnJS(onGestureFinish)();
309
- },
43
+ const onTransitionEndLatest = useLatestCallback(() => {
44
+ onTransitionEnd?.(open === false);
310
45
  });
311
46
 
312
- const translateX = useDerivedValue(() => {
313
- // Comment stolen from react-native-gesture-handler/DrawerLayout
314
- //
315
- // While closing the drawer when user starts gesture outside of its area (in greyed
316
- // out part of the window), we want the drawer to follow only once finger reaches the
317
- // edge of the drawer.
318
- // E.g. on the diagram below drawer is illustrate by X signs and the greyed out area by
319
- // dots. The touch gesture starts at '*' and moves left, touch path is indicated by
320
- // an arrow pointing left
321
- // 1) +---------------+ 2) +---------------+ 3) +---------------+ 4) +---------------+
322
- // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
323
- // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
324
- // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
325
- // |XXXXXXXX|......| |XXXXXXXX|.<-*..| |XXXXXXXX|<--*..| |XXXXX|<-----*..|
326
- // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
327
- // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
328
- // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........|
329
- // +---------------+ +---------------+ +---------------+ +---------------+
330
- //
331
- // For the above to work properly we define animated value that will keep start position
332
- // of the gesture. Then we use that value to calculate how much we need to subtract from
333
- // the translationX. If the gesture started on the greyed out area we take the distance from the
334
- // edge of the drawer to the start position. Otherwise we don't subtract at all and the
335
- // drawer be pulled back as soon as you start the pan.
336
- //
337
- // This is used only when drawerType is "front"
338
- const touchDistance =
339
- drawerType === 'front' && gestureState.value === GestureState.ACTIVE
340
- ? minmax(
341
- drawerPosition === 'left'
342
- ? touchStartX.value - drawerWidth
343
- : layout.width - drawerWidth - touchStartX.value,
344
- 0,
345
- layout.width
346
- )
347
- : 0;
348
-
349
- const translateX =
350
- drawerPosition === 'left'
351
- ? minmax(translationX.value + touchDistance, -drawerWidth, 0)
352
- : minmax(translationX.value - touchDistance, 0, drawerWidth);
353
-
354
- return translateX;
355
- });
47
+ React.useEffect(() => {
48
+ const element = drawerRef.current as HTMLDivElement | null;
356
49
 
357
- const drawerAnimatedStyle = useAnimatedStyle(() => {
358
- const distanceFromEdge = layout.width - drawerWidth;
50
+ if (element) {
51
+ element.addEventListener('transitionstart', onTransitionStartLatest);
52
+ element.addEventListener('transitionend', onTransitionEndLatest);
53
+ }
54
+ }, [onTransitionEndLatest, onTransitionStartLatest]);
359
55
 
360
- return {
361
- transform:
362
- drawerType === 'permanent'
363
- ? // Reanimated needs the property to be present, but it results in Browser bug
364
- // https://bugs.chromium.org/p/chromium/issues/detail?id=20574
365
- []
366
- : [
367
- {
368
- translateX:
369
- // The drawer stays in place when `drawerType` is `back`
370
- (drawerType === 'back' ? 0 : translateX.value) +
371
- (drawerPosition === 'left' ? 0 : distanceFromEdge),
372
- },
373
- ],
374
- };
375
- });
56
+ const isOpen = drawerType === 'permanent' ? true : open;
57
+ const isRight = drawerPosition === 'right';
376
58
 
377
- const contentAnimatedStyle = useAnimatedStyle(() => {
378
- return {
379
- transform:
380
- drawerType === 'permanent'
381
- ? // Reanimated needs the property to be present, but it results in Browser bug
382
- // https://bugs.chromium.org/p/chromium/issues/detail?id=20574
383
- []
384
- : [
385
- {
386
- translateX:
387
- // The screen content stays in place when `drawerType` is `front`
388
- drawerType === 'front'
59
+ const drawerAnimatedStyle =
60
+ drawerType !== 'permanent'
61
+ ? {
62
+ transition: 'transform 0.3s',
63
+ transform: [
64
+ {
65
+ // The drawer stays in place at open position when `drawerType` is `back`
66
+ translateX:
67
+ open || drawerType === 'back'
68
+ ? drawerPosition === 'left'
389
69
  ? 0
390
- : translateX.value +
391
- drawerWidth * (drawerPosition === 'left' ? 1 : -1),
392
- },
393
- ],
394
- };
395
- });
396
-
397
- const progress = useDerivedValue(() => {
398
- return drawerType === 'permanent'
399
- ? 1
400
- : interpolate(
401
- translateX.value,
402
- [getDrawerTranslationX(false), getDrawerTranslationX(true)],
403
- [0, 1]
404
- );
405
- });
70
+ : layout.width - drawerWidth
71
+ : drawerPosition === 'left'
72
+ ? -drawerWidth
73
+ : layout.width,
74
+ },
75
+ ],
76
+ }
77
+ : null;
78
+
79
+ const contentAnimatedStyle =
80
+ drawerType !== 'permanent'
81
+ ? {
82
+ transition: 'transform 0.3s',
83
+ transform: [
84
+ {
85
+ translateX: open
86
+ ? // The screen content stays in place when `drawerType` is `front`
87
+ drawerType === 'front'
88
+ ? 0
89
+ : drawerWidth * (drawerPosition === 'left' ? 1 : -1)
90
+ : 0,
91
+ },
92
+ ],
93
+ }
94
+ : null;
406
95
 
407
96
  return (
408
- <GestureHandlerRootView style={[styles.container, style]}>
97
+ <View style={[styles.container, style]}>
409
98
  <DrawerProgressContext.Provider value={progress}>
410
- <PanGestureHandler
411
- activeOffsetX={[-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]}
412
- failOffsetY={[-SWIPE_MIN_OFFSET, SWIPE_MIN_OFFSET]}
413
- hitSlop={hitSlop}
414
- enabled={drawerType !== 'permanent' && swipeEnabled}
415
- onGestureEvent={onGestureEvent}
416
- {...gestureHandlerProps}
99
+ <View
100
+ style={[
101
+ styles.main,
102
+ {
103
+ flexDirection:
104
+ drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
105
+ },
106
+ ]}
417
107
  >
418
- {/* Immediate child of gesture handler needs to be an Animated.View */}
419
- <Animated.View
108
+ <View style={[styles.content, contentAnimatedStyle]}>
109
+ <View
110
+ accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
111
+ importantForAccessibility={
112
+ isOpen && drawerType !== 'permanent'
113
+ ? 'no-hide-descendants'
114
+ : 'auto'
115
+ }
116
+ style={styles.content}
117
+ >
118
+ {children}
119
+ </View>
120
+ {drawerType !== 'permanent' ? (
121
+ <Overlay
122
+ open={open}
123
+ progress={progress}
124
+ onPress={() => onClose()}
125
+ style={overlayStyle}
126
+ accessibilityLabel={overlayAccessibilityLabel}
127
+ />
128
+ ) : null}
129
+ </View>
130
+ <View
131
+ ref={drawerRef}
420
132
  style={[
421
- styles.main,
133
+ styles.drawer,
422
134
  {
423
- flexDirection:
424
- drawerType === 'permanent' && !isRight
425
- ? 'row-reverse'
426
- : 'row',
135
+ width: drawerWidth,
136
+ position: drawerType === 'permanent' ? 'relative' : 'absolute',
137
+ zIndex: drawerType === 'back' ? -1 : 0,
427
138
  },
139
+ drawerAnimatedStyle,
140
+ drawerStyle,
428
141
  ]}
429
142
  >
430
- <Animated.View style={[styles.content, contentAnimatedStyle]}>
431
- <View
432
- accessibilityElementsHidden={
433
- isOpen && drawerType !== 'permanent'
434
- }
435
- importantForAccessibility={
436
- isOpen && drawerType !== 'permanent'
437
- ? 'no-hide-descendants'
438
- : 'auto'
439
- }
440
- style={styles.content}
441
- >
442
- {children}
443
- </View>
444
- {drawerType !== 'permanent' ? (
445
- <Overlay
446
- progress={progress}
447
- onPress={() => toggleDrawer(false)}
448
- style={overlayStyle}
449
- accessibilityLabel={overlayAccessibilityLabel}
450
- />
451
- ) : null}
452
- </Animated.View>
453
- <Animated.View
454
- removeClippedSubviews={Platform.OS !== 'ios'}
455
- style={[
456
- styles.drawer,
457
- {
458
- width: drawerWidth,
459
- position:
460
- drawerType === 'permanent' ? 'relative' : 'absolute',
461
- zIndex: drawerType === 'back' ? -1 : 0,
462
- },
463
- drawerType === 'permanent' ? null : borderRadiiStyle,
464
- drawerAnimatedStyle,
465
- drawerStyle,
466
- ]}
467
- >
468
- {renderDrawerContent()}
469
- </Animated.View>
470
- </Animated.View>
471
- </PanGestureHandler>
143
+ {renderDrawerContent()}
144
+ </View>
145
+ </View>
472
146
  </DrawerProgressContext.Provider>
473
- </GestureHandlerRootView>
147
+ </View>
474
148
  );
475
149
  }
476
150
 
@@ -489,11 +163,5 @@ const styles = StyleSheet.create({
489
163
  },
490
164
  main: {
491
165
  flex: 1,
492
- ...Platform.select({
493
- // FIXME: We need to hide `overflowX` on Web so the translated content doesn't show offscreen.
494
- // But adding `overflowX: 'hidden'` prevents content from collapsing the URL bar.
495
- web: null,
496
- default: { overflow: 'hidden' },
497
- }),
498
166
  },
499
167
  });
@@ -0,0 +1,63 @@
1
+ import * as React from 'react';
2
+ import { Pressable, StyleSheet } from 'react-native';
3
+ import Animated, {
4
+ useAnimatedProps,
5
+ useAnimatedStyle,
6
+ } from 'react-native-reanimated';
7
+
8
+ import type { OverlayProps } from '../types';
9
+
10
+ const PROGRESS_EPSILON = 0.05;
11
+
12
+ export function Overlay({
13
+ progress,
14
+ onPress,
15
+ style,
16
+ accessibilityLabel = 'Close drawer',
17
+ ...rest
18
+ }: OverlayProps) {
19
+ const animatedStyle = useAnimatedStyle(() => {
20
+ return {
21
+ opacity: progress.value,
22
+ // We don't want the user to be able to press through the overlay when drawer is open
23
+ // We can send the overlay behind the screen to avoid it
24
+ zIndex: progress.value > PROGRESS_EPSILON ? 0 : -1,
25
+ };
26
+ });
27
+
28
+ const animatedProps = useAnimatedProps(() => {
29
+ const active = progress.value > PROGRESS_EPSILON;
30
+
31
+ return {
32
+ pointerEvents: active ? 'auto' : 'none',
33
+ accessibilityElementsHidden: !active,
34
+ importantForAccessibility: active ? 'auto' : 'no-hide-descendants',
35
+ } as const;
36
+ });
37
+
38
+ return (
39
+ <Animated.View
40
+ {...rest}
41
+ style={[styles.overlay, animatedStyle, style]}
42
+ animatedProps={animatedProps}
43
+ >
44
+ <Pressable
45
+ onPress={onPress}
46
+ style={styles.pressable}
47
+ accessibilityRole="button"
48
+ accessibilityLabel={accessibilityLabel}
49
+ />
50
+ </Animated.View>
51
+ );
52
+ }
53
+
54
+ const styles = StyleSheet.create({
55
+ overlay: {
56
+ ...StyleSheet.absoluteFillObject,
57
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
58
+ },
59
+ pressable: {
60
+ flex: 1,
61
+ pointerEvents: 'auto',
62
+ },
63
+ });