@react-navigation/elements 3.0.0-alpha.5 → 3.0.0-alpha.51

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 (213) 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 +6 -7
  6. package/lib/module/Badge.js.map +1 -1
  7. package/lib/module/BlurEffectBackground.js +3 -3
  8. package/lib/module/BlurEffectBackground.js.map +1 -1
  9. package/lib/module/Button.js +99 -14
  10. package/lib/module/Button.js.map +1 -1
  11. package/lib/module/Color.js +565 -4
  12. package/lib/module/Color.js.map +1 -1
  13. package/lib/module/Container.js +23 -1
  14. package/lib/module/Container.js.map +1 -1
  15. package/lib/module/Header/Header.js +30 -18
  16. package/lib/module/Header/Header.js.map +1 -1
  17. package/lib/module/Header/HeaderBackButton.js +27 -13
  18. package/lib/module/Header/HeaderBackButton.js.map +1 -1
  19. package/lib/module/Header/HeaderButton.js +4 -5
  20. package/lib/module/Header/HeaderButton.js.map +1 -1
  21. package/lib/module/Header/HeaderButtonBackground.js +1 -2
  22. package/lib/module/Header/HeaderButtonBackground.js.map +1 -1
  23. package/lib/module/Header/HeaderIcon.js +14 -17
  24. package/lib/module/Header/HeaderIcon.js.map +1 -1
  25. package/lib/module/Header/HeaderSearchBar.js +36 -26
  26. package/lib/module/Header/HeaderSearchBar.js.map +1 -1
  27. package/lib/module/Header/getHeaderTitle.js.map +1 -1
  28. package/lib/module/Header/useHeaderHeight.js +1 -1
  29. package/lib/module/Header/useHeaderHeight.js.map +1 -1
  30. package/lib/module/Label/getLabel.js.map +1 -1
  31. package/lib/module/LiquidGlassView.ios.js +1 -2
  32. package/lib/module/LiquidGlassView.ios.js.map +1 -1
  33. package/lib/module/LiquidGlassView.js +0 -4
  34. package/lib/module/LiquidGlassView.js.map +1 -1
  35. package/lib/module/PlatformColor.js +1 -0
  36. package/lib/module/PlatformColor.js.map +1 -1
  37. package/lib/module/PlatformColor.native.js +1 -1
  38. package/lib/module/PlatformColor.native.js.map +1 -1
  39. package/lib/module/PlatformIcon.js +46 -0
  40. package/lib/module/PlatformIcon.js.map +1 -0
  41. package/lib/module/PlatformPressable.js +64 -21
  42. package/lib/module/PlatformPressable.js.map +1 -1
  43. package/lib/module/SafeAreaProviderCompat.js +2 -4
  44. package/lib/module/SafeAreaProviderCompat.js.map +1 -1
  45. package/lib/module/Screen.js +59 -20
  46. package/lib/module/Screen.js.map +1 -1
  47. package/lib/module/index.js +2 -7
  48. package/lib/module/index.js.map +1 -1
  49. package/lib/module/internal.js +1 -1
  50. package/lib/module/internal.js.map +1 -1
  51. package/lib/module/useFrameSize.js +55 -5
  52. package/lib/module/useFrameSize.js.map +1 -1
  53. package/lib/typescript/src/ActivityView.d.ts +31 -0
  54. package/lib/typescript/src/ActivityView.d.ts.map +1 -0
  55. package/lib/typescript/src/ActivityView.native.d.ts +3 -0
  56. package/lib/typescript/src/ActivityView.native.d.ts.map +1 -0
  57. package/lib/typescript/src/Badge.d.ts +7 -6
  58. package/lib/typescript/src/Badge.d.ts.map +1 -1
  59. package/lib/typescript/src/BlurEffectBackground.d.ts +2 -2
  60. package/lib/typescript/src/BlurEffectBackground.d.ts.map +1 -1
  61. package/lib/typescript/src/Button.d.ts +54 -6
  62. package/lib/typescript/src/Button.d.ts.map +1 -1
  63. package/lib/typescript/src/Color.d.ts +4 -4
  64. package/lib/typescript/src/Color.d.ts.map +1 -1
  65. package/lib/typescript/src/Container.d.ts +8 -4
  66. package/lib/typescript/src/Container.d.ts.map +1 -1
  67. package/lib/typescript/src/Header/Header.d.ts +5 -4
  68. package/lib/typescript/src/Header/Header.d.ts.map +1 -1
  69. package/lib/typescript/src/Header/HeaderBackButton.d.ts +3 -2
  70. package/lib/typescript/src/Header/HeaderBackButton.d.ts.map +1 -1
  71. package/lib/typescript/src/Header/HeaderBackground.d.ts +5 -5
  72. package/lib/typescript/src/Header/HeaderBackground.d.ts.map +1 -1
  73. package/lib/typescript/src/Header/HeaderButton.d.ts +7 -2
  74. package/lib/typescript/src/Header/HeaderButton.d.ts.map +1 -1
  75. package/lib/typescript/src/Header/HeaderButtonBackground.d.ts +1 -1
  76. package/lib/typescript/src/Header/HeaderButtonBackground.d.ts.map +1 -1
  77. package/lib/typescript/src/Header/HeaderIcon.d.ts +10 -4
  78. package/lib/typescript/src/Header/HeaderIcon.d.ts.map +1 -1
  79. package/lib/typescript/src/Header/HeaderSearchBar.d.ts +10 -7
  80. package/lib/typescript/src/Header/HeaderSearchBar.d.ts.map +1 -1
  81. package/lib/typescript/src/Header/HeaderTitle.d.ts +4 -4
  82. package/lib/typescript/src/Header/HeaderTitle.d.ts.map +1 -1
  83. package/lib/typescript/src/Header/getHeaderTitle.d.ts +2 -2
  84. package/lib/typescript/src/Header/getHeaderTitle.d.ts.map +1 -1
  85. package/lib/typescript/src/Label/Label.d.ts +1 -1
  86. package/lib/typescript/src/Label/Label.d.ts.map +1 -1
  87. package/lib/typescript/src/Label/getLabel.d.ts +2 -2
  88. package/lib/typescript/src/Label/getLabel.d.ts.map +1 -1
  89. package/lib/typescript/src/LiquidGlassView.d.ts +7 -5
  90. package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -1
  91. package/lib/typescript/src/LiquidGlassView.ios.d.ts +4 -2
  92. package/lib/typescript/src/LiquidGlassView.ios.d.ts.map +1 -1
  93. package/lib/typescript/src/MissingIcon.d.ts +3 -3
  94. package/lib/typescript/src/MissingIcon.d.ts.map +1 -1
  95. package/lib/typescript/src/PlatformColor.d.ts +1 -0
  96. package/lib/typescript/src/PlatformColor.d.ts.map +1 -1
  97. package/lib/typescript/src/PlatformColor.native.d.ts +1 -1
  98. package/lib/typescript/src/PlatformColor.native.d.ts.map +1 -1
  99. package/lib/typescript/src/PlatformIcon.d.ts +11 -0
  100. package/lib/typescript/src/PlatformIcon.d.ts.map +1 -0
  101. package/lib/typescript/src/PlatformPressable.d.ts +12 -15
  102. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  103. package/lib/typescript/src/SafeAreaProviderCompat.d.ts +1 -1
  104. package/lib/typescript/src/SafeAreaProviderCompat.d.ts.map +1 -1
  105. package/lib/typescript/src/Screen.d.ts +10 -7
  106. package/lib/typescript/src/Screen.d.ts.map +1 -1
  107. package/lib/typescript/src/Text.d.ts +1 -1
  108. package/lib/typescript/src/Text.d.ts.map +1 -1
  109. package/lib/typescript/src/index.d.ts +21 -21
  110. package/lib/typescript/src/index.d.ts.map +1 -1
  111. package/lib/typescript/src/internal.d.ts +6 -6
  112. package/lib/typescript/src/internal.d.ts.map +1 -1
  113. package/lib/typescript/src/types.d.ts +120 -70
  114. package/lib/typescript/src/types.d.ts.map +1 -1
  115. package/lib/typescript/src/useFrameSize.d.ts +2 -2
  116. package/lib/typescript/src/useFrameSize.d.ts.map +1 -1
  117. package/package.json +27 -22
  118. package/src/ActivityView.native.tsx +88 -0
  119. package/src/ActivityView.tsx +127 -0
  120. package/src/Badge.tsx +11 -10
  121. package/src/BlurEffectBackground.tsx +3 -3
  122. package/src/Button.tsx +177 -28
  123. package/src/Color.tsx +755 -8
  124. package/src/Container.tsx +27 -4
  125. package/src/Header/Header.tsx +47 -27
  126. package/src/Header/HeaderBackButton.tsx +34 -20
  127. package/src/Header/HeaderBackground.tsx +3 -3
  128. package/src/Header/HeaderButton.tsx +16 -18
  129. package/src/Header/HeaderButtonBackground.tsx +1 -4
  130. package/src/Header/HeaderIcon.tsx +26 -13
  131. package/src/Header/HeaderSearchBar.tsx +58 -49
  132. package/src/Header/HeaderTitle.tsx +3 -3
  133. package/src/Header/getHeaderTitle.tsx +4 -1
  134. package/src/Header/useHeaderHeight.tsx +1 -1
  135. package/src/Label/getLabel.tsx +1 -1
  136. package/src/LiquidGlassView.ios.tsx +14 -9
  137. package/src/LiquidGlassView.tsx +15 -11
  138. package/src/MissingIcon.tsx +2 -2
  139. package/src/PlatformColor.native.tsx +1 -1
  140. package/src/PlatformColor.tsx +4 -0
  141. package/src/PlatformIcon.tsx +70 -0
  142. package/src/PlatformPressable.tsx +108 -48
  143. package/src/SafeAreaProviderCompat.tsx +2 -3
  144. package/src/Screen.tsx +87 -29
  145. package/src/index.tsx +2 -16
  146. package/src/internal.tsx +1 -1
  147. package/src/types.tsx +153 -76
  148. package/src/useFrameSize.tsx +71 -7
  149. package/lib/module/Lazy.js +0 -42
  150. package/lib/module/Lazy.js.map +0 -1
  151. package/lib/module/assets/back-icon.ios.svg +0 -4
  152. package/lib/module/assets/back-icon@1x.android.png +0 -0
  153. package/lib/module/assets/back-icon@1x.ios.png +0 -0
  154. package/lib/module/assets/back-icon@2x.android.png +0 -0
  155. package/lib/module/assets/back-icon@2x.ios.png +0 -0
  156. package/lib/module/assets/back-icon@3x.android.png +0 -0
  157. package/lib/module/assets/back-icon@3x.ios.png +0 -0
  158. package/lib/module/assets/back-icon@4x.android.png +0 -0
  159. package/lib/module/assets/back-icon@4x.ios.png +0 -0
  160. package/lib/module/assets/clear-icon@1x.png +0 -0
  161. package/lib/module/assets/clear-icon@2x.png +0 -0
  162. package/lib/module/assets/clear-icon@3x.png +0 -0
  163. package/lib/module/assets/clear-icon@4x.png +0 -0
  164. package/lib/module/assets/close-icon@1x.png +0 -0
  165. package/lib/module/assets/close-icon@2x.png +0 -0
  166. package/lib/module/assets/close-icon@3x.png +0 -0
  167. package/lib/module/assets/close-icon@4x.png +0 -0
  168. package/lib/module/assets/search-icon-legacy@1x.ios.png +0 -0
  169. package/lib/module/assets/search-icon-legacy@2x.ios.png +0 -0
  170. package/lib/module/assets/search-icon-legacy@3x.ios.png +0 -0
  171. package/lib/module/assets/search-icon-legacy@4x.ios.png +0 -0
  172. package/lib/module/assets/search-icon.ios.svg +0 -4
  173. package/lib/module/assets/search-icon@1x.android.png +0 -0
  174. package/lib/module/assets/search-icon@1x.ios.png +0 -0
  175. package/lib/module/assets/search-icon@2x.android.png +0 -0
  176. package/lib/module/assets/search-icon@2x.ios.png +0 -0
  177. package/lib/module/assets/search-icon@3x.android.png +0 -0
  178. package/lib/module/assets/search-icon@3x.ios.png +0 -0
  179. package/lib/module/assets/search-icon@4x.android.png +0 -0
  180. package/lib/module/assets/search-icon@4x.ios.png +0 -0
  181. package/lib/typescript/src/Lazy.d.ts +0 -31
  182. package/lib/typescript/src/Lazy.d.ts.map +0 -1
  183. package/src/Lazy.tsx +0 -59
  184. package/src/assets/back-icon.ios.svg +0 -4
  185. package/src/assets/back-icon@1x.android.png +0 -0
  186. package/src/assets/back-icon@1x.ios.png +0 -0
  187. package/src/assets/back-icon@2x.android.png +0 -0
  188. package/src/assets/back-icon@2x.ios.png +0 -0
  189. package/src/assets/back-icon@3x.android.png +0 -0
  190. package/src/assets/back-icon@3x.ios.png +0 -0
  191. package/src/assets/back-icon@4x.android.png +0 -0
  192. package/src/assets/back-icon@4x.ios.png +0 -0
  193. package/src/assets/clear-icon@1x.png +0 -0
  194. package/src/assets/clear-icon@2x.png +0 -0
  195. package/src/assets/clear-icon@3x.png +0 -0
  196. package/src/assets/clear-icon@4x.png +0 -0
  197. package/src/assets/close-icon@1x.png +0 -0
  198. package/src/assets/close-icon@2x.png +0 -0
  199. package/src/assets/close-icon@3x.png +0 -0
  200. package/src/assets/close-icon@4x.png +0 -0
  201. package/src/assets/search-icon-legacy@1x.ios.png +0 -0
  202. package/src/assets/search-icon-legacy@2x.ios.png +0 -0
  203. package/src/assets/search-icon-legacy@3x.ios.png +0 -0
  204. package/src/assets/search-icon-legacy@4x.ios.png +0 -0
  205. package/src/assets/search-icon.ios.svg +0 -4
  206. package/src/assets/search-icon@1x.android.png +0 -0
  207. package/src/assets/search-icon@1x.ios.png +0 -0
  208. package/src/assets/search-icon@2x.android.png +0 -0
  209. package/src/assets/search-icon@2x.ios.png +0 -0
  210. package/src/assets/search-icon@3x.android.png +0 -0
  211. package/src/assets/search-icon@3x.ios.png +0 -0
  212. package/src/assets/search-icon@4x.android.png +0 -0
  213. package/src/assets/search-icon@4x.ios.png +0 -0
