@react-navigation/elements 2.9.3 → 3.0.0-alpha.0

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 +152 -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 +27 -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 +5 -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 +20 -22
  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 +226 -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 +29 -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 +21 -21
  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,21 +1,25 @@
1
- import { useLocale, useTheme } from '@react-navigation/native';
1
+ import { useTheme } from '@react-navigation/native';
2
2
  import * as React from 'react';
3
3
  import {
4
4
  Animated,
5
- Image,
5
+ type ColorValue,
6
6
  Platform,
7
7
  type StyleProp,
8
8
  StyleSheet,
9
+ // eslint-disable-next-line no-restricted-imports
10
+ type Text,
9
11
  type TextStyle,
10
12
  View,
11
13
  } from 'react-native';
12
14
 
13
15
  import backIcon from '../assets/back-icon.png';
14
- import backIconMask from '../assets/back-icon-mask.png';
15
- import { MaskedView } from '../MaskedView';
16
- import type { HeaderBackButtonProps } from '../types';
17
- import { HeaderButton } from './HeaderButton';
18
- import { HeaderIcon, ICON_MARGIN } from './HeaderIcon';
16
+ import { isLiquidGlassSupported } from '../LiquidGlassView';
17
+ import type {
18
+ HeaderBackButtonDisplayMode,
19
+ HeaderBackButtonProps,
20
+ } from '../types';
21
+ import { BUTTON_SIZE, HeaderButton } from './HeaderButton';
22
+ import { HeaderIcon } from './HeaderIcon';
19
23
 
