@react-navigation/elements 2.9.5 → 3.0.0-alpha.1

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 (184) hide show
  1. package/lib/module/Badge.js +2 -2
  2. package/lib/module/Badge.js.map +1 -1
  3. package/lib/module/BlurEffectBackground.js +59 -0
  4. package/lib/module/BlurEffectBackground.js.map +1 -0
  5. package/lib/module/Button.js +7 -6
  6. package/lib/module/Button.js.map +1 -1
  7. package/lib/module/Color.js +11 -0
  8. package/lib/module/Color.js.map +1 -0
  9. package/lib/module/Container.js +42 -0
  10. package/lib/module/Container.js.map +1 -0
  11. package/lib/module/Header/Header.js +156 -97
  12. package/lib/module/Header/Header.js.map +1 -1
  13. package/lib/module/Header/HeaderBackButton.js +130 -121
  14. package/lib/module/Header/HeaderBackButton.js.map +1 -1
  15. package/lib/module/Header/HeaderBackground.js +10 -17
  16. package/lib/module/Header/HeaderBackground.js.map +1 -1
  17. package/lib/module/Header/HeaderButton.js +6 -2
  18. package/lib/module/Header/HeaderButton.js.map +1 -1
  19. package/lib/module/Header/HeaderButtonBackground.js +34 -0
  20. package/lib/module/Header/HeaderButtonBackground.js.map +1 -0
  21. package/lib/module/Header/HeaderSearchBar.js +174 -123
  22. package/lib/module/Header/HeaderSearchBar.js.map +1 -1
  23. package/lib/module/Header/HeaderTitle.js.map +1 -1
  24. package/lib/module/Header/getDefaultHeaderHeight.js +22 -10
  25. package/lib/module/Header/getDefaultHeaderHeight.js.map +1 -1
  26. package/lib/module/Label/Label.js.map +1 -1
  27. package/lib/module/LiquidGlassView.ios.js +21 -0
  28. package/lib/module/LiquidGlassView.ios.js.map +1 -0
  29. package/lib/module/LiquidGlassView.js +13 -0
  30. package/lib/module/LiquidGlassView.js.map +1 -0
  31. package/lib/module/MissingIcon.js +1 -0
  32. package/lib/module/MissingIcon.js.map +1 -1
  33. package/lib/module/PlatformColor.js +9 -0
  34. package/lib/module/PlatformColor.js.map +1 -0
  35. package/lib/module/PlatformColor.native.js +4 -0
  36. package/lib/module/PlatformColor.native.js.map +1 -0
  37. package/lib/module/PlatformPressable.js.map +1 -1
  38. package/lib/module/Screen.js +29 -23
  39. package/lib/module/Screen.js.map +1 -1
  40. package/lib/module/assets/back-icon.ios.svg +4 -0
  41. package/lib/module/assets/back-icon@1x.ios.png +0 -0
  42. package/lib/module/assets/back-icon@2x.ios.png +0 -0
  43. package/lib/module/assets/back-icon@3x.ios.png +0 -0
  44. package/lib/module/assets/back-icon@4x.ios.png +0 -0
  45. package/lib/module/assets/search-icon-legacy.png +0 -0
  46. package/lib/module/assets/search-icon-legacy@1x.ios.png +0 -0
  47. package/lib/module/assets/search-icon-legacy@2x.ios.png +0 -0
  48. package/lib/module/assets/search-icon-legacy@3x.ios.png +0 -0
  49. package/lib/module/assets/search-icon-legacy@4x.ios.png +0 -0
  50. package/lib/module/assets/search-icon.ios.svg +4 -0
  51. package/lib/module/assets/search-icon@1x.ios.png +0 -0
  52. package/lib/module/assets/search-icon@2x.ios.png +0 -0
  53. package/lib/module/assets/search-icon@3x.ios.png +0 -0
  54. package/lib/module/assets/search-icon@4x.ios.png +0 -0
  55. package/lib/module/getBlurBackgroundColor.js +48 -0
  56. package/lib/module/getBlurBackgroundColor.js.map +1 -0
  57. package/lib/module/index.js +2 -8
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/internal.js +10 -0
  60. package/lib/module/internal.js.map +1 -0
  61. package/lib/module/useFrameSize.js +4 -4
  62. package/lib/module/useFrameSize.js.map +1 -1
  63. package/lib/typescript/src/Badge.d.ts.map +1 -1
  64. package/lib/typescript/src/BlurEffectBackground.d.ts +16 -0
  65. package/lib/typescript/src/BlurEffectBackground.d.ts.map +1 -0
  66. package/lib/typescript/src/Button.d.ts +5 -4
  67. package/lib/typescript/src/Button.d.ts.map +1 -1
  68. package/lib/typescript/src/Color.d.ts +13 -0
  69. package/lib/typescript/src/Color.d.ts.map +1 -0
  70. package/lib/typescript/src/Container.d.ts +8 -0
  71. package/lib/typescript/src/Container.d.ts.map +1 -0
  72. package/lib/typescript/src/Header/Header.d.ts +1 -5
  73. package/lib/typescript/src/Header/Header.d.ts.map +1 -1
  74. package/lib/typescript/src/Header/HeaderBackButton.d.ts +1 -1
  75. package/lib/typescript/src/Header/HeaderBackButton.d.ts.map +1 -1
  76. package/lib/typescript/src/Header/HeaderBackground.d.ts +5 -3
  77. package/lib/typescript/src/Header/HeaderBackground.d.ts.map +1 -1
  78. package/lib/typescript/src/Header/HeaderButton.d.ts +2 -0
  79. package/lib/typescript/src/Header/HeaderButton.d.ts.map +1 -1
  80. package/lib/typescript/src/Header/HeaderButtonBackground.d.ts +7 -0
  81. package/lib/typescript/src/Header/HeaderButtonBackground.d.ts.map +1 -0
  82. package/lib/typescript/src/Header/HeaderSearchBar.d.ts +5 -2
  83. package/lib/typescript/src/Header/HeaderSearchBar.d.ts.map +1 -1
  84. package/lib/typescript/src/Header/HeaderTitle.d.ts +2 -2
  85. package/lib/typescript/src/Header/HeaderTitle.d.ts.map +1 -1
  86. package/lib/typescript/src/Header/getDefaultHeaderHeight.d.ts +5 -2
  87. package/lib/typescript/src/Header/getDefaultHeaderHeight.d.ts.map +1 -1
  88. package/lib/typescript/src/Label/Label.d.ts +2 -2
  89. package/lib/typescript/src/Label/Label.d.ts.map +1 -1
  90. package/lib/typescript/src/LiquidGlassView.d.ts +9 -0
  91. package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -0
  92. package/lib/typescript/src/LiquidGlassView.ios.d.ts +5 -0
  93. package/lib/typescript/src/LiquidGlassView.ios.d.ts.map +1 -0
  94. package/lib/typescript/src/MissingIcon.d.ts +2 -2
  95. package/lib/typescript/src/MissingIcon.d.ts.map +1 -1
  96. package/lib/typescript/src/PlatformColor.d.ts +7 -0
  97. package/lib/typescript/src/PlatformColor.d.ts.map +1 -0
  98. package/lib/typescript/src/PlatformColor.native.d.ts +2 -0
  99. package/lib/typescript/src/PlatformColor.native.d.ts.map +1 -0
  100. package/lib/typescript/src/PlatformPressable.d.ts +3 -3
  101. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  102. package/lib/typescript/src/Screen.d.ts.map +1 -1
  103. package/lib/typescript/src/getBlurBackgroundColor.d.ts +7 -0
  104. package/lib/typescript/src/getBlurBackgroundColor.d.ts.map +1 -0
  105. package/lib/typescript/src/index.d.ts +0 -6
  106. package/lib/typescript/src/index.d.ts.map +1 -1
  107. package/lib/typescript/src/internal.d.ts +8 -0
  108. package/lib/typescript/src/internal.d.ts.map +1 -0
  109. package/lib/typescript/src/types.d.ts +47 -31
  110. package/lib/typescript/src/types.d.ts.map +1 -1
  111. package/package.json +19 -17
  112. package/src/Badge.tsx +3 -2
  113. package/src/BlurEffectBackground.tsx +90 -0
  114. package/src/Button.tsx +33 -21
  115. package/src/Color.tsx +21 -0
  116. package/src/Container.tsx +44 -0
  117. package/src/Header/Header.tsx +230 -156
  118. package/src/Header/HeaderBackButton.tsx +194 -168
  119. package/src/Header/HeaderBackground.tsx +17 -19
  120. package/src/Header/HeaderButton.tsx +7 -2
  121. package/src/Header/HeaderButtonBackground.tsx +35 -0
  122. package/src/Header/HeaderSearchBar.tsx +227 -129
  123. package/src/Header/HeaderTitle.tsx +2 -1
  124. package/src/Header/getDefaultHeaderHeight.tsx +29 -18
  125. package/src/Label/Label.tsx +2 -1
  126. package/src/LiquidGlassView.ios.tsx +39 -0
  127. package/src/LiquidGlassView.tsx +20 -0
  128. package/src/MissingIcon.tsx +12 -3
  129. package/src/PlatformColor.native.tsx +1 -0
  130. package/src/PlatformColor.tsx +8 -0
  131. package/src/PlatformPressable.tsx +2 -1
  132. package/src/Screen.tsx +24 -25
  133. package/src/assets/back-icon.ios.svg +4 -0
  134. package/src/assets/back-icon@1x.ios.png +0 -0
  135. package/src/assets/back-icon@2x.ios.png +0 -0
  136. package/src/assets/back-icon@3x.ios.png +0 -0
  137. package/src/assets/back-icon@4x.ios.png +0 -0
  138. package/src/assets/search-icon-legacy.png +0 -0
  139. package/src/assets/search-icon-legacy@1x.ios.png +0 -0
  140. package/src/assets/search-icon-legacy@2x.ios.png +0 -0
  141. package/src/assets/search-icon-legacy@3x.ios.png +0 -0
  142. package/src/assets/search-icon-legacy@4x.ios.png +0 -0
  143. package/src/assets/search-icon.ios.svg +4 -0
  144. package/src/assets/search-icon@1x.ios.png +0 -0
  145. package/src/assets/search-icon@2x.ios.png +0 -0
  146. package/src/assets/search-icon@3x.ios.png +0 -0
  147. package/src/assets/search-icon@4x.ios.png +0 -0
  148. package/src/getBlurBackgroundColor.tsx +68 -0
  149. package/src/index.tsx +2 -8
  150. package/src/internal.tsx +7 -0
  151. package/src/types.tsx +50 -32
  152. package/src/useFrameSize.tsx +4 -4
  153. package/lib/module/Background.js +0 -22
  154. package/lib/module/Background.js.map +0 -1
  155. package/lib/module/MaskedView.android.js +0 -4
  156. package/lib/module/MaskedView.android.js.map +0 -1
  157. package/lib/module/MaskedView.ios.js +0 -4
  158. package/lib/module/MaskedView.ios.js.map +0 -1
  159. package/lib/module/MaskedView.js +0 -12
  160. package/lib/module/MaskedView.js.map +0 -1
  161. package/lib/module/MaskedViewNative.js +0 -30
  162. package/lib/module/MaskedViewNative.js.map +0 -1
  163. package/lib/module/ResourceSavingView.js +0 -57
  164. package/lib/module/ResourceSavingView.js.map +0 -1
  165. package/lib/module/assets/back-icon-mask.png +0 -0
  166. package/lib/typescript/src/Background.d.ts +0 -9
  167. package/lib/typescript/src/Background.d.ts.map +0 -1
  168. package/lib/typescript/src/MaskedView.android.d.ts +0 -2
  169. package/lib/typescript/src/MaskedView.android.d.ts.map +0 -1
  170. package/lib/typescript/src/MaskedView.d.ts +0 -11
  171. package/lib/typescript/src/MaskedView.d.ts.map +0 -1
  172. package/lib/typescript/src/MaskedView.ios.d.ts +0 -2
  173. package/lib/typescript/src/MaskedView.ios.d.ts.map +0 -1
  174. package/lib/typescript/src/MaskedViewNative.d.ts +0 -11
  175. package/lib/typescript/src/MaskedViewNative.d.ts.map +0 -1
  176. package/lib/typescript/src/ResourceSavingView.d.ts +0 -10
  177. package/lib/typescript/src/ResourceSavingView.d.ts.map +0 -1
  178. package/src/Background.tsx +0 -24
  179. package/src/MaskedView.android.tsx +0 -1
  180. package/src/MaskedView.ios.tsx +0 -1
  181. package/src/MaskedView.tsx +0 -13
  182. package/src/MaskedViewNative.tsx +0 -33
  183. package/src/ResourceSavingView.tsx +0 -76
  184. package/src/assets/back-icon-mask.png +0 -0
