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