@react-navigation/elements 3.0.0-alpha.2 → 3.0.0-alpha.20

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 (171) hide show
  1. package/lib/module/ActivityView.js +90 -0
  2. package/lib/module/ActivityView.js.map +1 -0
  3. package/lib/module/ActivityView.native.js +67 -0
  4. package/lib/module/ActivityView.native.js.map +1 -0
  5. package/lib/module/Badge.js +5 -4
  6. package/lib/module/Badge.js.map +1 -1
  7. package/lib/module/Button.js +7 -2
  8. package/lib/module/Button.js.map +1 -1
  9. package/lib/module/Color.js +95 -0
  10. package/lib/module/Color.js.map +1 -1
  11. package/lib/module/Container.js +11 -1
  12. package/lib/module/Container.js.map +1 -1
  13. package/lib/module/Header/Header.js +26 -11
  14. package/lib/module/Header/Header.js.map +1 -1
  15. package/lib/module/Header/HeaderBackButton.js +27 -13
  16. package/lib/module/Header/HeaderBackButton.js.map +1 -1
  17. package/lib/module/Header/HeaderIcon.js +29 -10
  18. package/lib/module/Header/HeaderIcon.js.map +1 -1
  19. package/lib/module/Header/HeaderSearchBar.js +28 -21
  20. package/lib/module/Header/HeaderSearchBar.js.map +1 -1
  21. package/lib/module/Header/getHeaderTitle.js.map +1 -1
  22. package/lib/module/Header/useHeaderHeight.js +1 -1
  23. package/lib/module/Header/useHeaderHeight.js.map +1 -1
  24. package/lib/module/Label/getLabel.js.map +1 -1
  25. package/lib/module/PlatformColor.js +1 -0
  26. package/lib/module/PlatformColor.js.map +1 -1
  27. package/lib/module/PlatformColor.native.js +1 -1
  28. package/lib/module/PlatformColor.native.js.map +1 -1
  29. package/lib/module/PlatformPressable.js.map +1 -1
  30. package/lib/module/SafeAreaProviderCompat.js +1 -1
  31. package/lib/module/SafeAreaProviderCompat.js.map +1 -1
  32. package/lib/module/Screen.js +13 -9
  33. package/lib/module/Screen.js.map +1 -1
  34. package/lib/module/index.js +1 -7
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/internal.js +1 -1
  37. package/lib/module/internal.js.map +1 -1
  38. package/lib/module/useFrameSize.js +1 -1
  39. package/lib/module/useFrameSize.js.map +1 -1
  40. package/lib/typescript/src/ActivityView.d.ts +31 -0
  41. package/lib/typescript/src/ActivityView.d.ts.map +1 -0
  42. package/lib/typescript/src/ActivityView.native.d.ts +3 -0
  43. package/lib/typescript/src/ActivityView.native.d.ts.map +1 -0
  44. package/lib/typescript/src/Badge.d.ts +3 -3
  45. package/lib/typescript/src/Badge.d.ts.map +1 -1
  46. package/lib/typescript/src/Button.d.ts +3 -3
  47. package/lib/typescript/src/Button.d.ts.map +1 -1
  48. package/lib/typescript/src/Color.d.ts +4 -3
  49. package/lib/typescript/src/Color.d.ts.map +1 -1
  50. package/lib/typescript/src/Container.d.ts +7 -4
  51. package/lib/typescript/src/Container.d.ts.map +1 -1
  52. package/lib/typescript/src/Header/Header.d.ts +2 -2
  53. package/lib/typescript/src/Header/Header.d.ts.map +1 -1
  54. package/lib/typescript/src/Header/HeaderBackButton.d.ts +1 -1
  55. package/lib/typescript/src/Header/HeaderBackButton.d.ts.map +1 -1
  56. package/lib/typescript/src/Header/HeaderBackground.d.ts +3 -3
  57. package/lib/typescript/src/Header/HeaderBackground.d.ts.map +1 -1
  58. package/lib/typescript/src/Header/HeaderIcon.d.ts +8 -4
  59. package/lib/typescript/src/Header/HeaderIcon.d.ts.map +1 -1
  60. package/lib/typescript/src/Header/HeaderSearchBar.d.ts +4 -4
  61. package/lib/typescript/src/Header/HeaderSearchBar.d.ts.map +1 -1
  62. package/lib/typescript/src/Header/HeaderTitle.d.ts +3 -3
  63. package/lib/typescript/src/Header/HeaderTitle.d.ts.map +1 -1
  64. package/lib/typescript/src/Header/getHeaderTitle.d.ts +1 -1
  65. package/lib/typescript/src/Header/getHeaderTitle.d.ts.map +1 -1
  66. package/lib/typescript/src/Label/getLabel.d.ts +2 -2
  67. package/lib/typescript/src/Label/getLabel.d.ts.map +1 -1
  68. package/lib/typescript/src/PlatformColor.d.ts +1 -0
  69. package/lib/typescript/src/PlatformColor.d.ts.map +1 -1
  70. package/lib/typescript/src/PlatformColor.native.d.ts +1 -1
  71. package/lib/typescript/src/PlatformColor.native.d.ts.map +1 -1
  72. package/lib/typescript/src/PlatformPressable.d.ts +12 -12
  73. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  74. package/lib/typescript/src/Screen.d.ts +6 -6
  75. package/lib/typescript/src/Screen.d.ts.map +1 -1
  76. package/lib/typescript/src/index.d.ts +1 -2
  77. package/lib/typescript/src/index.d.ts.map +1 -1
  78. package/lib/typescript/src/internal.d.ts +1 -1
  79. package/lib/typescript/src/internal.d.ts.map +1 -1
  80. package/lib/typescript/src/types.d.ts +119 -69
  81. package/lib/typescript/src/types.d.ts.map +1 -1
  82. package/package.json +14 -13
  83. package/src/ActivityView.native.tsx +90 -0
  84. package/src/ActivityView.tsx +129 -0
  85. package/src/Badge.tsx +9 -7
  86. package/src/Button.tsx +15 -7
  87. package/src/Color.tsx +133 -3
  88. package/src/Container.tsx +20 -4
  89. package/src/Header/Header.tsx +43 -20
  90. package/src/Header/HeaderBackButton.tsx +31 -17
  91. package/src/Header/HeaderBackground.tsx +3 -3
  92. package/src/Header/HeaderIcon.tsx +49 -9
  93. package/src/Header/HeaderSearchBar.tsx +31 -22
  94. package/src/Header/HeaderTitle.tsx +3 -3
  95. package/src/Header/getHeaderTitle.tsx +4 -1
  96. package/src/Header/useHeaderHeight.tsx +1 -1
  97. package/src/Label/getLabel.tsx +1 -1
  98. package/src/PlatformColor.native.tsx +1 -1
  99. package/src/PlatformColor.tsx +4 -0
  100. package/src/PlatformPressable.tsx +12 -8
  101. package/src/SafeAreaProviderCompat.tsx +1 -1
  102. package/src/Screen.tsx +19 -19
  103. package/src/index.tsx +1 -16
  104. package/src/internal.tsx +1 -1
  105. package/src/types.tsx +153 -76
  106. package/src/useFrameSize.tsx +1 -1
  107. package/lib/module/Lazy.js +0 -42
  108. package/lib/module/Lazy.js.map +0 -1
  109. package/lib/module/assets/back-icon.ios.svg +0 -4
  110. package/lib/module/assets/back-icon@1x.android.png +0 -0
  111. package/lib/module/assets/back-icon@1x.ios.png +0 -0
  112. package/lib/module/assets/back-icon@2x.android.png +0 -0
  113. package/lib/module/assets/back-icon@2x.ios.png +0 -0
  114. package/lib/module/assets/back-icon@3x.android.png +0 -0
  115. package/lib/module/assets/back-icon@3x.ios.png +0 -0
  116. package/lib/module/assets/back-icon@4x.android.png +0 -0
  117. package/lib/module/assets/back-icon@4x.ios.png +0 -0
  118. package/lib/module/assets/clear-icon@1x.png +0 -0
  119. package/lib/module/assets/clear-icon@2x.png +0 -0
  120. package/lib/module/assets/clear-icon@3x.png +0 -0
  121. package/lib/module/assets/clear-icon@4x.png +0 -0
  122. package/lib/module/assets/close-icon@1x.png +0 -0
  123. package/lib/module/assets/close-icon@2x.png +0 -0
  124. package/lib/module/assets/close-icon@3x.png +0 -0
  125. package/lib/module/assets/close-icon@4x.png +0 -0
  126. package/lib/module/assets/search-icon-legacy@1x.ios.png +0 -0
  127. package/lib/module/assets/search-icon-legacy@2x.ios.png +0 -0
  128. package/lib/module/assets/search-icon-legacy@3x.ios.png +0 -0
  129. package/lib/module/assets/search-icon-legacy@4x.ios.png +0 -0
  130. package/lib/module/assets/search-icon.ios.svg +0 -4
  131. package/lib/module/assets/search-icon@1x.android.png +0 -0
  132. package/lib/module/assets/search-icon@1x.ios.png +0 -0
  133. package/lib/module/assets/search-icon@2x.android.png +0 -0
  134. package/lib/module/assets/search-icon@2x.ios.png +0 -0
  135. package/lib/module/assets/search-icon@3x.android.png +0 -0
  136. package/lib/module/assets/search-icon@3x.ios.png +0 -0
  137. package/lib/module/assets/search-icon@4x.android.png +0 -0
  138. package/lib/module/assets/search-icon@4x.ios.png +0 -0
  139. package/lib/typescript/src/Lazy.d.ts +0 -31
  140. package/lib/typescript/src/Lazy.d.ts.map +0 -1
  141. package/src/Lazy.tsx +0 -59
  142. package/src/assets/back-icon.ios.svg +0 -4
  143. package/src/assets/back-icon@1x.android.png +0 -0
  144. package/src/assets/back-icon@1x.ios.png +0 -0
  145. package/src/assets/back-icon@2x.android.png +0 -0
  146. package/src/assets/back-icon@2x.ios.png +0 -0
  147. package/src/assets/back-icon@3x.android.png +0 -0
  148. package/src/assets/back-icon@3x.ios.png +0 -0
  149. package/src/assets/back-icon@4x.android.png +0 -0
  150. package/src/assets/back-icon@4x.ios.png +0 -0
  151. package/src/assets/clear-icon@1x.png +0 -0
  152. package/src/assets/clear-icon@2x.png +0 -0
  153. package/src/assets/clear-icon@3x.png +0 -0
  154. package/src/assets/clear-icon@4x.png +0 -0
  155. package/src/assets/close-icon@1x.png +0 -0
  156. package/src/assets/close-icon@2x.png +0 -0
  157. package/src/assets/close-icon@3x.png +0 -0
  158. package/src/assets/close-icon@4x.png +0 -0
  159. package/src/assets/search-icon-legacy@1x.ios.png +0 -0
  160. package/src/assets/search-icon-legacy@2x.ios.png +0 -0
  161. package/src/assets/search-icon-legacy@3x.ios.png +0 -0
  162. package/src/assets/search-icon-legacy@4x.ios.png +0 -0
  163. package/src/assets/search-icon.ios.svg +0 -4
  164. package/src/assets/search-icon@1x.android.png +0 -0
  165. package/src/assets/search-icon@1x.ios.png +0 -0
  166. package/src/assets/search-icon@2x.android.png +0 -0
  167. package/src/assets/search-icon@2x.ios.png +0 -0
  168. package/src/assets/search-icon@3x.android.png +0 -0
  169. package/src/assets/search-icon@3x.ios.png +0 -0
  170. package/src/assets/search-icon@4x.android.png +0 -0
  171. package/src/assets/search-icon@4x.ios.png +0 -0