20
24
  export function HeaderBackButton({
21
25
  disabled,
@@ -23,27 +27,25 @@ export function HeaderBackButton({
23
27
  backImage,
24
28
  label,
25
29
  labelStyle,
26
- displayMode = Platform.OS === 'ios' ? 'default' : 'minimal',
27
- onLabelLayout,
30
+ displayMode = 'minimal',
28
31
  onPress,
29
32
  pressColor,
30
33
  pressOpacity,
31
- screenLayout,
32
34
  tintColor,
33
- titleLayout,
34
35
  truncatedLabel = 'Back',
35
36
  accessibilityLabel = label && label !== 'Back' ? `${label}, back` : 'Go back',
36
37
  testID,
37
38
  style,
38
39
  href,
39
40
  }: HeaderBackButtonProps) {
40
- const { colors, fonts } = useTheme();
41
- const { direction } = useLocale();
41
+ const [measuredMinimal, setMeasuredMinimal] = React.useReducer(
42
+ () => true,
43
+ false
44
+ );
42
45
 
43
- const [labelWidth, setLabelWidth] = React.useState<number | null>(null);
44
- const [truncatedLabelWidth, setTruncatedLabelWidth] = React.useState<
45
- number | null
46
- >(null);
46
+ const { colors } = useTheme();
47
+
48
+ const isMinimal = displayMode === 'minimal' || measuredMinimal;
47
49
 
48
50
  const renderBackImage = () => {
49
51
  if (backImage) {
@@ -52,118 +54,13 @@ export function HeaderBackButton({
52
54
  return (
53
55
  <HeaderIcon
54
56
  source={backIcon}
55
- tintColor={tintColor}
56
- style={[
57
- styles.icon,
58
- displayMode !== 'minimal' && styles.iconWithLabel,
59
- ]}
57
+ tintColor={tintColor ?? colors.text}
58
+ style={styles.icon}
60
59
  />
61
60
  );
62
61
  }
63
62
  };
64
63
 
65
- const renderLabel = () => {
66
- if (displayMode === 'minimal') {
67
- return null;
68
- }
69
-
70
- const availableSpace =
71
- titleLayout && screenLayout
72
- ? (screenLayout.width - titleLayout.width) / 2 -
73
- (ICON_WIDTH + ICON_MARGIN)
74
- : null;
75
-
76
- const potentialLabelText =
77
- displayMode === 'default' ? label : truncatedLabel;
78
- const finalLabelText =
79
- availableSpace && labelWidth && truncatedLabelWidth
80
- ? availableSpace > labelWidth
81
- ? potentialLabelText
82
- : availableSpace > truncatedLabelWidth
83
- ? truncatedLabel
84
- : null
85
- : potentialLabelText;
86
-
87
- const commonStyle: Animated.WithAnimatedValue<StyleProp<TextStyle>> = [
88
- fonts.regular,
89
- styles.label,
90
- labelStyle,
91
- ];
92
-
93
- const hiddenStyle: Animated.WithAnimatedValue<StyleProp<TextStyle>> = [
94
- commonStyle,
95
- {
96
- position: 'absolute',
97
- top: 0,
98
- left: 0,
99
- opacity: 0,
100
- },
101
- ];
102
-
103
- const labelElement = (
104
- <View style={styles.labelWrapper}>
105
- {label && displayMode === 'default' ? (
106
- <Animated.Text
107
- style={hiddenStyle}
108
- numberOfLines={1}
109
- onLayout={(e) => setLabelWidth(e.nativeEvent.layout.width)}
110
- >
111
- {label}
112
- </Animated.Text>
113
- ) : null}
114
- {truncatedLabel ? (
115
- <Animated.Text
116
- style={hiddenStyle}
117
- numberOfLines={1}
118
- onLayout={(e) => setTruncatedLabelWidth(e.nativeEvent.layout.width)}
119
- >
120
- {truncatedLabel}
121
- </Animated.Text>
122
- ) : null}
123
- {finalLabelText ? (
124
- <Animated.Text
125
- accessible={false}
126
- onLayout={onLabelLayout}
127
- style={[tintColor ? { color: tintColor } : null, commonStyle]}
128
- numberOfLines={1}
129
- allowFontScaling={!!allowFontScaling}
130
- >
131
- {finalLabelText}
132
- </Animated.Text>
133
- ) : null}
134
- </View>
135
- );
136
-
137
- if (backImage || Platform.OS !== 'ios') {
138
- // When a custom backimage is specified, we can't mask the label
139
- // Otherwise there might be weird effect due to our mask not being the same as the image
140
- return labelElement;
141
- }
142
-
143
- return (
144
- <MaskedView
145
- maskElement={
146
- <View
147
- style={[
148
- styles.iconMaskContainer,
149
- // Extend the mask to the center of the screen so that label isn't clipped during animation
150
- screenLayout ? { minWidth: screenLayout.width / 2 - 27 } : null,
151
- ]}
152
- >
153
- <Image
154
- source={backIconMask}
155
- resizeMode="contain"
156
- style={[styles.iconMask, direction === 'rtl' && styles.flip]}
157
- />
158
- <View style={styles.iconMaskFillerRect} />
159
- </View>
160
- }
161
- >
162
- {labelElement}
163
- </MaskedView>
164
- );
165
- };
166
-
167
64
  const handlePress = () => {
168
65
  if (onPress) {
169
66
  requestAnimationFrame(() => onPress());
@@ -179,71 +76,200 @@ export function HeaderBackButton({
179
76
  onPress={handlePress}
180
77
  pressColor={pressColor}
181
78
  pressOpacity={pressOpacity}
182
- style={[styles.container, style]}
79
+ style={[styles.container, isMinimal && styles.containerMinimal, style]}
183
80
  >
184
81
  <React.Fragment>
185
82
  {renderBackImage()}
186
- {renderLabel()}
83
+ {!isMinimal ? (
84
+ <HeaderBackLabel
85
+ allowFontScaling={allowFontScaling}
86
+ displayMode={displayMode}
87
+ label={label}
88
+ labelStyle={labelStyle}
89
+ tintColor={tintColor}
90
+ truncatedLabel={truncatedLabel}
91
+ onMeasureMinimal={setMeasuredMinimal}
92
+ />
93
+ ) : null}
187
94
  </React.Fragment>
188
95
  </HeaderButton>
189
96
  );
190
97
  }
191
98
 
99
+ function HeaderBackLabel({
100
+ allowFontScaling,
101
+ displayMode,
102
+ label,
103
+ labelStyle,
104
+ tintColor,
105
+ truncatedLabel,
106
+ onMeasureMinimal,
107
+ }: {
108
+ allowFontScaling?: boolean;
109
+ displayMode: HeaderBackButtonDisplayMode;
110
+ label: string | undefined;
111
+ labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
112
+ tintColor?: ColorValue;
113
+ truncatedLabel: string | undefined;
114
+ onMeasureMinimal: () => void;
115
+ }) {
116
+ const { fonts } = useTheme();
117
+
118
+ const [wrapperWidth, setWrapperWidth] = React.useState<number | null>(null);
119
+ const [labelWidth, setLabelWidth] = React.useState<number | null>(null);
120
+ const [truncatedLabelWidth, setTruncatedLabelWidth] = React.useState<
121
+ number | null
122
+ >(null);
123
+
124
+ const wrapperRef = React.useRef<View | null>(null);
125
+ const labelRef = React.useRef<Text | null>(null);
126
+ const truncatedLabelRef = React.useRef<Text | null>(null);
127
+
128
+ React.useLayoutEffect(() => {
129
+ wrapperRef.current?.measure((_x, _y, width) => {
130
+ setWrapperWidth(width);
131
+ });
132
+
133
+ labelRef.current?.measure((_x, _y, width) => {
134
+ setLabelWidth(width);
135
+ });
136
+
137
+ truncatedLabelRef.current?.measure((_x, _y, width) => {
138
+ setTruncatedLabelWidth(width);
139
+ });
140
+ }, []);
141
+
142
+ const availableSpace = wrapperWidth;
143
+
144
+ const potentialLabelText = displayMode === 'default' ? label : truncatedLabel;
145
+ const hasMeasured =
146
+ availableSpace !== null && labelWidth !== null && truncatedLabel
147
+ ? truncatedLabelWidth !== null
148
+ : true;
149
+
150
+ const finalLabelText =
151
+ availableSpace && labelWidth
152
+ ? availableSpace >= labelWidth
153
+ ? potentialLabelText
154
+ : truncatedLabelWidth
155
+ ? availableSpace >= truncatedLabelWidth
156
+ ? truncatedLabel
157
+ : null
158
+ : null
159
+ : potentialLabelText;
160
+
161
+ const isMinimal = hasMeasured && finalLabelText === null;
162
+
163
+ React.useLayoutEffect(() => {
164
+ if (isMinimal) {
165
+ onMeasureMinimal();
166
+ }
167
+ }, [isMinimal, onMeasureMinimal]);
168
+
169
+ const commonStyle: Animated.WithAnimatedValue<StyleProp<TextStyle>> = [
170
+ fonts.regular,
171
+ styles.label,
172
+ labelStyle,
173
+ ];
174
+
175
+ const hiddenStyle: Animated.WithAnimatedValue<StyleProp<TextStyle>> = [
176
+ commonStyle,
177
+ {
178
+ position: 'absolute',
179
+ top: 0,
180
+ left: 0,
181
+ opacity: 0,
182
+ },
183
+ ];
184
+
185
+ return (
186
+ <View
187
+ ref={wrapperRef}
188
+ onLayout={(e) => {
189
+ setWrapperWidth(e.nativeEvent.layout.width);
190
+ }}
191
+ style={styles.labelWrapper}
192
+ >
193
+ {label && displayMode === 'default' ? (
194
+ <Animated.Text
195
+ ref={labelRef}
196
+ onLayout={(e) => setLabelWidth(e.nativeEvent.layout.width)}
197
+ aria-hidden={true}
198
+ style={hiddenStyle}
199
+ numberOfLines={1}
200
+ allowFontScaling={allowFontScaling}
201
+ >
202
+ {label}
203
+ </Animated.Text>
204
+ ) : null}
205
+ {truncatedLabel ? (
206
+ <Animated.Text
207
+ ref={truncatedLabelRef}
208
+ onLayout={(e) => setTruncatedLabelWidth(e.nativeEvent.layout.width)}
209
+ aria-hidden={true}
210
+ style={hiddenStyle}
211
+ numberOfLines={1}
212
+ allowFontScaling={allowFontScaling}
213
+ >
214
+ {truncatedLabel}
215
+ </Animated.Text>
216
+ ) : null}
217
+ {finalLabelText ? (
218
+ <Animated.Text
219
+ accessible={false}
220
+ style={[tintColor ? { color: tintColor } : null, commonStyle]}
221
+ numberOfLines={1}
222
+ allowFontScaling={allowFontScaling}
223
+ >
224
+ {finalLabelText}
225
+ </Animated.Text>
226
+ ) : null}
227
+ </View>
228
+ );
229
+ }
230
+
231
+ // iOS uses a smaller chevron, Android uses a larger arrow
192
232
  const ICON_WIDTH = Platform.OS === 'ios' ? 13 : 24;
193
- const ICON_MARGIN_END = Platform.OS === 'ios' ? 22 : 3;
233
+ 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
236
+
237
+ // Standard distance between chevron and label
238
+ const ICON_LABEL_SPACING = 9;
239
+
240
+ const LABEL_FONT_SIZE = 17;
241
+ const LABEL_LETTER_SPACING = 0.35;
194
242
 
195
243
  const styles = StyleSheet.create({
196
244
  container: {
245
+ borderRadius: BUTTON_SIZE / 2,
246
+ flexShrink: 1,
197
247
  paddingHorizontal: 0,
198
- minWidth: StyleSheet.hairlineWidth, // Avoid collapsing when title is long
199
- ...Platform.select({
200
- ios: null,
201
- default: {
202
- marginVertical: 3,
203
- marginHorizontal: 11,
204
- },
205
- }),
248
+ minWidth: StyleSheet.hairlineWidth, // Avoid collapsing when title is long,
249
+ },
250
+ containerMinimal: {
251
+ minWidth: BUTTON_SIZE,
252
+ minHeight: BUTTON_SIZE,
253
+ aspectRatio: 1,
254
+ alignItems: 'center',
255
+ justifyContent: Platform.OS === 'ios' ? 'flex-start' : 'center',
206
256
  },
207
257
  label: {
208
- fontSize: 17,
209
- // Title and back label are a bit different width due to title being bold
210
- // Adjusting the letterSpacing makes them coincide better
211
- letterSpacing: 0.35,
258
+ fontSize: LABEL_FONT_SIZE,
259
+ letterSpacing: LABEL_LETTER_SPACING,
212
260
  },
213
261
  labelWrapper: {
214
- // These styles will make sure that the label doesn't fill the available space
215
- // Otherwise it messes with the measurement of the label
262
+ // Make sure that the label doesn't fill the available space
216
263
  flexDirection: 'row',
217
264
  alignItems: 'flex-start',
218
- marginEnd: ICON_MARGIN,
265
+ flexGrow: 1,
266
+ flexShrink: 1,
267
+ flexBasis: 0,
268
+ marginStart: ICON_LABEL_SPACING,
219
269
  },
220
270
  icon: {
221
271
  width: ICON_WIDTH,
222
- marginEnd: ICON_MARGIN_END,
223
- },
224
- iconWithLabel:
225
- Platform.OS === 'ios'
226
- ? {
227
- marginEnd: 6,
228
- }
229
- : {},
230
- iconMaskContainer: {
231
- flex: 1,
232
- flexDirection: 'row',
233
- justifyContent: 'center',
234
- },
235
- iconMaskFillerRect: {
236
- flex: 1,
237
- backgroundColor: '#000',
238
- },
239
- iconMask: {
240
- height: 21,
241
- width: 13,
242
- marginStart: -14.5,
243
- marginVertical: 12,
244
- alignSelf: 'center',
245
- },
246
- flip: {
247
- transform: 'scaleX(-1)',
272
+ marginStart: ICON_SPACING_START,
273
+ marginEnd: 0,
248
274
  },
249
275
  });
@@ -1,7 +1,6 @@
1
1
  import { useTheme } from '@react-navigation/native';
2
2
  import * as React from 'react';
3
3
  import {
4
- Animated,
5
4
  Platform,
6
5
  type StyleProp,
7
6
  StyleSheet,
@@ -9,31 +8,38 @@ import {
9
8
  type ViewStyle,
10
9
  } from 'react-native';
11
10
 
11
+ import { BlurEffectBackground } from '../BlurEffectBackground';
12
+ import { type BlurEffectType } from '../getBlurBackgroundColor';
13
+
12
14
  type Props = Omit<ViewProps, 'style'> & {
13
- style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
15
+ blurEffect?: BlurEffectType | 'none';
16
+ style?: StyleProp<ViewStyle>;
14
17
  children?: React.ReactNode;
15
18
  };
16
19
 
17
- export function HeaderBackground({ style, ...rest }: Props) {
18
- const { colors, dark } = useTheme();
20
+ export function HeaderBackground({
21
+ blurEffect,
22
+ style,
23
+ children,
24
+ ...rest
25
+ }: Props) {
26
+ const { colors } = useTheme();
19
27
 
20
28
  return (
21
- <Animated.View
29
+ <BlurEffectBackground
30
+ blurEffect={blurEffect}
22
31
  style={[
23
32
  styles.container,
24
33
  {
25
34
  backgroundColor: colors.card,
26
35
  borderBottomColor: colors.border,
27
- ...(Platform.OS === 'ios' && {
28
- shadowColor: dark
29
- ? 'rgba(255, 255, 255, 0.45)'
30
- : 'rgba(0, 0, 0, 1)',
31
- }),
32
36
  },
33
37
  style,
34
38
  ]}
35
39
  {...rest}
36
- />
40
+ >
41
+ {children}
42
+ </BlurEffectBackground>
37
43
  );
38
44
  }
39
45
 
@@ -44,14 +50,6 @@ const styles = StyleSheet.create({
44
50
  android: {
45
51
  elevation: 4,
46
52
  },
47
- ios: {
48
- shadowOpacity: 0.3,
49
- shadowRadius: 0,
50
- shadowOffset: {
51
- width: 0,
52
- height: StyleSheet.hairlineWidth,
53
- },
54
- },
55
53
  default: {
56
54
  borderBottomWidth: StyleSheet.hairlineWidth,
57
55
  },
@@ -44,6 +44,9 @@ export const HeaderButton = React.forwardRef(HeaderButtonInternal);
44
44
 
45
45
  HeaderButton.displayName = 'HeaderButton';
46
46
 
47
+ export const BUTTON_SIZE = Platform.OS === 'ios' ? 44 : 48;
48
+ export const BUTTON_SPACING = Platform.OS === 'ios' ? 10 : 8;
49
+
47
50
  const androidRipple = {
48
51
  borderless: true,
49
52
  foreground: Platform.OS === 'android' && Platform.Version >= 23,
@@ -54,9 +57,11 @@ const styles = StyleSheet.create({
54
57
  container: {
55
58
  flexDirection: 'row',
56
59
  alignItems: 'center',
57
- paddingHorizontal: 8,
60
+ justifyContent: 'center',
61
+ minHeight: BUTTON_SIZE,
62
+ minWidth: BUTTON_SIZE,
58
63
  // Roundness for iPad hover effect
59
- borderRadius: 10,
64
+ borderRadius: Platform.OS === 'ios' ? BUTTON_SIZE / 2 : 10,
60
65
  borderCurve: 'continuous',
61
66
  },
62
67
  disabled: {
@@ -0,0 +1,29 @@
1
+ import { Animated, StyleSheet } from 'react-native';
2
+
3
+ import { LiquidGlassView } from '../LiquidGlassView';
4
+ import { BUTTON_SIZE } from './HeaderButton';
5
+
6
+ type Props = React.ComponentProps<typeof Animated.View>;
7
+
8
+ const AnimatedLiquidGlassView =
9
+ Animated.createAnimatedComponent(LiquidGlassView);
10
+
11
+ export function HeaderButtonBackground({ style, ...rest }: Props) {
12
+ return (
13
+ <AnimatedLiquidGlassView
14
+ interactive
15
+ style={[styles.container, style]}
16
+ {...rest}
17
+ />
18
+ );
19
+ }
20
+
21
+ const styles = StyleSheet.create({
22
+ container: {
23
+ flexDirection: 'row',
24
+ alignItems: 'center',
25
+ minHeight: BUTTON_SIZE,
26
+ borderRadius: BUTTON_SIZE / 2,
27
+ borderCurve: 'continuous',
28
+ },
29
+ });