@react-navigation/drawer 8.0.0-alpha.4 → 8.0.0-alpha.41

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 (62) hide show
  1. package/lib/module/index.js.map +1 -1
  2. package/lib/module/navigators/createDrawerNavigator.js +3 -7
  3. package/lib/module/navigators/createDrawerNavigator.js.map +1 -1
  4. package/lib/module/utils/useDrawerStatus.js +1 -1
  5. package/lib/module/utils/useDrawerStatus.js.map +1 -1
  6. package/lib/module/views/DrawerContent.js +4 -2
  7. package/lib/module/views/DrawerContent.js.map +1 -1
  8. package/lib/module/views/DrawerContentScrollView.js +30 -13
  9. package/lib/module/views/DrawerContentScrollView.js.map +1 -1
  10. package/lib/module/views/DrawerItem.js +25 -7
  11. package/lib/module/views/DrawerItem.js.map +1 -1
  12. package/lib/module/views/DrawerItemList.js +17 -7
  13. package/lib/module/views/DrawerItemList.js.map +1 -1
  14. package/lib/module/views/DrawerToggleButton.js +27 -10
  15. package/lib/module/views/DrawerToggleButton.js.map +1 -1
  16. package/lib/module/views/DrawerView.js +24 -17
  17. package/lib/module/views/DrawerView.js.map +1 -1
  18. package/lib/typescript/src/index.d.ts +11 -11
  19. package/lib/typescript/src/index.d.ts.map +1 -1
  20. package/lib/typescript/src/navigators/createDrawerNavigator.d.ts +9 -15
  21. package/lib/typescript/src/navigators/createDrawerNavigator.d.ts.map +1 -1
  22. package/lib/typescript/src/types.d.ts +24 -25
  23. package/lib/typescript/src/types.d.ts.map +1 -1
  24. package/lib/typescript/src/views/DrawerContent.d.ts +2 -2
  25. package/lib/typescript/src/views/DrawerContent.d.ts.map +1 -1
  26. package/lib/typescript/src/views/DrawerContentScrollView.d.ts +5 -2
  27. package/lib/typescript/src/views/DrawerContentScrollView.d.ts.map +1 -1
  28. package/lib/typescript/src/views/DrawerItem.d.ts +18 -17
  29. package/lib/typescript/src/views/DrawerItem.d.ts.map +1 -1
  30. package/lib/typescript/src/views/DrawerItemList.d.ts +1 -1
  31. package/lib/typescript/src/views/DrawerItemList.d.ts.map +1 -1
  32. package/lib/typescript/src/views/DrawerToggleButton.d.ts +12 -7
  33. package/lib/typescript/src/views/DrawerToggleButton.d.ts.map +1 -1
  34. package/lib/typescript/src/views/DrawerView.d.ts +3 -2
  35. package/lib/typescript/src/views/DrawerView.d.ts.map +1 -1
  36. package/package.json +26 -26
  37. package/src/index.tsx +1 -0
  38. package/src/navigators/createDrawerNavigator.tsx +9 -40
  39. package/src/types.tsx +30 -25
  40. package/src/utils/useDrawerStatus.tsx +1 -1
  41. package/src/views/DrawerContent.tsx +6 -2
  42. package/src/views/DrawerContentScrollView.tsx +31 -15
  43. package/src/views/DrawerItem.tsx +56 -22
  44. package/src/views/DrawerItemList.tsx +22 -8
  45. package/src/views/DrawerToggleButton.tsx +48 -22
  46. package/src/views/DrawerView.tsx +37 -20
  47. package/lib/module/views/assets/toggle-drawer-icon@1x.android.png +0 -0
  48. package/lib/module/views/assets/toggle-drawer-icon@1x.ios.png +0 -0
  49. package/lib/module/views/assets/toggle-drawer-icon@2x.android.png +0 -0
  50. package/lib/module/views/assets/toggle-drawer-icon@2x.ios.png +0 -0
  51. package/lib/module/views/assets/toggle-drawer-icon@3x.android.png +0 -0
  52. package/lib/module/views/assets/toggle-drawer-icon@3x.ios.png +0 -0
  53. package/lib/module/views/assets/toggle-drawer-icon@4x.android.png +0 -0
  54. package/lib/module/views/assets/toggle-drawer-icon@4x.ios.png +0 -0
  55. package/src/views/assets/toggle-drawer-icon@1x.android.png +0 -0
  56. package/src/views/assets/toggle-drawer-icon@1x.ios.png +0 -0
  57. package/src/views/assets/toggle-drawer-icon@2x.android.png +0 -0
  58. package/src/views/assets/toggle-drawer-icon@2x.ios.png +0 -0
  59. package/src/views/assets/toggle-drawer-icon@3x.android.png +0 -0
  60. package/src/views/assets/toggle-drawer-icon@3x.ios.png +0 -0
  61. package/src/views/assets/toggle-drawer-icon@4x.android.png +0 -0
  62. package/src/views/assets/toggle-drawer-icon@4x.ios.png +0 -0