@@ -0,0 +1,129 @@
1
+ import { Activity, useCallback, useEffect, useState } from 'react';
2
+ import { Platform, View, type ViewStyle } from 'react-native';
3
+
4
+ import { Container } from './Container';
5
+
6
+ export type Props = {
7
+ /**
8
+ * Mode of the activity view
9
+ * - `normal`: The view renders normally
10
+ * - `inert`: Content is not interactive
11
+ * - `paused`: Effects are unmounted and content is not interactive
12
+ */
13
+ mode: 'normal' | 'inert' | 'paused';
14
+ /**
15
+ * Whether the content is visible or not
16
+ */
17
+ visible: boolean;
18
+ /**
19
+ * Delay before pausing effects.
20
+ * So pending animations have time to finish.
21
+ *
22
+ * Defaults to 500ms.
23
+ */
24
+ delay?: number | undefined;
25
+ /**
26
+ * The style for the container view
27
+ */
28
+ style?: Omit<React.CSSProperties & ViewStyle, 'display'> | undefined;
29
+ /**
30
+ * The content of the activity view
31
+ */
32
+ children: React.ReactNode;
33
+ };
34
+
35
+ export function ActivityView({
36
+ mode,
37
+ visible,
38
+ delay = 500,
39
+ style,
40
+ children,
41
+ }: Props) {
42
+ const [delayedMode, setDelayedMode] = useState(mode);
43
+
44
+ useEffect(() => {
45
+ if (!delay) {
46
+ return;
47
+ }
48
+
49
+ const timer = setTimeout(() => {
50
+ setDelayedMode(mode);
51
+ }, delay);
52
+
53
+ return () => clearTimeout(timer);
54
+ }, [delay, mode]);
55
+
56
+ const display = visible ? 'flex' : 'none';
57
+ const activityMode =
58
+ mode !== 'paused' || (delay && delayedMode !== 'paused')
59
+ ? 'visible'
60
+ : 'hidden';
61
+
62
+ /**
63
+ * Activity has 2 modes, visible and hidden - hidden unmounts effects
64
+ * But what we want is to unmount effects, without hiding content
65
+ * So we use hidden mode, but unset display: none to make content visible
66
+ */
67
+ const onRef = useCallback(
68
+ (node: HTMLDivElement | View | null) => {
69
+ if (Platform.OS !== 'web' || !(node && node instanceof HTMLElement)) {
70
+ return;
71
+ }
72
+
73
+ const observers: MutationObserver[] = [];
74
+
75
+ const observe = () => {
76
+ // Remove previous observers
77
+ observers.forEach((o) => o.disconnect());
78
+ observers.length = 0;
79
+
80
+ const children = node.childNodes;
81
+
82
+ // When the style attribute for children is updated by React
83
+ // We observe it and update display to make content visible
84
+ children.forEach((child) => {
85
+ if (child instanceof HTMLElement) {
86
+ child.style.display = display;
87
+
88
+ const o = new MutationObserver(() => {
89
+ child.style.display = display;
90
+ });
91
+
92
+ o.observe(child, {
93
+ attributes: true,
94
+ attributeFilter: ['style'],
95
+ });
96
+
97
+ observers.push(o);
98
+ }
99
+ });
100
+ };
101
+
102
+ observe();
103
+
104
+ // React removes refs when `Activity` is hidden
105
+ // So we render outside of the `Activity` and observer child list
106
+ const observer = new MutationObserver(observe);
107
+
108
+ observer.observe(node, {
109
+ childList: true,
110
+ });
111
+
112
+ return () => {
113
+ observer.disconnect();
114
+ observers.forEach((o) => o.disconnect());
115
+ };
116
+ },
117
+ [display]
118
+ );
119
+
120
+ return (
121
+ <Container ref={onRef} style={{ display: 'contents' }}>
122
+ <Activity mode={activityMode}>
123
+ <Container inert={mode !== 'normal'} style={{ ...style, display }}>
124
+ {children}
125
+ </Container>
126
+ </Activity>
127
+ </Container>
128
+ );
129
+ }
package/src/Badge.tsx CHANGED
@@ -19,15 +19,15 @@ type Props = TextProps & {
19
19
  /**
20
20
  * Content of the `Badge`.
21
21
  */
22
- children?: string | number;
22
+ children?: string | number | undefined;
23
23
  /**
24
24
  * Size of the `Badge`.
25
25
  */
26
- size?: number;
26
+ size?: number | undefined;
27
27
  /**
28
28
  * Style object for the tab bar container.
29
29
  */
30
- style?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
30
+ style?: Animated.WithAnimatedValue<StyleProp<TextStyle>> | undefined;
31
31
  };