@@ -1,9 +1,8 @@
1
1
  import { useNavigation, useTheme } from '@react-navigation/native';
2
- import Color from 'color';
3
2
  import * as React from 'react';
4
3
  import {
5
4
  Animated,
6
- type LayoutChangeEvent,
5
+ Easing,
7
6
  Platform,
8
7
  StyleSheet,
9
8
  View,
@@ -12,20 +11,21 @@ import {
12
11
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
13
12
 
14
13
  import searchIcon from '../assets/search-icon.png';
15
- import type { HeaderOptions, Layout } from '../types';
14
+ import { Color } from '../Color';
15
+ import { isLiquidGlassSupported } from '../LiquidGlassView';
16
+ import { PlatformColor } from '../PlatformColor';
17
+ import type { HeaderOptions } from '../types';
16
18
  import { useFrameSize } from '../useFrameSize';
17
19
  import { getDefaultHeaderHeight } from './getDefaultHeaderHeight';
18
20
  import { HeaderBackButton } from './HeaderBackButton';
19
21
  import { HeaderBackground } from './HeaderBackground';
20
- import { HeaderButton } from './HeaderButton';
22
+ import { BUTTON_SIZE, BUTTON_SPACING, HeaderButton } from './HeaderButton';
23
+ import { HeaderButtonBackground } from './HeaderButtonBackground';
21
24
  import { HeaderIcon } from './HeaderIcon';
22
25
  import { HeaderSearchBar } from './HeaderSearchBar';
23
26
  import { HeaderShownContext } from './HeaderShownContext';
24
27
  import { HeaderTitle } from './HeaderTitle';
25
28
 
26
- // Width of the screen in split layout on portrait mode on iPad Mini
27
- const IPAD_MINI_MEDIUM_WIDTH = 414;
28
-
29
29
  type Props = HeaderOptions & {
30
30
  /**
31
31
  * Options for the back button.
@@ -44,16 +44,18 @@ type Props = HeaderOptions & {
44
44
  * Whether the header is in a modal
45
45
  */
46
46
  modal?: boolean;
47
- /**
48
- * Layout of the screen.
49
- */
50
- layout?: Layout;
51
47
  /**
52
48
  * Title text for the header.
53
49
  */
54
50
  title: string;
55
51
  };
56
52
 
53
+ const STATUS_BAR_OFFSET = Platform.select({
54
+ // The top inset on iOS is a bit less than the status bar height
55
+ ios: -7,
56
+ default: 0,
57
+ });
58
+
57
59
  const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
58
60
  Object.keys(styles).forEach((styleProp) => {
59
61
  const value = styles[styleProp];
@@ -70,54 +72,41 @@ const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
70
72
  });
71
73
  };
72
74
 
75
+ const useNativeDriver = Platform.OS !== 'web';
76
+
73
77
  export function Header(props: Props) {
74
78
  const insets = useSafeAreaInsets();
75
- const frame = useFrameSize((size) => size, true);
76
79
  const { colors } = useTheme();
77
80
 
78
81
  const navigation = useNavigation();
79
82
  const isParentHeaderShown = React.useContext(HeaderShownContext);
80
83
 
81
84
  const [searchBarVisible, setSearchBarVisible] = React.useState(false);
82
- const [titleLayout, setTitleLayout] = React.useState<Layout | undefined>(
83
- undefined
84
- );
85
-
86
- const onTitleLayout = (e: LayoutChangeEvent) => {
87
- const { height, width } = e.nativeEvent.layout;
88
-
89
- setTitleLayout((titleLayout) => {
90
- if (
91
- titleLayout &&
92
- height === titleLayout.height &&
93
- width === titleLayout.width
94
- ) {
95
- return titleLayout;
96
- }
97
-
98
- return { height, width };
99
- });
100
- };
101
85
 
102
86
  const {
103
- layout = frame,
104
87
  modal = false,
105
88
  back,
106
89
  title,
107
90
  headerTitle: customTitle,
108
91
  headerTitleAlign = Platform.OS === 'ios' ? 'center' : 'left',
109
92
  headerLeft = back ? (props) => <HeaderBackButton {...props} /> : undefined,
93
+ headerLeftBackgroundVisible,
110
94
  headerSearchBarOptions,
111
95
  headerTransparent,
112
96
  headerTintColor,
113
97
  headerBackground,
98
+ headerBlurEffect,
114
99
  headerRight,
100
+ headerRightBackgroundVisible,
115
101
  headerTitleAllowFontScaling: titleAllowFontScaling,
116
102
  headerTitleStyle: titleStyle,
117
103
  headerLeftContainerStyle: leftContainerStyle,
118
104
  headerRightContainerStyle: rightContainerStyle,
119
105
  headerTitleContainerStyle: titleContainerStyle,
120
- headerBackButtonDisplayMode = Platform.OS === 'ios' ? 'default' : 'minimal',
106
+ headerBackButtonDisplayMode = Platform.OS !== 'ios' ||
107
+ isLiquidGlassSupported
108
+ ? 'minimal'
109
+ : 'default',
121
110
  headerBackTitleStyle,
122
111
  headerBackgroundContainerStyle: backgroundContainerStyle,
123
112
  headerStyle: customHeaderStyle,
@@ -127,10 +116,12 @@ export function Header(props: Props) {
127
116
  headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
128
117
  } = props;
129
118
 
130
- const defaultHeight = getDefaultHeaderHeight(
131
- layout,
132
- modal,
133
- headerStatusBarHeight
119
+ const defaultHeight = useFrameSize((frame) =>
120
+ getDefaultHeaderHeight({
121
+ landscape: frame.width > frame.height,
122
+ modalPresentation: modal,
123
+ topInset: headerStatusBarHeight,
124
+ })
134
125
  );
135
126
 
136
127
  const {
@@ -271,7 +262,10 @@ export function Header(props: Props) {
271
262
  const iconTintColor =
272
263
  headerTintColor ??
273
264
  Platform.select({
274
- ios: colors.primary,
265
+ ios:
266
+ isLiquidGlassSupported && PlatformColor
267
+ ? PlatformColor('label')
268
+ : colors.primary,
275
269
  default: colors.text,
276
270
  });
277
271
 
@@ -281,8 +275,6 @@ export function Header(props: Props) {
281
275
  pressColor: headerPressColor,
282
276
  pressOpacity: headerPressOpacity,
283
277
  displayMode: headerBackButtonDisplayMode,
284
- titleLayout,
285
- screenLayout: layout,
286
278
  canGoBack: Boolean(back),
287
279
  onPress: back ? navigation.goBack : undefined,
288
280
  label: back?.title,
@@ -307,166 +299,248 @@ export function Header(props: Props) {
307
299
  )
308
300
  : customTitle;
309
301
 
302
+ const buttonMinWidth =
303
+ headerTitleAlign === 'center' && (leftButton || rightButton)
304
+ ? BUTTON_SIZE
305
+ : 0;
306
+
307
+ const [searchBarRendered, setSearchBarRendered] =
308
+ React.useState(searchBarVisible);
309
+ const searchBarVisibleRef = React.useRef(searchBarVisible);
310
+ const [searchBarVisibleAnim] = React.useState(
311
+ () => new Animated.Value(searchBarVisible ? 1 : 0)
312
+ );
313
+
314
+ if (searchBarVisible && !searchBarRendered) {
315
+ setSearchBarRendered(true);
316
+ }
317
+
318
+ React.useEffect(() => {
319
+ // Avoid act warning in tests just by rendering header
320
+ if (searchBarVisible === searchBarVisibleRef.current) {
321
+ return;
322
+ }
323
+
324
+ Animated.timing(searchBarVisibleAnim, {
325
+ toValue: searchBarVisible ? 1 : 0,
326
+ duration: 150,
327
+ useNativeDriver,
328
+ easing: Easing.in(Easing.linear),
329
+ }).start(({ finished }) => {
330
+ if (finished) {
331
+ setSearchBarRendered(searchBarVisible);
332
+ searchBarVisibleRef.current = searchBarVisible;
333
+ }
334
+ });
335
+
336
+ return () => {
337
+ searchBarVisibleAnim.stopAnimation();
338
+ };
339
+ }, [searchBarVisible, searchBarVisibleAnim]);
340
+
341
+ const headerOpacity = searchBarVisibleAnim.interpolate({
342
+ inputRange: [0, 1],
343
+ outputRange: [1, 0],
344
+ });
345
+
346
+ const searchBarOpacity = searchBarVisibleAnim.interpolate({
347
+ inputRange: [0, 1],
348
+ outputRange: [
349
+ // FIXME: Liquid glass views don't work properly with `opacity: 0`
350
+ // So we use a small value instead to workaround this issue.
351
+ 0.1, 1,
352
+ ],
353
+ });
354
+
355
+ const statusBarSpacing = Math.max(
356
+ headerStatusBarHeight + STATUS_BAR_OFFSET,
357
+ 0
358
+ );
359
+
310
360
  return (
311
361
  <Animated.View
312
- pointerEvents="box-none"
313
- style={[{ height, minHeight, maxHeight, opacity, transform }]}
362
+ style={[
363
+ {
364
+ pointerEvents: 'box-none',
365
+ height,
366
+ minHeight,
367
+ maxHeight,
368
+ opacity,
369
+ transform,
370
+ },
371
+ ]}
314
372
  >
315
- <Animated.View
316
- pointerEvents="box-none"
317
- style={[StyleSheet.absoluteFill, backgroundContainerStyle]}
318
- >
373
+ <Animated.View style={[styles.background, backgroundContainerStyle]}>
319
374
  {headerBackground ? (
320
375
  headerBackground({ style: backgroundStyle })
321
376
  ) : (
322
377
  <HeaderBackground
323
- pointerEvents={
324
- // Allow touch through the header when background color is transparent
325
- headerTransparent &&
326
- (backgroundStyle.backgroundColor === 'transparent' ||
327
- Color(backgroundStyle.backgroundColor).alpha() === 0)
328
- ? 'none'
329
- : 'auto'
330
- }
331
- style={backgroundStyle}
378
+ blurEffect={headerBlurEffect}
379
+ style={[
380
+ {
381
+ // Allow touch through the header when background color is transparent
382
+ pointerEvents:
383
+ headerTransparent &&
384
+ backgroundStyle.backgroundColor &&
385
+ (backgroundStyle.backgroundColor === 'transparent' ||
386
+ Color(backgroundStyle.backgroundColor)?.alpha() === 0)
387
+ ? 'none'
388
+ : 'auto',
389
+ },
390
+ backgroundStyle,
391
+ ]}
332
392
  />
333
393
  )}
334
394
  </Animated.View>
335
- <View pointerEvents="none" style={{ height: headerStatusBarHeight }} />
336
- <View
337
- pointerEvents="box-none"
395
+ <Animated.View
338
396
  style={[
339
397
  styles.content,
340
- Platform.OS === 'ios' && frame.width >= IPAD_MINI_MEDIUM_WIDTH
341
- ? styles.large
342
- : null,
398
+ {
399
+ pointerEvents: searchBarVisible ? 'none' : 'auto',
400
+ marginTop: statusBarSpacing,
401
+ opacity: headerOpacity,
402
+ },
343
403
  ]}
344
404
  >
345
- <Animated.View
346
- pointerEvents="box-none"
405
+ <View
347
406
  style={[
348
407
  styles.start,
349
- !searchBarVisible && headerTitleAlign === 'center' && styles.expand,
350
- { marginStart: insets.left },
351
- leftContainerStyle,
408
+ headerTitleAlign === 'center' ? styles.expand : styles.shrink,
409
+ {
410
+ minWidth: buttonMinWidth,
411
+ marginStart: insets.left,
412
+ },
352
413
  ]}
353
414
  >
354
- {leftButton}
415
+ <HeaderButtonBackground
416
+ plain={headerLeftBackgroundVisible === false}
417
+ style={[styles.buttonContainer, leftContainerStyle]}
418
+ >
419
+ {leftButton}
420
+ </HeaderButtonBackground>
421
+ </View>
422
+ <Animated.View
423
+ style={[
424
+ styles.title,
425
+ !leftButton && styles.titleStart,
426
+ titleContainerStyle,
427
+ ]}
428
+ >
429
+ {headerTitle({
430
+ children: title,
431
+ allowFontScaling: titleAllowFontScaling,
432
+ tintColor: headerTintColor,
433
+ style: [styles.titleText, titleStyle],
434
+ })}
355
435
  </Animated.View>
356
- {Platform.OS === 'ios' || !searchBarVisible ? (
357
- <>
358
- <Animated.View
359
- pointerEvents="box-none"
360
- style={[
361
- styles.title,
362
- {
363
- // Avoid the title from going offscreen or overlapping buttons
364
- maxWidth:
365
- headerTitleAlign === 'center'
366
- ? layout.width -
367
- ((leftButton
368
- ? headerBackButtonDisplayMode !== 'minimal'
369
- ? 80
370
- : 32
371
- : 16) +
372
- (rightButton || headerSearchBarOptions ? 16 : 0) +
373
- Math.max(insets.left, insets.right)) *
374
- 2
375
- : layout.width -
376
- ((leftButton ? 52 : 16) +
377
- (rightButton || headerSearchBarOptions ? 52 : 16) +
378
- insets.left -
379
- insets.right),
380
- },
381
- headerTitleAlign === 'left' && leftButton
382
- ? { marginStart: 4 }
383
- : { marginHorizontal: 16 },
384
- titleContainerStyle,
385
- ]}
386
- >
387
- {headerTitle({
388
- children: title,
389
- allowFontScaling: titleAllowFontScaling,
390
- tintColor: headerTintColor,
391
- onLayout: onTitleLayout,
392
- style: titleStyle,
393
- })}
394
- </Animated.View>
395
- <Animated.View
396
- pointerEvents="box-none"
397
- style={[
398
- styles.end,
399
- styles.expand,
400
- { marginEnd: insets.right },
401
- rightContainerStyle,
402
- ]}
403
- >
404
- {rightButton}
405
- {headerSearchBarOptions ? (
406
- <HeaderButton
407
- tintColor={iconTintColor}
408
- pressColor={headerPressColor}
409
- pressOpacity={headerPressOpacity}
410
- onPress={() => {
411
- setSearchBarVisible(true);
412
- headerSearchBarOptions?.onOpen?.();
413
- }}
414
- >
415
- <HeaderIcon source={searchIcon} tintColor={iconTintColor} />
416
- </HeaderButton>
417
- ) : null}
418
- </Animated.View>
419
- </>
420
- ) : null}
421
- {Platform.OS === 'ios' || searchBarVisible ? (
422
- <HeaderSearchBar
423
- {...headerSearchBarOptions}
424
- visible={searchBarVisible}
425
- onClose={() => {
426
- setSearchBarVisible(false);
427
- headerSearchBarOptions?.onClose?.();
428
- }}
429
- tintColor={headerTintColor}
430
- style={[
431
- Platform.OS === 'ios'
432
- ? [
433
- StyleSheet.absoluteFill,
434
- { paddingTop: headerStatusBarHeight ? 0 : 4 },
435
- { backgroundColor: backgroundColor ?? colors.card },
436
- ]
437
- : !leftButton && { marginStart: 8 },
438
- ]}
439
- />
440
- ) : null}
441
- </View>
436
+ <View
437
+ style={[
438
+ styles.end,
439
+ styles.expand,
440
+ {
441
+ minWidth: buttonMinWidth,
442
+ marginEnd: insets.right,
443
+ },
444
+ ]}
445
+ >
446
+ <HeaderButtonBackground
447
+ plain={headerRightBackgroundVisible === false}
448
+ style={[styles.buttonContainer, rightContainerStyle]}
449
+ >
450
+ {rightButton}
451
+ {headerSearchBarOptions ? (
452
+ <HeaderButton
453
+ tintColor={iconTintColor}
454
+ pressColor={headerPressColor}
455
+ pressOpacity={headerPressOpacity}
456
+ onPress={() => {
457
+ setSearchBarVisible(true);
458
+ headerSearchBarOptions?.onOpen?.();
459
+ }}
460
+ >
461
+ <HeaderIcon source={searchIcon} tintColor={iconTintColor} />
462
+ </HeaderButton>
463
+ ) : null}
464
+ </HeaderButtonBackground>
465
+ </View>
466
+ </Animated.View>
467
+ {searchBarRendered ? (
468
+ <HeaderSearchBar
469
+ {...headerSearchBarOptions}
470
+ statusBarHeight={statusBarSpacing}
471
+ visible={searchBarVisible}
472
+ onClose={() => {
473
+ setSearchBarVisible(false);
474
+ headerSearchBarOptions?.onClose?.();
475
+ }}
476
+ tintColor={headerTintColor}
477
+ style={[StyleSheet.absoluteFill, { opacity: searchBarOpacity }]}
478
+ />
479
+ ) : null}
442
480
  </Animated.View>
443
481
  );
444
482
  }
445
483
 
484
+ const BUTTON_OFFSET = Platform.OS === 'ios' ? 10 : 4;
485
+ const TITLE_START_OFFSET =
486
+ Platform.OS === 'ios'
487
+ ? 0
488
+ : // Since button container is always present,
489
+ // We need to account for its horizontal margin as well
490
+ 16 - BUTTON_OFFSET * 2;
491
+
446
492
  const styles = StyleSheet.create({
447
493
  content: {
494
+ pointerEvents: 'box-none',
448
495
  flex: 1,
449
496
  flexDirection: 'row',
450
497
  alignItems: 'stretch',
451
498
  },
452
- large: {
453
- marginHorizontal: 5,
454
- },
455
499
  title: {
500
+ flexShrink: 1,
501
+ minWidth: 0,
456
502
  justifyContent: 'center',
503
+ pointerEvents: 'box-none',
504
+ // Make sure title goes below liquid glass buttons
505
+ zIndex: -1,
506
+ },
507
+ titleStart: {
508
+ marginLeft: TITLE_START_OFFSET,
509
+ },
510
+ titleText: {
511
+ textAlign: 'center',
512
+ },
513
+ buttonContainer: {
514
+ flexDirection: 'row',
515
+ pointerEvents: 'box-none',
516
+ gap: BUTTON_SPACING,
517
+ marginHorizontal: BUTTON_OFFSET,
457
518
  },
458
519
  start: {
459
520
  flexDirection: 'row',
460
521
  alignItems: 'center',
461
522
  justifyContent: 'flex-start',
523
+ pointerEvents: 'box-none',
462
524
  },
463
525
  end: {
464
526
  flexDirection: 'row',
465
527
  alignItems: 'center',
466
528
  justifyContent: 'flex-end',
529
+ pointerEvents: 'box-none',
467
530
  },
468
531
  expand: {
469
532
  flexGrow: 1,
533
+ flexShrink: 1,
470
534
  flexBasis: 0,
471
535
  },
536
+ shrink: {
537
+ flexGrow: 0,
538
+ flexShrink: 1,
539
+ minWidth: 0,
540
+ maxWidth: '50%',
541
+ },
542
+ background: {
543
+ ...StyleSheet.absoluteFillObject,
544
+ pointerEvents: 'box-none',
545
+ },
472
546
  });