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

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 +42 -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 +55 -279
  7. package/lib/commonjs/views/Drawer.js.map +1 -1
  8. package/lib/commonjs/views/Drawer.native.js +324 -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 +36 -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 +56 -278
  20. package/lib/module/views/Drawer.js.map +1 -1
  21. package/lib/module/views/Drawer.native.js +315 -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 +49 -0
  49. package/src/utils/useDrawerProgress.tsx +2 -2
  50. package/src/utils/useFakeSharedValue.tsx +49 -0
  51. package/src/views/Drawer.native.tsx +466 -0
  52. package/src/views/Drawer.tsx +117 -433
  53. package/src/views/Overlay.native.tsx +63 -0
  54. package/src/views/Overlay.tsx +26 -59
@@ -1,476 +1,166 @@
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
- };
11
+ const DRAWER_BORDER_RADIUS = 16;
66
12
 
67
13
  export function Drawer({
68
14
  layout: customLayout,
69
- drawerPosition = I18nManager.getConstants().isRTL ? 'right' : 'left',
15
+ drawerPosition = 'left',
70
16
  drawerStyle,
71
- drawerType = Platform.select({ ios: 'slide', default: 'front' }),
72
- gestureHandlerProps,
73
- hideStatusBarOnOpen = false,
74
- keyboardDismissMode = 'on-drag',
17
+ drawerType = 'front',
75
18
  onClose,
76
- onOpen,
77
- onGestureStart,
78
- onGestureCancel,
79
- onGestureEnd,
80
19
  onTransitionStart,
81
20
  onTransitionEnd,
82
21
  open,
83
22
  overlayStyle,
84
23
  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
24
  renderDrawerContent,
93
25
  children,
94
26
  style,
95
27
  }: DrawerProps) {
96
- // FIXME: temporary workaround for useSafeAreaFrame not updating on Web
97
28
  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
29
 
125
- if (drawerPosition === 'left') {
126
- return open ? 0 : -drawerWidth;
127
- }
128
-
129
- return open ? 0 : drawerWidth;
130
- },
131
- [drawerPosition, drawerWidth]
132
- );
30
+ const layout = customLayout ?? windowDimensions;
31
+ const drawerWidth = getDrawerWidth({ layout, drawerStyle });
133
32
 
134
- const hideStatusBar = React.useCallback(
135
- (hide: boolean) => {
136
- if (hideStatusBarOnOpen) {
137
- StatusBar.setHidden(hide, statusBarAnimation);
138
- }
139
- },
140
- [hideStatusBarOnOpen, statusBarAnimation]
141
- );
33
+ const progress = useFakeSharedValue(open ? 1 : 0);
142
34
 
143
35
  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
- };
36
+ progress.value = open ? 1 : 0;
37
+ }, [open, progress]);
202
38
 
203
- const touchStartX = useSharedValue(0);
204
- const touchX = useSharedValue(0);
205
- const translationX = useSharedValue(getDrawerTranslationX(open));
206
- const gestureState = useSharedValue<GestureState>(GestureState.UNDETERMINED);
39
+ const drawerRef = React.useRef<View>(null);
207
40
 