package/src/Container.tsx CHANGED
@@ -1,18 +1,40 @@
1
+ import * as React from 'react';
1
2
  import { Platform, View, type ViewStyle } from 'react-native';
2
3
 
3
4
  export type Props = {
4
- inert?: boolean;
5
- style?: React.CSSProperties & ViewStyle;
5
+ ref?: React.Ref<HTMLDivElement | React.ComponentRef<typeof View>> | undefined;
6
+ inert?: boolean | undefined;
7
+ style?:
8
+ | (ViewStyle &
9
+ Omit<React.CSSProperties, 'backgroundColor'> & {
10
+ backgroundColor?: ViewStyle['backgroundColor'] | undefined;
11
+ })
12
+ | undefined;
6
13
  children: React.ReactNode;
7
14
  };
8
15
 
9
- export function Container({ inert, children, style }: Props) {
16
+ export function Container({ ref, inert, children, style }: Props) {
10
17
  if (Platform.OS === 'web') {
18
+ const { backgroundColor, flex, ...rest } = style ?? {};
19
+
11
20
  return (
12
21
  <div
22
+ ref={ref as React.Ref<HTMLDivElement> | undefined}
13
23
  inert={inert}
14
24
  aria-hidden={inert}
15
- style={{ ...DEFAULT_STYLE, ...style }}
25
+ style={{
26
+ ...DEFAULT_STYLE,
27
+ // FIXME: A numeric flex shorthand uses a percentage basis in browsers.
28
+ // With nested containers, it slows down layout in Safari on iOS 27.
29
+ // So we explicitly set it to `0` without percentage, which renders 0px.
30
+ ...(flex !== undefined && flex > 0
31
+ ? { flexGrow: flex, flexShrink: 1, flexBasis: 0 }
32
+ : { flex }),
33
+ ...rest,
34
+ backgroundColor:
35
+ // In practice we only get string on web instead of OpaqueValue
36
+ typeof backgroundColor === 'string' ? backgroundColor : undefined,
37
+ }}
16
38
  >
17
39
  {children}
18
40
  </div>
@@ -21,6 +43,7 @@ export function Container({ inert, children, style }: Props) {
21
43
 
22
44
  return (
23
45
  <View
46
+ ref={ref as React.Ref<View> | undefined}
24
47
  aria-hidden={inert}
25
48
  style={[{ pointerEvents: inert ? 'none' : 'box-none' }, style]}
26
49
  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
 
@@ -174,7 +180,7 @@ export function Header(props: Props) {
174
180
  transform,
175
181
  transformOrigin,
176
182
  ...unsafeStyles
177
- } = StyleSheet.flatten(customHeaderStyle || {}) as ViewStyle;
183
+ } = (StyleSheet.flatten(customHeaderStyle) || {}) as ViewStyle;
178
184
 
179
185
  if (process.env.NODE_ENV !== 'production') {
180
186
  warnIfHeaderStylesDefined(unsafeStyles);
@@ -231,12 +237,9 @@ export function Header(props: Props) {
231
237
  // Setting a property to undefined triggers default style
232
238
  // So we need to filter them out
233
239
  // Users can use `null` instead
234
- for (const styleProp in safeStyles) {
235
- // @ts-expect-error: typescript wrongly complains that styleProp cannot be used to index safeStyles
236
- if (safeStyles[styleProp] === undefined) {
237
- // @ts-expect-error don't need to care about index signature for deletion
238
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
239
- delete safeStyles[styleProp];
240
+ for (const [styleProp, value] of Object.entries(safeStyles)) {
241
+ if (value === undefined) {
242
+ Reflect.deleteProperty(safeStyles, styleProp);
240
243
  }
241
244
  }
242
245
 
@@ -321,21 +324,21 @@ export function Header(props: Props) {
321
324
  return;
322
325
  }
323
326
 
324
- Animated.timing(searchBarVisibleAnim, {
327
+ const animation = Animated.timing(searchBarVisibleAnim, {
325
328
  toValue: searchBarVisible ? 1 : 0,
326
329
  duration: 150,
327
330
  useNativeDriver,
328
331
  easing: Easing.in(Easing.linear),
329
- }).start(({ finished }) => {
332
+ });
333
+
334
+ animation.start(({ finished }) => {
330
335
  if (finished) {
331
336
  setSearchBarRendered(searchBarVisible);
332
337
  searchBarVisibleRef.current = searchBarVisible;
333
338
  }
334
339
  });
335
340
 
336
- return () => {
337
- searchBarVisibleAnim.stopAnimation();
338
- };
341
+ return () => animation.stop();
339
342
  }, [searchBarVisible, searchBarVisibleAnim]);
340
343
 
341
344
  const headerOpacity = searchBarVisibleAnim.interpolate({
@@ -402,6 +405,7 @@ export function Header(props: Props) {
402
405
  },
403
406
  ]}
404
407
  >
408
+ <UNSTABLE_CornerInset direction="horizontal" edge="left" />
405
409
  <View
406
410
  style={[
407
411
  styles.start,
@@ -458,7 +462,23 @@ export function Header(props: Props) {
458
462
  headerSearchBarOptions?.onOpen?.();
459
463
  }}
460
464
  >
461
- <HeaderIcon source={searchIcon} tintColor={iconTintColor} />
465
+ <HeaderIcon
466
+ icon={Platform.select({
467
+ android: {
468
+ type: 'materialSymbol',
469
+ name: 'search',
470
+ },
471
+ ios: {
472
+ type: 'sfSymbol',
473
+ name: 'magnifyingglass',
474
+ },
475
+ default: {
476
+ type: 'image',
477
+ source: searchIcon,
478
+ },
479
+ })}
480
+ color={iconTintColor}
481
+ />
462
482
  </HeaderButton>
463
483
  ) : null}
464
484
  </HeaderButtonBackground>
@@ -540,7 +560,7 @@ const styles = StyleSheet.create({
540
560
  maxWidth: '50%',
541
561
  },
542
562
  background: {
543
- ...StyleSheet.absoluteFillObject,
563
+ ...StyleSheet.absoluteFill,
544
564
  pointerEvents: 'box-none',
545
565
  },
546
566
  });
@@ -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
  }) {
@@ -121,9 +135,9 @@ function HeaderBackLabel({
121
135
  number | null
122
136
  >(null);
123
137
 
124
- const wrapperRef = React.useRef<View | null>(null);
125
- const labelRef = React.useRef<Text | null>(null);
126
- const truncatedLabelRef = React.useRef<Text | null>(null);
138
+ const wrapperRef = React.useRef<React.ComponentRef<typeof View>>(null);
139
+ const labelRef = React.useRef<React.ComponentRef<typeof Text>>(null);
140
+ const truncatedLabelRef = React.useRef<React.ComponentRef<typeof Text>>(null);
127
141
 
128
142
  React.useLayoutEffect(() => {
129
143
  wrapperRef.current?.measure((_x, _y, width) => {
@@ -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({
@@ -4,20 +4,22 @@ import { Platform, StyleSheet } from 'react-native';
4
4
  import { PlatformPressable } from '../PlatformPressable';
5
5
  import type { HeaderButtonProps } from '../types';
6
6
 
7
- function HeaderButtonInternal(
8
- {
9
- disabled,
10
- onPress,
11
- pressColor,
12
- pressOpacity,
13
- accessibilityLabel,
14
- testID,
15
- style,
16
- href,
17
- children,
18
- }: HeaderButtonProps,
19
- ref: React.Ref<React.ComponentRef<typeof PlatformPressable>>
20
- ) {
7
+ type Props = HeaderButtonProps & {
8
+ ref?: React.Ref<React.ComponentRef<typeof PlatformPressable>> | undefined;
9
+ };
10
+
11
+ export function HeaderButton({
12
+ disabled,
13
+ onPress,
14
+ pressColor,
15
+ pressOpacity,
16
+ accessibilityLabel,
17
+ testID,
18
+ style,
19
+ href,
20
+ children,
21
+ ref,
22
+ }: Props) {
21
23
  return (
22
24
  <PlatformPressable
23
25
  ref={ref}
@@ -40,10 +42,6 @@ function HeaderButtonInternal(
40
42
  );
41
43
  }
42
44
 
43
- export const HeaderButton = React.forwardRef(HeaderButtonInternal);
44
-
45
- HeaderButton.displayName = 'HeaderButton';
46
-
47
45
  export const BUTTON_SIZE = Platform.OS === 'ios' ? 44 : 48;
48
46
  export const BUTTON_SPACING = Platform.OS === 'ios' ? 10 : 8;
49
47
 
@@ -1,15 +1,12 @@
1
1
  import { Animated, StyleSheet } from 'react-native';
2
2
 
3
- import { LiquidGlassView } from '../LiquidGlassView';
3
+ import { AnimatedLiquidGlassView } from '../LiquidGlassView';
4
4
  import { BUTTON_SIZE } from './HeaderButton';
5
5
 
6
6
  type Props = React.ComponentProps<typeof Animated.View> & {
7
7
  plain?: boolean;
8
8
  };
9
9
 
10
- const AnimatedLiquidGlassView =
11
- Animated.createAnimatedComponent(LiquidGlassView);
12
-
13
10
  export function HeaderButtonBackground({ plain, style, ...rest }: Props) {
14
11
  if (plain) {
15
12
  return <Animated.View style={[styles.container, style]} {...rest} />;
@@ -1,24 +1,37 @@
1
- import { useLocale, useTheme } from '@react-navigation/native';
2
- import { Image, type ImageProps, Platform, StyleSheet } from 'react-native';
1
+ import { useLocale } from '@react-navigation/native';
2
+ import {
3
+ type ColorValue,
4
+ Platform,
5
+ type StyleProp,
6
+ StyleSheet,
7
+ } from 'react-native';
3
8
 
4
- export function HeaderIcon({ source, style, ...rest }: ImageProps) {
5
- const { colors } = useTheme();
9
+ import { PlatformIcon, type PlatformIconStyle } from '../PlatformIcon';
10
+ import type { Icon } from '../types';
11
+
12
+ type Props = {
13
+ icon: Icon;
14
+ color: ColorValue;
15
+ style?: StyleProp<PlatformIconStyle> | undefined;
16
+ };
17
+
18
+ export function HeaderIcon({ icon, color, style }: Props) {
6
19
  const { direction } = useLocale();
7
20
 
21
+ const iconStyle = [styles.icon, direction === 'rtl' && styles.flip, style];
22
+
8
23
  return (
9
- <Image
10
- source={source}
11
- resizeMode="contain"
12
- fadeDuration={0}
13
- tintColor={colors.text}
14
- style={[styles.icon, direction === 'rtl' && styles.flip, style]}
15
- {...rest}
24
+ <PlatformIcon
25
+ icon={icon}
26
+ color={color}
27
+ size={ICON_SIZE}
28
+ style={iconStyle}
16
29
  />
17
30
  );
18
31
  }
19
32
 
20
- export const ICON_SIZE = Platform.OS === 'ios' ? 21 : 24;
21
- export const ICON_MARGIN = Platform.OS === 'ios' ? 8 : 3;
33
+ const ICON_SIZE = Platform.OS === 'ios' ? 21 : 24;
34
+ const ICON_MARGIN = Platform.OS === 'ios' ? 8 : 3;
22
35
 
23
36
  const styles = StyleSheet.create({
24
37
  icon: {
@@ -1,11 +1,9 @@
1
- import { useNavigation, useTheme } from '@react-navigation/native';
1
+ import { SFSymbol, useNavigation, useTheme } from '@react-navigation/native';
2
2
  import * as React from 'react';
3
3
  import {
4
4
  Animated,
5
5
  BackHandler,
6
6
  type ColorValue,
7
- Image,
8
- type NativeEventSubscription,
9
7
  Platform,
10
8
  type StyleProp,
11
9
  StyleSheet,
@@ -14,10 +12,7 @@ import {
14
12
  type ViewStyle,
15
13
  } from 'react-native';
16
14
 
17
- import clearIcon from '../assets/clear-icon.png';
18
15
  import closeIcon from '../assets/close-icon.png';
19
- import searchIcon from '../assets/search-icon.png';
20
- import searchIconLegacy from '../assets/search-icon-legacy.png';
21
16
  import { Color } from '../Color';
22
17
  import {
23
18
  AnimatedLiquidGlassContainerView,
@@ -34,11 +29,12 @@ import { HeaderIcon } from './HeaderIcon';
34
29
  type Props = Omit<HeaderSearchBarOptions, 'ref'> & {
35
30
  visible: boolean;
36
31
  onClose: () => void;
37
- tintColor?: ColorValue;
38
- pressColor?: ColorValue;
39
- pressOpacity?: number;
32
+ tintColor?: ColorValue | undefined;
33
+ pressColor?: ColorValue | undefined;
34
+ pressOpacity?: number | undefined;
40
35
  statusBarHeight: number;
41
- style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
36
+ style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>> | undefined;
37
+ ref?: React.Ref<HeaderSearchBarRef> | undefined;
42
38
  };
43
39
 
44
40
  const INPUT_TYPE_TO_MODE = {
@@ -50,33 +46,31 @@ const INPUT_TYPE_TO_MODE = {
50
46
 
51
47
  const useNativeDriver = Platform.OS !== 'web';
52
48
 
53
- function HeaderSearchBarInternal(
54
- {
55
- visible,
56
- inputType,
57
- autoFocus = true,
58
- autoCapitalize,
59
- placeholder = 'Search',
60
- enterKeyHint = 'search',
61
- cancelButtonText = 'Cancel',
62
- onChange,
63
- onClose,
64
- tintColor,
65
- pressColor,
66
- pressOpacity,
67
- statusBarHeight,
68
- style,
69
- ...rest
70
- }: Props,
71
- ref: React.ForwardedRef<HeaderSearchBarRef>
72
- ) {
49
+ export function HeaderSearchBar({
50
+ visible,
51
+ inputType,
52
+ autoFocus = true,
53
+ autoCapitalize,
54
+ placeholder = 'Search',
55
+ enterKeyHint = 'search',
56
+ cancelButtonText = 'Cancel',
57
+ onChange,
58
+ onClose,
59
+ tintColor,
60
+ pressColor,
61
+ pressOpacity,
62
+ statusBarHeight,
63
+ style,
64
+ ref,
65
+ ...rest
66
+ }: Props) {
73
67
  const navigation = useNavigation();
74
68
  const { dark, colors, fonts } = useTheme();
75
69
  const [value, setValue] = React.useState('');
76
70
  const [clearVisibleAnim] = React.useState(() => new Animated.Value(0));
77
71
 
78
72
  const clearVisibleValueRef = React.useRef(false);
79
- const inputRef = React.useRef<TextInput>(null);
73
+ const inputRef = React.useRef<React.ComponentRef<typeof TextInput>>(null);
80
74
 
81
75
  const hasText = value !== '';
82
76
 
@@ -126,13 +120,17 @@ function HeaderSearchBarInternal(
126
120
  }
127
121
  };
128
122
 
129
- let backHandlerSubscription: NativeEventSubscription | undefined;
123
+ let backHandlerSubscription:
124
+ | ReturnType<typeof BackHandler.addEventListener>
125
+ | undefined;
130
126
 
131
127
  if (Platform.OS === 'web') {
132
128
  document?.body?.addEventListener?.('keyup', onKeyup);
133
129
  } else {
130
+ // eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
134
131
  backHandlerSubscription = BackHandler.addEventListener(
135
132
  'hardwareBackPress',
133
+ // eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
136
134
  () => {
137
135
  cancelSearch();
138
136
  return true;
@@ -210,13 +208,14 @@ function HeaderSearchBarInternal(
210
208
  ) : null}
211
209
  <HeaderButtonBackground style={styles.searchbarContainer}>
212
210
  {Platform.OS === 'ios' ? (
213
- <HeaderIcon
214
- source={isLiquidGlassSupported ? searchIcon : searchIconLegacy}
215
- tintColor={textColor}
216
- style={[
211
+ <SFSymbol
212
+ name="magnifyingglass"
213
+ size={SEARCH_ICON_SIZE}
214
+ color={textColor}
215
+ style={StyleSheet.flatten([
217
216
  styles.inputSearchIconIos,
218
217
  !isLiquidGlassSupported && styles.inputSearchIconIosLegacy,
219
- ]}
218
+ ])}
220
219
  />
221
220
  ) : null}
222
221
  <TextInput
@@ -263,10 +262,10 @@ function HeaderSearchBarInternal(
263
262
  !isLiquidGlassSupported && styles.clearButtonIosLegacy,
264
263
  ]}
265
264
  >
266
- <Image
267
- source={clearIcon}
268
- resizeMode="contain"
269
- tintColor={textColor}
265
+ <SFSymbol
266
+ name="xmark.circle.fill"
267
+ size={CLEAR_ICON_SIZE}
268
+ color={textColor}
270
269
  style={styles.clearIconIos}
271
270
  />
272
271
  </PlatformPressable>
@@ -278,7 +277,19 @@ function HeaderSearchBarInternal(
278
277
  onPress={onClear}
279
278
  style={[styles.closeButton, { opacity: clearVisibleAnim }]}
280
279
  >
281
- <HeaderIcon source={closeIcon} tintColor={textColor} />
280
+ <HeaderIcon
281
+ icon={Platform.select({
282
+ android: {
283
+ type: 'materialSymbol',
284
+ name: 'close',
285
+ },
286
+ default: {
287
+ type: 'image',
288
+ source: closeIcon,
289
+ },
290
+ })}
291
+ color={textColor}
292
+ />
282
293
  </HeaderButton>
283
294
  ) : null}
284
295
  {Platform.OS === 'ios' ? (
@@ -288,7 +299,7 @@ function HeaderSearchBarInternal(
288
299
  accessibilityLabel={cancelButtonText}
289
300
  onPress={cancelSearch}
290
301
  >
291
- <HeaderIcon source={closeIcon} tintColor={textColor} />
302
+ <SFSymbol name="xmark" color={textColor} />
292
303
  </HeaderButton>
293
304
  </HeaderButtonBackground>
294
305
  ) : (
@@ -332,14 +343,14 @@ const styles = StyleSheet.create({
332
343
  },
333
344
  inputSearchIconIos: {
334
345
  position: 'absolute',
335
- opacity: 0.5,
336
- top: SEARCHBAR_ICON_SPACING,
337
- left: SEARCHBAR_ICON_SPACING,
346
+ top: '50%',
347
+ left: BUTTON_SPACING + SEARCHBAR_ICON_SPACING / 2,
348
+ marginTop: -SEARCH_ICON_SIZE / 2,
338
349
  height: SEARCH_ICON_SIZE,
339
350
  width: SEARCH_ICON_SIZE,
340
351
  },
341
352
  inputSearchIconIosLegacy: {
342
- top: SEARCHBAR_LEGACY_VERTICAL_OFFSET_IOS,
353
+ top: SEARCHBAR_ICON_SPACING,
343
354
  },
344
355
  backButton: {
345
356
  alignSelf: 'center',
@@ -417,5 +428,3 @@ const styles = StyleSheet.create({
417
428
  }),
418
429
  },
419
430
  });
420
-
421
- export const HeaderSearchBar = React.forwardRef(HeaderSearchBarInternal);
@@ -10,9 +10,9 @@ import {
10
10
  } from 'react-native';
11
11
 
12
12
  type Props = Omit<TextProps, 'style'> & {
13
- tintColor?: ColorValue;
14
- children?: string;
15
- style?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
13
+ tintColor?: ColorValue | undefined;
14
+ children?: string | undefined;
15
+ style?: Animated.WithAnimatedValue<StyleProp<TextStyle>> | undefined;
16
16
  };
17
17
 
18
18
  export function HeaderTitle({ tintColor, style, ...rest }: Props) {
@@ -1,7 +1,10 @@
1
1
  import type { HeaderOptions } from '../types';
2
2
 
3
3
  export function getHeaderTitle(
4
- options: { title?: string; headerTitle?: HeaderOptions['headerTitle'] },
4
+ options: {
5
+ title?: string | undefined;
6
+ headerTitle?: HeaderOptions['headerTitle'];
7
+ },
5
8
  fallback: string
6
9
  ): string {
7
10
  return typeof options.headerTitle === 'string'
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { HeaderHeightContext } from './HeaderHeightContext';
4
4
 
5
5
  export function useHeaderHeight() {
6
- const height = React.useContext(HeaderHeightContext);
6
+ const height = React.use(HeaderHeightContext);
7
7
 
8
8
  if (height === undefined) {
9
9
  throw new Error(
@@ -1,5 +1,5 @@
1
1
  export function getLabel(
2
- options: { label?: string; title?: string },
2
+ options: { label?: string | undefined; title?: string | undefined },
3
3
  fallback: string
4
4
  ): string {
5
5
  return options.label !== undefined