32
32
 
33
33
  const useNativeDriver = Platform.OS !== 'web';
@@ -49,17 +49,19 @@ export function Badge({
49
49
  return;
50
50
  }
51
51
 
52
- Animated.timing(opacity, {
52
+ const animation = Animated.timing(opacity, {
53
53
  toValue: visible ? 1 : 0,
54
54
  duration: 150,
55
55
  useNativeDriver,
56
- }).start(({ finished }) => {
56
+ });
57
+
58
+ animation.start(({ finished }) => {
57
59
  if (finished && !visible) {
58
60
  setRendered(false);
59
61
  }
60
62
  });
61
63
 
62
- return () => opacity.stopAnimation();
64
+ return () => animation.stop();
63
65
  }, [opacity, rendered, visible]);
64
66
 
65
67
  if (!rendered) {
@@ -73,7 +75,7 @@ export function Badge({
73
75
  // @ts-expect-error: backgroundColor definitely exists
74
76
  const { backgroundColor = colors.notification, ...restStyle } =
75
77
  StyleSheet.flatten(style) || {};
76
- const textColor = Color(backgroundColor)?.isLight() ? 'black' : 'white';
78
+ const textColor = Color.foreground(backgroundColor);
77
79
 
78
80
  const borderRadius = size / 2;
79
81
  const fontSize = Math.floor((size * 3) / 4);
package/src/Button.tsx CHANGED
@@ -15,15 +15,15 @@ import {
15
15
  import { Text } from './Text';
16
16
 
17
17
  type ButtonBaseProps = Omit<PlatformPressableProps, 'children'> & {
18
- variant?: 'plain' | 'tinted' | 'filled';
19
- color?: ColorValue;
18
+ variant?: 'plain' | 'tinted' | 'filled' | undefined;
19
+ color?: ColorValue | undefined;
20
20
  children: string | string[];
21
21
  };
22
22
 
23
23
  type ButtonLinkProps<
24
24
  ParamList extends {} = RootParamList,
25
25
  RouteName extends keyof ParamList = keyof ParamList,
26
- > = LinkProps<ParamList, RouteName> & Omit<ButtonBaseProps, 'onPress'>;
26
+ > = LinkProps<ParamList, RouteName> & ButtonBaseProps;
27
27
 
28
28
  const BUTTON_RADIUS = 40;
29
29
 
@@ -54,12 +54,22 @@ function ButtonLink<
54
54
  params,
55
55
  action,
56
56
  href,
57
+ onPress,
57
58
  ...rest
58
59
  }: ButtonLinkProps<ParamList, RouteName>) {
59
60
  // @ts-expect-error: This is already type-checked by the prop types
60
61
  const props = useLinkProps({ screen, params, action, href });
61
62
 
62
- return <ButtonBase {...rest} {...props} />;
63
+ return (
64
+ <ButtonBase
65
+ {...rest}
66
+ {...props}
67
+ onPress={(e) => {
68
+ onPress?.(e);
69
+ props.onPress?.(e);
70
+ }}
71
+ />
72
+ );
63
73
  }
64
74
 
65
75
  function ButtonBase({
@@ -90,9 +100,7 @@ function ButtonBase({
90
100
  break;
91
101
  case 'filled':
92
102
  backgroundColor = color;
93
- textColor = Color(color)?.isDark()
94
- ? 'white'
95
- : (Color(color)?.darken(0.71).string() ?? '#fff');
103
+ textColor = Color.foreground(backgroundColor);
96
104
  break;
97
105
  }
98
106
 
package/src/Color.tsx CHANGED
@@ -1,10 +1,10 @@
1
1
  // eslint-disable-next-line no-restricted-imports
2
2
  import OriginalColor from 'color';
3
- import type { ColorValue } from 'react-native';
3
+ import { type ColorValue, Platform } from 'react-native';
4
+
5
+ import { DynamicColorIOS, PlatformColor } from './PlatformColor';
4
6
 
5
7
  type ColorType = {
6
- isLight(): boolean;
7
- isDark(): boolean;
8
8
  alpha(amount: number): ColorType;
9
9
  alpha(): number;
10
10
  fade(amount: number): ColorType;
@@ -19,3 +19,133 @@ export function Color(value: ColorValue): ColorType | undefined {
19
19
 
20
20
  return undefined;
21
21
  }
22
+
23
+ Color.foreground = (color: ColorValue): ColorValue => {
24
+ const value = color as unknown;
25
+
26
+ if (typeof value === 'object' && value != null) {
27
+ // Special case for Android platform colors
28
+ // Available colors: https://developer.android.com/reference/android/R.color
29
+ if (
30
+ Platform.OS === 'android' &&
31
+ PlatformColor &&
32
+ 'resource_paths' in value &&
33
+ Array.isArray(value.resource_paths) &&
34
+ typeof value.resource_paths[0] === 'string'
35
+ ) {
36
+ const name = value.resource_paths[0].replace('@android:color/', '');
37
+
38
+ if (name in ANDROID_COLOR_MAP) {
39
+ return PlatformColor(`@android:color/${ANDROID_COLOR_MAP[name]}`);
40
+ }
41
+ }
42
+
43
+ // Special case for iOS platform colors
44
+ if (
45
+ Platform.OS === 'ios' &&
46
+ PlatformColor &&
47
+ 'semantic' in value &&
48
+ Array.isArray(value.semantic) &&
49
+ typeof value.semantic[0] === 'string'
50
+ ) {
51
+ const name = value.semantic[0];
52
+
53
+ if (name in IOS_COLOR_MAP) {
54
+ const foreground = IOS_COLOR_MAP[name];
55
+ return foreground === 'white' || foreground === 'black'
56
+ ? foreground
57
+ : PlatformColor(foreground);
58
+ }
59
+ }
60
+
61
+ // Special case for iOS dynamic colors
62
+ if (
63
+ Platform.OS === 'ios' &&
64
+ DynamicColorIOS &&
65
+ 'dynamic' in value &&
66
+ typeof value.dynamic === 'object' &&
67
+ value.dynamic != null &&
68
+ 'light' in value.dynamic &&
69
+ typeof value.dynamic.light === 'string' &&
70
+ 'dark' in value.dynamic &&
71
+ typeof value.dynamic.dark === 'string'
72
+ ) {
73
+ const lightForeground = Color.foreground(value.dynamic.light);
74
+ const darkForeground = Color.foreground(value.dynamic.dark);
75
+
76
+ if (lightForeground && darkForeground) {
77
+ return DynamicColorIOS({
78
+ light: lightForeground,
79
+ dark: darkForeground,
80
+ });
81
+ }
82
+ }
83
+ } else if (typeof color === 'string' && !color.startsWith('var(')) {
84
+ const processed = OriginalColor(color);
85
+
86
+ if (processed.isLight()) {
87
+ return processed.darken(0.71).string();
88
+ }
89
+ }
90
+
91
+ return '#fff';
92
+ };
93
+
94
+ const ANDROID_COLOR_MAP: Record<string, string> = {
95
+ system_background_dark: 'system_on_background_dark',
96
+ system_background_light: 'system_on_background_light',
97
+ system_error_container_dark: 'system_on_error_container_dark',
98
+ system_error_container_light: 'system_on_error_container_light',
99
+ system_error_dark: 'system_on_error_dark',
100
+ system_error_light: 'system_on_error_light',
101
+ system_primary_container_dark: 'system_on_primary_container_dark',
102
+ system_primary_container_light: 'system_on_primary_container_light',
103
+ system_primary_dark: 'system_on_primary_dark',
104
+ system_primary_fixed: 'system_on_primary_fixed',
105
+ system_primary_light: 'system_on_primary_light',
106
+ system_secondary_container_dark: 'system_on_secondary_container_dark',
107
+ system_secondary_container_light: 'system_on_secondary_container_light',
108
+ system_secondary_dark: 'system_on_secondary_dark',
109
+ system_secondary_fixed: 'system_on_secondary_fixed',
110
+ system_secondary_light: 'system_on_secondary_light',
111
+ system_surface_dark: 'system_on_surface_dark',
112
+ system_surface_disabled: 'system_on_surface_disabled',
113
+ system_surface_light: 'system_on_surface_light',
114
+ system_surface_variant_dark: 'system_on_surface_variant_dark',
115
+ system_surface_variant_light: 'system_on_surface_variant_light',
116
+ };
117
+
118
+ const IOS_COLOR_MAP: Record<string, string> = {
119
+ systemBackground: 'label',
120
+ secondarySystemBackground: 'label',
121
+ tertiarySystemBackground: 'label',
122
+ systemGroupedBackground: 'label',
123
+ secondarySystemGroupedBackground: 'label',
124
+ tertiarySystemGroupedBackground: 'label',
125
+
126
+ systemFill: 'label',
127
+ secondarySystemFill: 'label',
128
+ tertiarySystemFill: 'label',
129
+ quaternarySystemFill: 'label',
130
+
131
+ systemRed: 'white',
132
+ systemGreen: 'white',
133
+ systemBlue: 'white',
134
+ systemIndigo: 'white',
135
+ systemPurple: 'white',
136
+ systemBrown: 'white',
137
+
138
+ systemOrange: 'black',
139
+ systemYellow: 'black',
140
+ systemMint: 'black',
141
+ systemTeal: 'black',
142
+ systemCyan: 'black',
143
+ systemPink: 'black',
144
+
145
+ systemGray: 'label',
146
+ systemGray2: 'label',
147
+ systemGray3: 'label',
148
+ systemGray4: 'label',
149
+ systemGray5: 'label',
150
+ systemGray6: 'label',
151
+ };
package/src/Container.tsx CHANGED
@@ -1,18 +1,33 @@
1
1
  import { Platform, View, type ViewStyle } from 'react-native';
2
2
 
3
3
  export type Props = {
4
- inert?: boolean;
5
- style?: React.CSSProperties & ViewStyle;
4
+ ref?: React.Ref<HTMLDivElement | View> | undefined;
5
+ inert?: boolean | undefined;
6
+ style?:
7
+ | (ViewStyle &
8
+ Omit<React.CSSProperties, 'backgroundColor'> & {
9
+ backgroundColor?: ViewStyle['backgroundColor'] | undefined;
10
+ })
11
+ | undefined;
6
12
  children: React.ReactNode;
7
13
  };
8
14
 
9
- export function Container({ inert, children, style }: Props) {
15
+ export function Container({ ref, inert, children, style }: Props) {
10
16
  if (Platform.OS === 'web') {
17
+ const { backgroundColor, ...rest } = style ?? {};
18
+
11
19
  return (
12
20
  <div
21
+ ref={ref as React.Ref<HTMLDivElement> | undefined}
13
22
  inert={inert}
14
23
  aria-hidden={inert}
15
- style={{ ...DEFAULT_STYLE, ...style }}
24
+ style={{
25
+ ...DEFAULT_STYLE,
26
+ ...rest,
27
+ backgroundColor:
28
+ // In practice we only get string on web instead of OpaqueValue
29
+ typeof backgroundColor === 'string' ? backgroundColor : undefined,
30
+ }}
16
31
  >
17
32
  {children}
18
33
  </div>
@@ -21,6 +36,7 @@ export function Container({ inert, children, style }: Props) {
21
36
 
22
37
  return (
23
38
  <View
39
+ ref={ref as React.Ref<View> | undefined}
24
40
  aria-hidden={inert}
25
41
  style={[{ pointerEvents: inert ? 'none' : 'box-none' }, style]}
26
42
  collapsable={false}
@@ -1,4 +1,8 @@
1
- import { useNavigation, useTheme } from '@react-navigation/native';
1
+ import {
2
+ UNSTABLE_CornerInset,
3
+ useNavigation,
4
+ useTheme,
5
+ } from '@react-navigation/native';
2
6
  import * as React from 'react';
3
7
  import {
4
8
  Animated,
@@ -30,20 +34,22 @@ type Props = HeaderOptions & {
30
34
  /**
31
35
  * Options for the back button.
32
36
  */
33
- back?: {
34
- /**
35
- * Title of the previous screen.
36
- */
37
- title: string | undefined;
38
- /**
39
- * The `href` to use for the anchor tag on web
40
- */
41
- href: string | undefined;
42
- };
37
+ back?:
38
+ | {
39
+ /**
40
+ * Title of the previous screen.
41
+ */
42
+ title: string | undefined;
43
+ /**
44
+ * The `href` to use for the anchor tag on web
45
+ */
46
+ href: string | undefined;
47
+ }
48
+ | undefined;
43
49
  /**
44
50
  * Whether the header is in a modal
45
51
  */
46
- modal?: boolean;
52
+ modal?: boolean | undefined;
47
53
  /**
48
54
  * Title text for the header.
49
55
  */
@@ -79,7 +85,7 @@ export function Header(props: Props) {
79
85
  const { colors } = useTheme();
80
86
 
81
87
  const navigation = useNavigation();
82
- const isParentHeaderShown = React.useContext(HeaderShownContext);
88
+ const isParentHeaderShown = React.use(HeaderShownContext);
83
89
 
84
90
  const [searchBarVisible, setSearchBarVisible] = React.useState(false);
85
91
 
@@ -321,21 +327,21 @@ export function Header(props: Props) {
321
327
  return;
322
328
  }
323
329
 
324
- Animated.timing(searchBarVisibleAnim, {
330
+ const animation = Animated.timing(searchBarVisibleAnim, {
325
331
  toValue: searchBarVisible ? 1 : 0,
326
332
  duration: 150,
327
333
  useNativeDriver,
328
334
  easing: Easing.in(Easing.linear),
329
- }).start(({ finished }) => {
335
+ });
336
+
337
+ animation.start(({ finished }) => {
330
338
  if (finished) {
331
339
  setSearchBarRendered(searchBarVisible);
332
340
  searchBarVisibleRef.current = searchBarVisible;
333
341
  }
334
342
  });
335
343
 
336
- return () => {
337
- searchBarVisibleAnim.stopAnimation();
338
- };
344
+ return () => animation.stop();
339
345
  }, [searchBarVisible, searchBarVisibleAnim]);
340
346
 
341
347
  const headerOpacity = searchBarVisibleAnim.interpolate({
@@ -402,6 +408,7 @@ export function Header(props: Props) {
402
408
  },
403
409
  ]}
404
410
  >
411
+ <UNSTABLE_CornerInset direction="horizontal" edge="left" />
405
412
  <View
406
413
  style={[
407
414
  styles.start,
@@ -458,7 +465,23 @@ export function Header(props: Props) {
458
465
  headerSearchBarOptions?.onOpen?.();
459
466
  }}
460
467
  >
461
- <HeaderIcon source={searchIcon} tintColor={iconTintColor} />
468
+ <HeaderIcon
469
+ icon={Platform.select({
470
+ android: {
471
+ type: 'materialSymbol',
472
+ name: 'search',
473
+ },
474
+ ios: {
475
+ type: 'sfSymbol',
476
+ name: 'magnifyingglass',
477
+ },
478
+ default: {
479
+ type: 'image',
480
+ source: searchIcon,
481
+ },
482
+ })}
483
+ color={iconTintColor}
484
+ />
462
485
  </HeaderButton>
463
486
  ) : null}
464
487
  </HeaderButtonBackground>
@@ -540,7 +563,7 @@ const styles = StyleSheet.create({
540
563
  maxWidth: '50%',
541
564
  },
542
565
  background: {
543
- ...StyleSheet.absoluteFillObject,
566
+ ...StyleSheet.absoluteFill,
544
567
  pointerEvents: 'box-none',
545
568
  },
546
569
  });
@@ -12,11 +12,12 @@ import {
12
12
  View,
13
13
  } from 'react-native';
14
14
 
15
- import backIcon from '../assets/back-icon.png';
15
+ import backIconImage from '../assets/back-icon.png';
16
16
  import { isLiquidGlassSupported } from '../LiquidGlassView';
17
17
  import type {
18
18
  HeaderBackButtonDisplayMode,
19
19
  HeaderBackButtonProps,
20
+ Icon,
20
21
  } from '../types';
21
22
  import { BUTTON_SIZE, HeaderButton } from './HeaderButton';
22
23
  import { HeaderIcon } from './HeaderIcon';
@@ -24,7 +25,7 @@ import { HeaderIcon } from './HeaderIcon';
24
25
  export function HeaderBackButton({
25
26
  disabled,
26
27
  allowFontScaling,
27
- backImage,
28
+ icon,
28
29
  label,
29
30
  labelStyle,
30
31
  displayMode = 'minimal',
@@ -48,17 +49,30 @@ export function HeaderBackButton({
48
49
  const isMinimal = displayMode === 'minimal' || measuredMinimal;
49
50
 
50
51
  const renderBackImage = () => {
51
- if (backImage) {
52
- return backImage({ tintColor: tintColor ?? colors.text });
53
- } else {
54
- return (
55
- <HeaderIcon
56
- source={backIcon}
57
- tintColor={tintColor ?? colors.text}
58
- style={styles.icon}
59
- />
60
- );
52
+ const color = tintColor ?? colors.text;
53
+
54
+ if (typeof icon === 'function') {
55
+ return icon({ tintColor: color });
61
56
  }
57
+
58
+ const backIcon =
59
+ icon ??
60
+ Platform.select<Icon>({
61
+ ios: {
62
+ type: 'sfSymbol',
63
+ name: 'chevron.left',
64
+ },
65
+ android: {
66
+ type: 'materialSymbol',
67
+ name: 'arrow_back',
68
+ },
69
+ default: {
70
+ type: 'image',
71
+ source: backIconImage,
72
+ },
73
+ });
74
+
75
+ return <HeaderIcon icon={backIcon} color={color} style={styles.icon} />;
62
76
  };
63
77
 
64
78
  const handlePress = () => {
@@ -105,11 +119,11 @@ function HeaderBackLabel({
105
119
  truncatedLabel,
106
120
  onMeasureMinimal,
107
121
  }: {
108
- allowFontScaling?: boolean;
122
+ allowFontScaling?: boolean | undefined;
109
123
  displayMode: HeaderBackButtonDisplayMode;
110
124
  label: string | undefined;
111
- labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
112
- tintColor?: ColorValue;
125
+ labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>> | undefined;
126
+ tintColor?: ColorValue | undefined;
113
127
  truncatedLabel: string | undefined;
114
128
  onMeasureMinimal: () => void;
115
129
  }) {
@@ -231,8 +245,8 @@ function HeaderBackLabel({
231
245
  // iOS uses a smaller chevron, Android uses a larger arrow
232
246
  const ICON_WIDTH = Platform.OS === 'ios' ? 13 : 24;
233
247
  const ICON_SPACING_START = isLiquidGlassSupported
234
- ? 13 // Standard distance of chevron from left edge in liquid glass
235
- : 0; // Otherwise icon is aligned to the start of the button
248
+ ? 15 // Standard distance of chevron from left edge in liquid glass
249
+ : 2; // Otherwise icon is aligned to the start of the button
236
250
 
237
251
  // Standard distance between chevron and label
238
252
  const ICON_LABEL_SPACING = 9;
@@ -12,9 +12,9 @@ import { BlurEffectBackground } from '../BlurEffectBackground';
12
12
  import { type BlurEffectType } from '../getBlurBackgroundColor';
13
13
 
14
14
  type Props = Omit<ViewProps, 'style'> & {
15
- blurEffect?: BlurEffectType | 'none';
16
- style?: StyleProp<ViewStyle>;
17
- children?: React.ReactNode;
15
+ blurEffect?: BlurEffectType | 'none' | undefined;
16
+ style?: StyleProp<ViewStyle> | undefined;
17
+ children?: React.ReactNode | undefined;
18
18
  };
19
19
 
20
20
  export function HeaderBackground({