208
- const handleAnimationStart = useLatestCallback((open: boolean) => {
209
- onTransitionStart?.(!open);
41
+ const onTransitionStartLatest = useLatestCallback(() => {
42
+ onTransitionStart?.(open === false);
210
43
  });
211
44
 
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
- },
45
+ const onTransitionEndLatest = useLatestCallback(() => {
46
+ onTransitionEnd?.(open === false);
310
47
  });
311
48
 
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
- });
49
+ React.useEffect(() => {
50
+ const element = drawerRef.current as HTMLDivElement | null;
356
51
 
357
- const drawerAnimatedStyle = useAnimatedStyle(() => {
358
- const distanceFromEdge = layout.width - drawerWidth;
52
+ if (element) {
53
+ element.addEventListener('transitionstart', onTransitionStartLatest);
54
+ element.addEventListener('transitionend', onTransitionEndLatest);
55
+ }
56
+ }, [onTransitionEndLatest, onTransitionStartLatest]);
359
57
 
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
- });
58
+ const isOpen = drawerType === 'permanent' ? true : open;
59
+ const isRight = drawerPosition === 'right';
376
60
 
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'
61
+ const borderRadiiStyle =
62
+ drawerType !== 'permanent'
63
+ ? isRight
64
+ ? {
65
+ borderTopLeftRadius: DRAWER_BORDER_RADIUS,
66
+ borderBottomLeftRadius: DRAWER_BORDER_RADIUS,
67
+ }
68
+ : {
69
+ borderTopRightRadius: DRAWER_BORDER_RADIUS,
70
+ borderBottomRightRadius: DRAWER_BORDER_RADIUS,
71
+ }
72
+ : null;
73
+
74
+ const drawerAnimatedStyle =
75
+ drawerType !== 'permanent'
76
+ ? {
77
+ transition: 'transform 0.3s',
78
+ transform: [
79
+ {
80
+ // The drawer stays in place at open position when `drawerType` is `back`
81
+ translateX:
82
+ open || drawerType === 'back'
83
+ ? drawerPosition === 'left'
389
84
  ? 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
- });
85
+ : layout.width - drawerWidth
86
+ : drawerPosition === 'left'
87
+ ? -drawerWidth
88
+ : layout.width,
89
+ },
90
+ ],
91
+ }
92
+ : null;
93
+
94
+ const contentAnimatedStyle =
95
+ drawerType !== 'permanent'
96
+ ? {
97
+ transition: 'transform 0.3s',
98
+ transform: [
99
+ {
100
+ translateX: open
101
+ ? // The screen content stays in place when `drawerType` is `front`
102
+ drawerType === 'front'
103
+ ? 0
104
+ : drawerWidth * (drawerPosition === 'left' ? 1 : -1)
105
+ : 0,
106
+ },
107
+ ],
108
+ }
109
+ : null;
406
110
 
407
111
  return (
408
- <GestureHandlerRootView style={[styles.container, style]}>
112
+ <View style={[styles.container, style]}>
409
113
  <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}
114
+ <View
115
+ style={[
116
+ styles.main,
117
+ {
118
+ flexDirection:
119
+ drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
120
+ },
121
+ ]}
417
122
  >
418
- {/* Immediate child of gesture handler needs to be an Animated.View */}
419
- <Animated.View
123
+ <View style={[styles.content, contentAnimatedStyle]}>
124
+ <View
125
+ accessibilityElementsHidden={isOpen && drawerType !== 'permanent'}
126
+ importantForAccessibility={
127
+ isOpen && drawerType !== 'permanent'
128
+ ? 'no-hide-descendants'
129
+ : 'auto'
130
+ }
131
+ style={styles.content}
132
+ >
133
+ {children}
134
+ </View>
135
+ {drawerType !== 'permanent' ? (
136
+ <Overlay
137
+ open={open}
138
+ progress={progress}
139
+ onPress={() => onClose()}
140
+ style={overlayStyle}
141
+ accessibilityLabel={overlayAccessibilityLabel}
142
+ />
143
+ ) : null}
144
+ </View>
145
+ <View
146
+ ref={drawerRef}
420
147
  style={[
421
- styles.main,
148
+ styles.drawer,
422
149
  {
423
- flexDirection:
424
- drawerType === 'permanent' && !isRight
425
- ? 'row-reverse'
426
- : 'row',
150
+ width: drawerWidth,
151
+ position: drawerType === 'permanent' ? 'relative' : 'absolute',
152
+ zIndex: drawerType === 'back' ? -1 : 0,
427
153
  },
154
+ borderRadiiStyle,
155
+ drawerAnimatedStyle,
156
+ drawerStyle,
428
157
  ]}
429
158
  >
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>
159
+ {renderDrawerContent()}
160
+ </View>
161
+ </View>
472
162
  </DrawerProgressContext.Provider>
473
- </GestureHandlerRootView>
163
+ </View>
474
164
  );
475
165
  }
476
166
 
@@ -489,11 +179,5 @@ const styles = StyleSheet.create({
489
179
  },
490
180
  main: {
491
181
  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
182
  },
499
183
  });
@@ -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
+ });