package/src/types.tsx CHANGED
@@ -1,4 +1,5 @@
1
- import type { HeaderOptions } from '@react-navigation/elements';
1
+ import type { HeaderOptions, Icon } from '@react-navigation/elements';
2
+ import type { Screen } from '@react-navigation/elements/internal';
2
3
  import type {
3
4
  DefaultNavigatorOptions,
4
5
  Descriptor,
@@ -13,7 +14,7 @@ import type {
13
14
  Theme,
14
15
  } from '@react-navigation/native';
15
16
  import type { ColorValue, StyleProp, TextStyle, ViewStyle } from 'react-native';
16
- import type { PanGesture } from 'react-native-gesture-handler';
17
+ import type { PanGestureConfig } from 'react-native-gesture-handler';
17
18
 
18
19
  export type Scene = {
19
20
  route: Route<string>;
@@ -29,12 +30,6 @@ export type DrawerNavigationConfig = {
29
30
  * Defaults to `DrawerContent`.
30
31
  */
31
32
  drawerContent?: (props: DrawerContentComponentProps) => React.ReactNode;
32
- /**
33
- * Whether inactive screens should be detached from the view hierarchy to save memory.
34
- * Make sure to call `enableScreens` from `react-native-screens` to make it work.
35
- * Defaults to `true`.
36
- */
37
- detachInactiveScreens?: boolean;
38
33
  };
39
34
 
40
35
  export type DrawerNavigationOptions = HeaderOptions & {
@@ -70,13 +65,15 @@ export type DrawerNavigationOptions = HeaderOptions & {
70
65
  | ((props: { color: ColorValue; focused: boolean }) => React.ReactNode);
71
66
 
72
67
  /**
73
- * A function that given { focused: boolean, color: ColorValue } returns a React.Node to display an icon the drawer.
68
+ * Icon to display for the drawer item.
74
69
  */
75
- drawerIcon?: (props: {
76
- color: ColorValue;
77
- size: number;
78
- focused: boolean;
79
- }) => React.ReactNode;
70
+ drawerIcon?:
71
+ | Icon
72
+ | ((props: {
73
+ color: ColorValue;
74
+ size: number;
75
+ focused: boolean;
76
+ }) => Icon | React.ReactNode);
80
77
 
81
78
  /**
82
79
  * Color for the icon and label in the active item in the drawer.
@@ -108,6 +105,11 @@ export type DrawerNavigationOptions = HeaderOptions & {
108
105
  */
109
106
  drawerItemStyle?: StyleProp<ViewStyle>;
110
107
 
108
+ /**
109
+ * ID to locate this drawer item in tests.
110
+ */
111
+ drawerItemTestID?: string;
112
+
111
113
  /**
112
114
  * Style object to apply to the `Text` inside content section which renders a label.
113
115
  */
@@ -143,7 +145,7 @@ export type DrawerNavigationOptions = HeaderOptions & {
143
145
  *
144
146
  * Defaults to `slide` on iOS and `front` on other platforms.
145
147
  */
146
- drawerType?: 'front' | 'back' | 'slide' | 'permanent';
148
+ drawerType?: 'front' | 'back' | 'slide' | 'permanent' | undefined;
147
149
 
148
150
  /**
149
151
  * Whether the statusbar should be hidden when the drawer is pulled or opens,
@@ -170,12 +172,12 @@ export type DrawerNavigationOptions = HeaderOptions & {
170
172
  /**
171
173
  * Style object for the component wrapping the screen content.
172
174
  */
173
- sceneStyle?: StyleProp<ViewStyle>;
175
+ sceneStyle?: React.ComponentProps<typeof Screen>['style'];
174
176
 
175
177
  /**
176
- * Function to modify the pan gesture handler via RNGH properties API.
178
+ * Function to modify the pan gesture config.
177
179
  */
178
- configureGestureHandler?: (gesture: PanGesture) => PanGesture;
180
+ configureGestureHandler?: (gesture: PanGestureConfig) => PanGestureConfig;
179
181
 
180
182
  /**
181
183
  * Whether you can use swipe gestures to open or close the drawer.
@@ -208,13 +210,17 @@ export type DrawerNavigationOptions = HeaderOptions & {
208
210
  popToTopOnBlur?: boolean;
209
211
 
210
212
  /**
211
- * Whether inactive screens should be suspended from re-rendering. Defaults to `false`.
212
- * Defaults to `true` when `enableFreeze()` is run at the top of the application.
213
- * Requires `react-native-screens` version >=3.16.0.
213
+ * What should happen when screens become inactive.
214
+ * - `pause`: Effects are cleaned up.
215
+ * - `none`: Screen renders normally
216
+ *
217
+ * Defaults to `pause`.
214
218
  *
215
- * Only supported on iOS and Android.
219
+ * If you set `lazy: false` or preload a screen,
220
+ * It won't be paused until after the first time it becomes focused.
221
+ * This makes sure that effects are run to initialize the screen.
216
222
  */
217
- freezeOnBlur?: boolean;
223
+ inactiveBehavior?: 'pause' | 'none';
218
224
  };
219
225
 
220
226
  export type DrawerContentComponentProps = {
@@ -307,11 +313,10 @@ export type DrawerDescriptor = Descriptor<
307
313
  export type DrawerDescriptorMap = Record<string, DrawerDescriptor>;
308
314
 
309
315
  export type DrawerProps = {
310
- dimensions: { width: number; height: number };
311
316
  drawerPosition: 'left' | 'right';
312
317
  drawerStyle?: StyleProp<ViewStyle>;
313
318
  drawerType: 'front' | 'back' | 'slide' | 'permanent';
314
- configureGestureHandler?: (gesture: PanGesture) => PanGesture;
319
+ configureGestureHandler?: (gesture: PanGestureConfig) => PanGestureConfig;
315
320
  hideStatusBarOnOpen: boolean;
316
321
  keyboardDismissMode: 'none' | 'on-drag';
317
322
  onClose: () => void;
@@ -8,7 +8,7 @@ import { DrawerStatusContext } from './DrawerStatusContext';
8
8
  * Returns 'open' if the drawer is open, 'closed' if the drawer is closed.
9
9
  */
10
10
  export function useDrawerStatus(): DrawerStatus {
11
- const drawerStatus = React.useContext(DrawerStatusContext);
11
+ const drawerStatus = React.use(DrawerStatusContext);
12
12
 
13
13
  if (drawerStatus === undefined) {
14
14
  throw new Error(
@@ -8,8 +8,12 @@ export function DrawerContent({
8
8
  ...rest
9
9
  }: DrawerContentComponentProps) {
10
10
  const focusedRoute = state.routes[state.index];
11
- const focusedDescriptor = descriptors[focusedRoute.key];
12
- const focusedOptions = focusedDescriptor.options;
11
+
12
+ if (focusedRoute == null) {
13
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
14
+ }
15
+
16
+ const focusedOptions = descriptors[focusedRoute.key]?.options ?? {};
13
17
 
14
18
  const { drawerContentStyle, drawerContentContainerStyle } = focusedOptions;
15
19
 
@@ -1,21 +1,31 @@
1
- import { useLocale } from '@react-navigation/native';
1
+ import { UNSTABLE_CornerInset, useLocale } from '@react-navigation/native';
2
2
  import * as React from 'react';
3
- import { ScrollView, type ScrollViewProps, StyleSheet } from 'react-native';
3
+ import {
4
+ Platform,
5
+ ScrollView,
6
+ type ScrollViewProps,
7
+ StyleSheet,
8
+ } from 'react-native';
4
9
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
10
 
6
11
  import { DrawerPositionContext } from '../utils/DrawerPositionContext';
7
12
 
8
13
  type Props = ScrollViewProps & {
9
14
  children: React.ReactNode;
15
+ ref?: React.Ref<ScrollView>;
10
16
  };
11
17
 
12
18
  const SPACING = 12;
13
19
 
14
- function DrawerContentScrollViewInner(
15
- { contentContainerStyle, style, children, ...rest }: Props,
16
- ref?: React.Ref<ScrollView>
17
- ) {
18
- const drawerPosition = React.useContext(DrawerPositionContext);
20
+ export function DrawerContentScrollView({
21
+ contentInsetAdjustmentBehavior = 'automatic',
22
+ contentContainerStyle,
23
+ style,
24
+ children,
25
+ ref,
26
+ ...rest
27
+ }: Props) {
28
+ const drawerPosition = React.use(DrawerPositionContext);
19
29
  const insets = useSafeAreaInsets();
20
30
  const { direction } = useLocale();
21
31
 
@@ -28,26 +38,32 @@ function DrawerContentScrollViewInner(
28
38
  <ScrollView
29
39
  {...rest}
30
40
  ref={ref}
41
+ contentInsetAdjustmentBehavior={contentInsetAdjustmentBehavior}
31
42
  contentContainerStyle={[
32
43
  {
33
- paddingTop: SPACING + insets.top,
34
- paddingBottom: SPACING + insets.bottom,
35
- paddingStart: SPACING + (!isRight ? insets.left : 0),
36
- paddingEnd: SPACING + (isRight ? insets.right : 0),
44
+ paddingTop:
45
+ SPACING + Platform.select({ ios: 0, default: insets.top }),
46
+ paddingBottom:
47
+ SPACING + Platform.select({ ios: 0, default: insets.bottom }),
48
+ paddingStart:
49
+ SPACING +
50
+ (!isRight ? Platform.select({ ios: 0, default: insets.left }) : 0),
51
+ paddingEnd:
52
+ SPACING +
53
+ (isRight ? Platform.select({ ios: 0, default: insets.right }) : 0),
37
54
  },
38
55
  contentContainerStyle,
39
56
  ]}
40
57
  style={[styles.container, style]}
41
58
  >
59
+ {Platform.OS === 'ios' && contentInsetAdjustmentBehavior !== 'never' && (
60
+ <UNSTABLE_CornerInset direction="vertical" edge="top" />
61
+ )}
42
62
  {children}
43
63
  </ScrollView>
44
64
  );
45
65
  }
46
66
 
47
- export const DrawerContentScrollView = React.forwardRef(
48
- DrawerContentScrollViewInner
49
- );
50
-
51
67
  const styles = StyleSheet.create({
52
68
  container: {
53
69
  flex: 1,
@@ -1,4 +1,9 @@
1
- import { PlatformPressable, Text } from '@react-navigation/elements';
1
+ import {
2
+ type Icon,
3
+ PlatformIcon,
4
+ PlatformPressable,
5
+ Text,
6
+ } from '@react-navigation/elements';
2
7
  import { Color } from '@react-navigation/elements/internal';
3
8
  import { type Route, useTheme } from '@react-navigation/native';
4
9
  import * as React from 'react';
@@ -16,11 +21,11 @@ type Props = {
16
21
  /**
17
22
  * The route object which should be specified by the drawer item.
18
23
  */
19
- route?: Route<string>;
24
+ route?: Route<string> | undefined;
20
25
  /**
21
26
  * The `href` to use for the anchor tag on web
22
27
  */
23
- href?: string;
28
+ href?: string | undefined;
24
29
  /**
25
30
  * The label text of the item.
26
31
  */
@@ -30,15 +35,18 @@ type Props = {
30
35
  /**
31
36
  * Icon to display for the `DrawerItem`.
32
37
  */
33
- icon?: (props: {
34
- focused: boolean;
35
- size: number;
36
- color: ColorValue;
37
- }) => React.ReactNode;
38
+ icon?:
39
+ | Icon
40
+ | ((props: {
41
+ focused: boolean;
42
+ size: number;
43
+ color: ColorValue;
44
+ }) => Icon | React.ReactNode)
45
+ | undefined;
38
46
  /**
39
47
  * Whether to highlight the drawer item as active.
40
48
  */
41
- focused?: boolean;
49
+ focused?: boolean | undefined;
42
50
  /**
43
51
  * Function to execute on press.
44
52
  */
@@ -46,54 +54,54 @@ type Props = {
46
54
  /**
47
55
  * Color for the icon and label when the item is active.
48
56
  */
49
- activeTintColor?: ColorValue;
57
+ activeTintColor?: ColorValue | undefined;
50
58
  /**
51
59
  * Color for the icon and label when the item is inactive.
52
60
  */
53
- inactiveTintColor?: ColorValue;
61
+ inactiveTintColor?: ColorValue | undefined;
54
62
  /**
55
63
  * Background color for item when its active.
56
64
  */
57
- activeBackgroundColor?: ColorValue;
65
+ activeBackgroundColor?: ColorValue | undefined;
58
66
  /**
59
67
  * Background color for item when its inactive.
60
68
  */
61
- inactiveBackgroundColor?: ColorValue;
69
+ inactiveBackgroundColor?: ColorValue | undefined;
62
70
  /**
63
71
  * Color of the touchable effect on press.
64
72
  * Only supported on Android.
65
73
  *
66
74
  * @platform android
67
75
  */
68
- pressColor?: ColorValue;
76
+ pressColor?: ColorValue | undefined;
69
77
  /**
70
78
  * Opacity of the touchable effect on press.
71
79
  * Only supported on iOS.
72
80
  *
73
81
  * @platform ios
74
82
  */
75
- pressOpacity?: number;
83
+ pressOpacity?: number | undefined;
76
84
  /**
77
85
  * Style object for the label element.
78
86
  */
79
- labelStyle?: StyleProp<TextStyle>;
87
+ labelStyle?: StyleProp<TextStyle> | undefined;
80
88
  /**
81
89
  * Style object for the wrapper element.
82
90
  */
83
- style?: StyleProp<ViewStyle>;
91
+ style?: StyleProp<ViewStyle> | undefined;
84
92
  /**
85
93
  * Whether label font should scale to respect Text Size accessibility settings.
86
94
  */
87
- allowFontScaling?: boolean;
95
+ allowFontScaling?: boolean | undefined;
88
96
 
89
97
  /**
90
98
  * Accessibility label for drawer item.
91
99
  */
92
- accessibilityLabel?: string;
100
+ accessibilityLabel?: string | undefined;
93
101
  /**
94
102
  * ID to locate this drawer item in tests.
95
103
  */
96
- testID?: string;
104
+ testID?: string | undefined;
97
105
  };
98
106
 
99
107
  /**
@@ -134,7 +142,30 @@ export function DrawerItem(props: Props) {
134
142
  'rgba(0, 0, 0, 0.06)')
135
143
  : inactiveBackgroundColor;
136
144
 
137
- const iconNode = icon ? icon({ size: 24, focused, color }) : null;
145
+ const iconNode = React.useMemo(() => {
146
+ if (!icon) {
147
+ return null;
148
+ }
149
+
150
+ const size = 24;
151
+
152
+ const iconValue =
153
+ typeof icon === 'function' ? icon({ size, focused, color }) : icon;
154
+
155
+ if (React.isValidElement(iconValue)) {
156
+ return iconValue;
157
+ }
158
+
159
+ if (
160
+ typeof iconValue === 'object' &&
161
+ iconValue != null &&
162
+ 'type' in iconValue
163
+ ) {
164
+ return <PlatformIcon icon={iconValue} size={size} color={color} />;
165
+ }
166
+
167
+ return null;
168
+ }, [icon, focused, color]);
138
169
 
139
170
  return (
140
171
  <View
@@ -147,7 +178,10 @@ export function DrawerItem(props: Props) {
147
178
  onPress={onPress}
148
179
  role="button"
149
180
  aria-label={accessibilityLabel}
150
- aria-selected={focused}
181
+ aria-current={
182
+ Platform.OS === 'web' ? (focused ? 'page' : undefined) : undefined
183
+ }
184
+ aria-selected={Platform.OS === 'web' ? undefined : focused}
151
185
  pressColor={pressColor}
152
186
  pressOpacity={pressOpacity}
153
187
  hoverEffect={typeof color === 'string' ? { color } : undefined}
@@ -23,8 +23,12 @@ export function DrawerItemList({ state, navigation, descriptors }: Props) {
23
23
  const { buildHref } = useLinkBuilder();
24
24
 
25
25
  const focusedRoute = state.routes[state.index];
26
- const focusedDescriptor = descriptors[focusedRoute.key];
27
- const focusedOptions = focusedDescriptor.options;
26
+
27
+ if (focusedRoute == null) {
28
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
29
+ }
30
+
31
+ const focusedOptions = descriptors[focusedRoute.key]?.options ?? {};
28
32
 
29
33
  const {
30
34
  drawerActiveTintColor,
@@ -34,6 +38,12 @@ export function DrawerItemList({ state, navigation, descriptors }: Props) {
34
38
  } = focusedOptions;
35
39
 
36
40
  return state.routes.map((route, i) => {
41
+ const descriptor = descriptors[route.key];
42
+
43
+ if (descriptor == null) {
44
+ throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
45
+ }
46
+
37
47
  const focused = i === state.index;
38
48
 
39
49
  const onPress = () => {
@@ -44,11 +54,13 @@ export function DrawerItemList({ state, navigation, descriptors }: Props) {
44
54
  });
45
55
 
46
56
  if (!event.defaultPrevented) {
47
- navigation.dispatch({
48
- ...(focused
49
- ? DrawerActions.closeDrawer()
50
- : CommonActions.navigate(route)),
51
- target: state.key,
57
+ React.startTransition(() => {
58
+ navigation.dispatch({
59
+ ...(focused
60
+ ? DrawerActions.closeDrawer()
61
+ : CommonActions.navigate(route.name, route.params)),
62
+ target: state.key,
63
+ });
52
64
  });
53
65
  }
54
66
  };
@@ -59,8 +71,9 @@ export function DrawerItemList({ state, navigation, descriptors }: Props) {
59
71
  drawerIcon,
60
72
  drawerLabelStyle,
61
73
  drawerItemStyle,
74
+ drawerItemTestID,
62
75
  drawerAllowFontScaling,
63
- } = descriptors[route.key].options;
76
+ } = descriptor.options;
64
77
 
65
78
  return (
66
79
  <DrawerItem
@@ -84,6 +97,7 @@ export function DrawerItemList({ state, navigation, descriptors }: Props) {
84
97
  labelStyle={drawerLabelStyle}
85
98
  style={drawerItemStyle}
86
99
  onPress={onPress}
100
+ testID={drawerItemTestID}
87
101
  />
88
102
  );
89
103
  }) as React.ReactNode as React.ReactElement;
@@ -1,51 +1,77 @@
1
- import { HeaderButton } from '@react-navigation/elements';
2
- import { DrawerActions, useNavigation } from '@react-navigation/native';
3
1
  import {
4
- type ColorValue,
5
- Image,
6
- type ImageSourcePropType,
7
- StyleSheet,
8
- } from 'react-native';
2
+ HeaderButton,
3
+ type Icon,
4
+ PlatformIcon,
5
+ } from '@react-navigation/elements';
6
+ import { DrawerActions, useNavigation } from '@react-navigation/native';
7
+ import * as React from 'react';
8
+ import { type ColorValue, Platform, StyleSheet } from 'react-native';
9
9
 
10
10
  import toggleDrawerIcon from './assets/toggle-drawer-icon.png';
11
11
 
12
12
  type Props = {
13
- accessibilityLabel?: string;
14
- pressColor?: ColorValue;
15
- pressOpacity?: number;
16
- tintColor?: ColorValue;
17
- imageSource?: ImageSourcePropType;
13
+ icon?:
14
+ | Icon
15
+ | ((props: { tintColor: ColorValue | undefined }) => React.ReactNode)
16
+ | undefined;
17
+ accessibilityLabel?: string | undefined;
18
+ pressColor?: ColorValue | undefined;
19
+ pressOpacity?: number | undefined;
20
+ tintColor?: ColorValue | undefined;
21
+ testID?: string | undefined;
18
22
  };
19
23
 
20
24
  export function DrawerToggleButton({
25
+ icon,
21
26
  tintColor,
22
27
  accessibilityLabel = 'Show navigation menu',
23
- imageSource = toggleDrawerIcon,
24
28
  ...rest
25
29
  }: Props) {
26
30
  const navigation = useNavigation();
27
31
 
32
+ const drawerIcon =
33
+ icon ??
34
+ Platform.select<Icon>({
35
+ ios: {
36
+ type: 'sfSymbol',
37
+ name: 'line.3.horizontal',
38
+ },
39
+ android: {
40
+ type: 'materialSymbol',
41
+ name: 'menu',
42
+ },
43
+ default: {
44
+ type: 'image',
45
+ source: toggleDrawerIcon,
46
+ },
47
+ });
48
+
28
49
  return (
29
50
  <HeaderButton
30
51
  {...rest}
31
52
  accessibilityLabel={accessibilityLabel}
32
53
  onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
33
54
  >
34
- <Image
35
- resizeMode="contain"
36
- source={imageSource}
37
- fadeDuration={0}
38
- tintColor={tintColor}
39
- style={styles.icon}
40
- />
55
+ {typeof drawerIcon === 'function' ? (
56
+ drawerIcon({ tintColor })
57
+ ) : (
58
+ <PlatformIcon
59
+ icon={drawerIcon}
60
+ size={ICON_SIZE}
61
+ color={tintColor}
62
+ style={styles.icon}
63
+ />
64
+ )}
41
65
  </HeaderButton>
42
66
  );
43
67
  }
44
68
 
69
+ const ICON_SIZE = 24;
70
+
45
71
  const styles = StyleSheet.create({
46
72
  icon: {
47
- height: 24,
48
- width: 24,
73
+ height: ICON_SIZE,
74
+ width: ICON_SIZE,
49
75
  marginVertical: 8,
50
76
  marginHorizontal: 5,
51
77
  },
@@ -1,5 +1,7 @@
1
1
  import { getHeaderTitle, Header } from '@react-navigation/elements';
2
2
  import {
3
+ ActivityView,
4
+ Container,
3
5
  SafeAreaProviderCompat,
4
6
  Screen as ScreenContent,
5
7
  } from '@react-navigation/elements/internal';
@@ -15,7 +17,6 @@ import {
15
17
  import * as React from 'react';
16
18
  import { Platform, StyleSheet } from 'react-native';
17
19
  import { Drawer } from 'react-native-drawer-layout';
18
- import { Screen, ScreenContainer } from 'react-native-screens';
19
20
  import useLatestCallback from 'use-latest-callback';
20
21
 
21
22
  import type {
@@ -24,6 +25,7 @@ import type {
24
25
  DrawerHeaderProps,
25
26
  DrawerNavigationConfig,
26
27
  DrawerNavigationHelpers,
28
+ DrawerNavigationOptions,
27
29
  DrawerNavigationProp,
28
30
  } from '../types';
29
31
  import { addCancelListener } from '../utils/addCancelListener';
@@ -52,13 +54,19 @@ function DrawerViewBase({
52
54
  descriptors,
53
55
  defaultStatus,
54
56
  drawerContent = renderDrawerContentDefault,
55
- detachInactiveScreens = Platform.OS === 'web' ||
56
- Platform.OS === 'android' ||
57
- Platform.OS === 'ios',
58
57
  }: Props) {
59
58
  const { direction } = useLocale();
60
59
 
61
- const focusedRouteKey = state.routes[state.index].key;
60
+ const focusedRoute = state.routes[state.index];
61
+
62
+ if (focusedRoute == null) {
63
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
64
+ }
65
+
66
+ const focusedRouteKey = focusedRoute.key;
67
+ const focusedOptions: DrawerNavigationOptions =
68
+ descriptors[focusedRouteKey]?.options ?? {};
69
+
62
70
  const {
63
71
  drawerHideStatusBarOnOpen,
64
72
  drawerPosition = direction === 'rtl' ? 'right' : 'left',
@@ -74,7 +82,7 @@ function DrawerViewBase({
74
82
  swipeMinDistance,
75
83
  overlayStyle,
76
84
  overlayAccessibilityLabel,
77
- } = descriptors[focusedRouteKey].options;
85
+ } = focusedOptions;
78
86
 
79
87
  const [loaded, setLoaded] = React.useState([focusedRouteKey]);
80
88
 
@@ -209,13 +217,16 @@ function DrawerViewBase({
209
217
 
210
218
  const renderSceneContent = () => {
211
219
  return (
212
- <ScreenContainer
213
- enabled={detachInactiveScreens}
214
- hasTwoStates
215
- style={styles.content}
216
- >
220
+ <Container style={styles.content}>
217
221
  {state.routes.map((route, index) => {
218
222
  const descriptor = descriptors[route.key];
223
+
224
+ if (descriptor == null) {
225
+ throw new Error(
226
+ `Couldn't find a descriptor for route '${route.key}'.`
227
+ );
228
+ }
229
+
219
230
  const { lazy = true } = descriptor.options;
220
231
  const isFocused = state.index === index;
221
232
  const isPreloaded = state.preloadedRouteKeys.includes(route.key);
@@ -231,7 +242,7 @@ function DrawerViewBase({
231
242
  }
232
243
 
233
244
  const {
234
- freezeOnBlur,
245
+ inactiveBehavior = 'pause',
235
246
  header = ({ options }: DrawerHeaderProps) => (
236
247
  <Header
237
248
  {...options}
@@ -254,14 +265,20 @@ function DrawerViewBase({
254
265
  sceneStyle,
255
266
  } = descriptor.options;
256
267
 
268
+ // For preloaded screens and if lazy is false,
269
+ // Keep them active so that the effects can run
270
+ const isActive =
271
+ inactiveBehavior === 'none' ||
272
+ isFocused ||
273
+ isPreloaded ||
274
+ (lazy === false && !loaded.includes(route.key));
275
+
257
276
  return (
258
- <Screen
277
+ <ActivityView
259
278
  key={route.key}
260
- style={[StyleSheet.absoluteFill, { zIndex: isFocused ? 0 : -1 }]}
261
- activityState={isFocused ? 2 : 0}
262
- enabled={detachInactiveScreens}
263
- freezeOnBlur={freezeOnBlur}
264
- shouldFreeze={!isFocused && !isPreloaded}
279
+ mode={isFocused ? 'normal' : isActive ? 'inert' : 'paused'}
280
+ visible={isFocused}
281
+ style={{ ...StyleSheet.absoluteFill, zIndex: isFocused ? 0 : -1 }}
265
282
  >
266
283
  <ScreenContent
267
284
  focused={isFocused}
@@ -280,10 +297,10 @@ function DrawerViewBase({
280
297
  >
281
298
  {descriptor.render()}
282
299
  </ScreenContent>
283
- </Screen>
300
+ </ActivityView>
284
301
  );
285
302
  })}
286
- </ScreenContainer>
303
+ </Container>
287
304
  );
288
305
  };
289
306