@momo-kits/foundation 0.169.7-beta.8 → 0.169.7-beta.9

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 (36) hide show
  1. package/Application/BottomTab/ActiveIndicator.tsx +1 -5
  2. package/Application/BottomTab/BottomTabBar.styles.ts +1 -18
  3. package/Application/BottomTab/BottomTabBar.tsx +10 -8
  4. package/Application/BottomTab/CurvedContainer.tsx +2 -8
  5. package/Application/BottomTab/CustomBottomTabItem.tsx +2 -2
  6. package/Application/BottomTab/DuoTabBar.tsx +3 -15
  7. package/Application/BottomTab/metrics.ts +3 -23
  8. package/Application/Components/HeaderAnimated.tsx +13 -37
  9. package/Application/Components/HeaderBackground.tsx +3 -16
  10. package/Application/Components/HeaderExtendHeader.tsx +28 -41
  11. package/Application/Components/HeaderLeft.tsx +18 -4
  12. package/Application/Components/HeaderRight.styles.ts +13 -15
  13. package/Application/Components/HeaderTitle.shared.ts +11 -10
  14. package/Application/Components/HeaderToolkitAction.tsx +37 -12
  15. package/Application/Components/NavigationButton.tsx +6 -9
  16. package/Application/Components/SearchHeader.tsx +7 -20
  17. package/Application/StackScreen.tsx +0 -9
  18. package/Application/index.ts +1 -11
  19. package/Application/navigationOptions.tsx +15 -6
  20. package/Application/types.header.ts +2 -7
  21. package/Application/types.tabs.ts +1 -0
  22. package/Application/types.ts +0 -7
  23. package/Input/InputSearch.tsx +2 -18
  24. package/Input/index.tsx +1 -7
  25. package/Layout/Card.tsx +10 -10
  26. package/Layout/Item.tsx +2 -2
  27. package/Layout/Screen.tsx +0 -3
  28. package/Layout/Section.tsx +8 -9
  29. package/Layout/constants.ts +19 -0
  30. package/Layout/screenHeader.tsx +52 -55
  31. package/Layout/screenTypes.ts +2 -10
  32. package/Layout/types.ts +37 -1
  33. package/Layout/useGridSystem.ts +49 -0
  34. package/Layout/utils.ts +0 -8
  35. package/package.json +1 -1
  36. package/Application/headerLayout.ts +0 -158
@@ -5,7 +5,6 @@ import Animated, {
5
5
  useSharedValue,
6
6
  withTiming,
7
7
  } from 'react-native-reanimated';
8
- import { defaultTheme } from '../../Consts';
9
8
  import { ApplicationContext } from '../../Context';
10
9
  import { getIndicatorShadow, TabBarMotion } from './metrics';
11
10
 
@@ -71,10 +70,7 @@ const ActiveIndicator: React.FC<Props> = ({
71
70
  styles.indicator,
72
71
  {
73
72
  borderRadius: radius,
74
- // a mini app's own theme may lack `tonal`; without the fallback the indicator is transparent
75
- backgroundColor:
76
- theme.colors.background.tonal ??
77
- defaultTheme.colors.background.tonal,
73
+ backgroundColor: theme.colors.background.tonal,
78
74
  boxShadow: getIndicatorShadow(theme.colors.primary),
79
75
  },
80
76
  style,
@@ -2,20 +2,6 @@ import { StyleSheet } from 'react-native';
2
2
  import { Colors, Spacing } from '../../Consts';
3
3
  import { CTA_SHADOW, TabBarMetrics } from './metrics';
4
4
 
5
- const ITEM_HEIGHT =
6
- TabBarMetrics.indicatorHeight +
7
- TabBarMetrics.itemIconLabelGap +
8
- TabBarMetrics.labelHeight;
9
- const CONTENT_HEIGHT = TabBarMetrics.height - 2 * TabBarMetrics.paddingVertical;
10
- // the items are centered in the content, so their label starts this far below the content top
11
- const ITEM_LABEL_TOP =
12
- (CONTENT_HEIGHT - ITEM_HEIGHT) / 2 +
13
- TabBarMetrics.indicatorHeight +
14
- TabBarMetrics.itemIconLabelGap;
15
- // the center action column is placed so its label starts on the same line as the tab labels
16
- const CTA_TOP =
17
- ITEM_LABEL_TOP - TabBarMetrics.ctaLabelGap - TabBarMetrics.ctaSize;
18
-
19
5
  export const styles = StyleSheet.create({
20
6
  bar: {
21
7
  position: 'absolute',
@@ -51,7 +37,7 @@ export const styles = StyleSheet.create({
51
37
  position: 'absolute',
52
38
  left: 0,
53
39
  right: 0,
54
- top: CTA_TOP,
40
+ top: -TabBarMetrics.ctaLift - TabBarMetrics.paddingVertical,
55
41
  alignItems: 'center',
56
42
  gap: TabBarMetrics.ctaLabelGap,
57
43
  },
@@ -60,9 +46,6 @@ export const styles = StyleSheet.create({
60
46
  width: TabBarMetrics.ctaSize,
61
47
  height: TabBarMetrics.ctaSize,
62
48
  borderRadius: TabBarMetrics.ctaSize / 2,
63
- // Figma Icon Button border: white 60% hairline
64
- borderWidth: 1,
65
- borderColor: `rgba(255, 255, 255, ${TabBarMetrics.ctaBorderOpacity})`,
66
49
  alignItems: 'center',
67
50
  justifyContent: 'center',
68
51
  },
@@ -181,17 +181,21 @@ const HorizontalTabBar: React.FC<{
181
181
  return renderItems(items);
182
182
  };
183
183
 
184
+ const customContainer = withCta && floatingButton?.container;
185
+
184
186
  return (
185
187
  <View
186
188
  style={[styles.container, { width }]}
187
189
  accessibilityLabel={'bottom_tab_bar'}
188
190
  >
189
- <CurvedContainer
190
- width={width}
191
- height={TabBarMetrics.height}
192
- radius={TabBarMetrics.height / 2}
193
- notchCenterX={withCta ? width / 2 : undefined}
194
- />
191
+ {customContainer || (
192
+ <CurvedContainer
193
+ width={width}
194
+ height={TabBarMetrics.height}
195
+ radius={TabBarMetrics.height / 2}
196
+ notchCenterX={withCta ? width / 2 : undefined}
197
+ />
198
+ )}
195
199
  <View style={styles.content}>
196
200
  <ActiveIndicator
197
201
  axis={'x'}
@@ -224,8 +228,6 @@ export default function BottomTabBar(props: Props) {
224
228
  window.height,
225
229
  insets.left,
226
230
  insets.right,
227
- state.routes.length,
228
- withCta,
229
231
  );
230
232
 
231
233
  return (
@@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
3
3
  import Svg, { Defs, FeGaussianBlur, Filter, Path } from 'react-native-svg';
4
4
  import { Colors } from '../../Consts';
5
5
  import { ApplicationContext } from '../../Context';
6
- import { NOTCH_CURVES, NOTCH_HALF_WIDTH, TabBarMetrics } from './metrics';
6
+ import { NOTCH_CURVES, NOTCH_HALF_WIDTH } from './metrics';
7
7
 
8
8
  type Props = {
9
9
  width: number;
@@ -139,13 +139,7 @@ const CurvedContainer: React.FC<Props> = ({
139
139
  filter={'url(#keyShadow)'}
140
140
  />
141
141
  )}
142
- <Path
143
- d={path}
144
- fill={theme.colors.background.surface}
145
- fillOpacity={TabBarMetrics.backgroundOpacity}
146
- stroke={theme.colors.border.default}
147
- strokeWidth={1}
148
- />
142
+ <Path d={path} fill={theme.colors.background.surface} />
149
143
  </Svg>
150
144
  );
151
145
  };
@@ -13,7 +13,7 @@ import Animated, {
13
13
  import { TabBarIcon } from './TabBarIcon';
14
14
  import { Text } from '../../Text';
15
15
  import { MiniAppContext } from '../../Context';
16
- import { Colors } from '../../Consts';
16
+ import { Colors, Spacing } from '../../Consts';
17
17
  import { TabBarMetrics, TabBarMotion } from './metrics';
18
18
 
19
19
  type IconRenderer = (props: {
@@ -143,7 +143,7 @@ const styles = StyleSheet.create({
143
143
  minWidth: 1,
144
144
  alignItems: 'center',
145
145
  justifyContent: 'center',
146
- gap: TabBarMetrics.itemIconLabelGap,
146
+ gap: Spacing.XXS,
147
147
  },
148
148
  slot: {
149
149
  height: TabBarMetrics.indicatorHeight,
@@ -3,11 +3,7 @@ import React from 'react';
3
3
  import { useWindowDimensions, View } from 'react-native';
4
4
  import { useScreenLayout } from '../../Layout/utils';
5
5
  import { FloatingButtonProps } from '../types';
6
- import {
7
- getSystemIndicatorHeight,
8
- hasCenterAction,
9
- HorizontalTabBar,
10
- } from './BottomTabBar';
6
+ import { getSystemIndicatorHeight, HorizontalTabBar } from './BottomTabBar';
11
7
  import { getHorizontalBarWidth, TabBarMetrics } from './metrics';
12
8
  import { getTabItems } from './tabItems';
13
9
  import { DuoRailTabBar } from './DuoRailTabBar';
@@ -38,7 +34,6 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
38
34
  const { insets } = useScreenLayout();
39
35
  const { width, height } = useWindowDimensions();
40
36
  const { floatingButton, activeTintColor, inactiveTintColor } = props;
41
- const tabItems = getTabItems(props);
42
37
 
43
38
  return (
44
39
  <View
@@ -49,15 +44,8 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
49
44
  ]}
50
45
  >
51
46
  <HorizontalTabBar
52
- items={tabItems}
53
- width={getHorizontalBarWidth(
54
- width,
55
- height,
56
- insets.left,
57
- insets.right,
58
- tabItems.length,
59
- hasCenterAction(floatingButton, tabItems.length),
60
- )}
47
+ items={getTabItems(props)}
48
+ width={getHorizontalBarWidth(width, height, insets.left, insets.right)}
61
49
  floatingButton={floatingButton}
62
50
  activeTintColor={activeTintColor}
63
51
  inactiveTintColor={inactiveTintColor}
@@ -8,18 +8,11 @@ import { Colors, Radius, Spacing } from '../../Consts';
8
8
  * iPhone Duo rail (center action under the container).
9
9
  */
10
10
  const TabBarMetrics = {
11
- height: 60,
11
+ height: 68,
12
12
  margin: Spacing.S,
13
- paddingVertical: Spacing.XS,
13
+ paddingVertical: Spacing.S,
14
14
  paddingHorizontal: Spacing.M,
15
- itemGap: 1,
16
- // gap between the icon slot and the label inside a tab (Figma: none)
17
- itemIconLabelGap: 0,
18
- // .Nav/Background: no backdrop blur, so use the Figma Blur=Off spec (opacity 95%, no blur) plus the 1px
19
- // border — nearly solid, matching the frosted sample; surface keeps dark-mode support
20
- backgroundOpacity: 0.95,
21
- // white 60% hairline around the center action button (Figma Icon Button border)
22
- ctaBorderOpacity: 0.6,
15
+ itemGap: Spacing.XS,
23
16
  iconSize: 24,
24
17
  // active indicator behind the icon: 24 icon + XS·M padding
25
18
  indicatorPaddingVertical: Spacing.XS,
@@ -32,9 +25,6 @@ const TabBarMetrics = {
32
25
  systemIndicatorHeight: 24,
33
26
  foldableMinShortSide: 600,
34
27
  foldableWidthRatio: 0.65,
35
- // a 2-tab bar doesn't stretch to the full width: each item is this fixed width and the bar is
36
- // sized to its content, centered (Figma Navigation · 2 Tab: item 100, bar ~225)
37
- twoTabItemWidth: 100,
38
28
  ctaSlotWidth: 92,
39
29
  ctaSize: 48,
40
30
  ctaIconSize: 28,
@@ -142,17 +132,7 @@ const getHorizontalBarWidth = (
142
132
  height: number,
143
133
  insetLeft: number,
144
134
  insetRight: number,
145
- itemCount: number,
146
- hasCta: boolean,
147
135
  ) => {
148
- // 2 tabs (no center action) stay content-sized and centered instead of filling the window
149
- if (itemCount === 2 && !hasCta) {
150
- return (
151
- TabBarMetrics.twoTabItemWidth * 2 +
152
- TabBarMetrics.paddingHorizontal * 2 +
153
- TabBarMetrics.itemGap
154
- );
155
- }
156
136
  const shortSide = Math.min(width, height);
157
137
  if (shortSide >= TabBarMetrics.foldableMinShortSide) {
158
138
  return shortSide * TabBarMetrics.foldableWidthRatio;
@@ -1,5 +1,4 @@
1
- import { StyleSheet, View } from 'react-native';
2
- import LinearGradient from 'react-native-linear-gradient';
1
+ import { StyleSheet } from 'react-native';
3
2
  import Animated, {
4
3
  Extrapolation,
5
4
  interpolate,
@@ -10,7 +9,6 @@ import React from 'react';
10
9
  import { Image } from '../../Image';
11
10
  import { Colors } from '../../Consts';
12
11
  import { useNormalizedSharedValue } from '../hooks';
13
- import { HeaderBarMetrics, useHeaderLayout } from '../headerLayout';
14
12
 
15
13
  const ASPECT_RATIOS: Record<
16
14
  NonNullable<HeaderAnimatedProps['aspectRatio']>,
@@ -23,19 +21,16 @@ const ASPECT_RATIOS: Record<
23
21
 
24
22
  /**
25
23
  * Parallax header image (or `children`): stretches when pulled down and fades out
26
- * as the content scrolls up. Wide windows keep it at phone size, centered.
24
+ * as the content scrolls up.
27
25
  */
28
26
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
29
27
  animatedValue,
30
28
  image,
31
29
  useScale = true,
32
30
  aspectRatio = '16/9',
33
- bannerBackgroundColor,
34
31
  children,
35
32
  }) => {
36
33
  const sharedValue = useNormalizedSharedValue(animatedValue);
37
- const { isWide, bannerWidth } = useHeaderLayout();
38
- const ratio = ASPECT_RATIOS[aspectRatio];
39
34
  const animatedStyle = useAnimatedStyle(() => {
40
35
  let scale = 1;
41
36
  if (useScale) {
@@ -64,36 +59,22 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
64
59
  };
65
60
  });
66
61
 
67
- const content = children ?? (
68
- <Image
69
- source={{
70
- uri: image,
71
- }}
72
- style={styles.full}
73
- />
74
- );
75
-
76
- if (!isWide) {
77
- return (
78
- <Animated.View
79
- style={[styles.container, { aspectRatio: ratio }, animatedStyle]}
80
- >
81
- {content}
82
- </Animated.View>
83
- );
84
- }
85
-
86
62
  return (
87
63
  <Animated.View
88
- style={[styles.container, { height: bannerWidth / ratio }, animatedStyle]}
64
+ style={[
65
+ styles.container,
66
+ { aspectRatio: ASPECT_RATIOS[aspectRatio] },
67
+ animatedStyle,
68
+ ]}
89
69
  >
90
- {!!bannerBackgroundColor && (
91
- <LinearGradient
92
- colors={[bannerBackgroundColor, bannerBackgroundColor + '00']}
93
- style={StyleSheet.absoluteFill}
70
+ {children ?? (
71
+ <Image
72
+ source={{
73
+ uri: image,
74
+ }}
75
+ style={styles.full}
94
76
  />
95
77
  )}
96
- <View style={styles.phoneBanner}>{content}</View>
97
78
  </Animated.View>
98
79
  );
99
80
  };
@@ -109,11 +90,6 @@ const styles = StyleSheet.create({
109
90
  alignSelf: 'center',
110
91
  },
111
92
  full: { width: '100%', height: '100%' },
112
- phoneBanner: {
113
- width: HeaderBarMetrics.bannerWidth,
114
- height: '100%',
115
- alignSelf: 'center',
116
- },
117
93
  });
118
94
 
119
95
  export { HeaderAnimated };
@@ -8,12 +8,10 @@ import { Colors, Styles } from '../../Consts';
8
8
  import { Image } from '../../Image';
9
9
  import BackgroundImageView from './BackgroundImageView';
10
10
  import { useNormalizedSharedValue } from '../hooks';
11
- import { HeaderBarMetrics, useHeaderLayout } from '../headerLayout';
12
11
 
13
12
  /**
14
13
  * Header background: surface and image that fade in on scroll, with an optional
15
- * gradient (custom or theme) on top, at phone height on wide windows. Outlined
16
- * when `showBaseLineDebug` is on.
14
+ * gradient (custom or theme) on top. Outlined when `showBaseLineDebug` is on.
17
15
  */
18
16
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
19
17
  animatedValue,
@@ -27,7 +25,6 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
27
25
  const gradientColor = customGradientColor ?? theme.colors.gradient;
28
26
  const headerImage = headerBackground ?? theme.assets?.headerBackground;
29
27
  const sharedValue = useNormalizedSharedValue(animatedValue);
30
- const { isWide } = useHeaderLayout();
31
28
 
32
29
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
33
30
 
@@ -41,19 +38,14 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
41
38
  />
42
39
  <View style={styles.gradientContainer}>
43
40
  {useGradient && !!gradientColor && (
44
- <Animated.View
45
- style={[styles.extendedHeader, isWide && styles.phoneHeight]}
46
- >
41
+ <Animated.View style={styles.extendedHeader}>
47
42
  <LinearGradient
48
43
  colors={[gradientColor, gradientColor + '00']}
49
44
  style={StyleSheet.absoluteFill}
50
45
  >
51
46
  {!!headerImage && (
52
47
  <Image
53
- style={[
54
- styles.headerBackground,
55
- isWide && styles.phoneHeight,
56
- ]}
48
+ style={styles.headerBackground}
57
49
  source={{ uri: headerImage }}
58
50
  loading={false}
59
51
  />
@@ -83,11 +75,6 @@ const styles = StyleSheet.create({
83
75
  position: 'absolute',
84
76
  aspectRatio: 375 / 154,
85
77
  },
86
- // wide windows keep the phone gradient height instead of scaling with the width
87
- phoneHeight: {
88
- aspectRatio: undefined,
89
- height: HeaderBarMetrics.gradientHeight,
90
- },
91
78
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
92
79
  });
93
80
 
@@ -17,16 +17,11 @@ import BackgroundImageView from './BackgroundImageView';
17
17
  import { type AnimatedCompatValue } from '../utils';
18
18
  import { useNormalizedSharedValue } from '../hooks';
19
19
  import { useScreenLayout } from '../../Layout';
20
- import { HeaderMetrics } from '../../Layout/utils';
21
- import {
22
- HeaderBarMetrics,
23
- type HeaderContentLayout,
24
- useHeaderLayout,
25
- } from '../headerLayout';
20
+ import { DuoMetrics, HeaderMetrics } from '../../Layout/utils';
26
21
 
27
- const { buttonSize, pillWidth, sidePadding, titleGap } = HeaderBarMetrics;
28
- const SCREEN_PADDING = sidePadding;
29
- const TOOLKIT_WIDTH = pillWidth + sidePadding;
22
+ const SCREEN_PADDING = 12;
23
+ const BACK_WIDTH = 28;
24
+ const TOOLKIT_WIDTH = 73;
30
25
 
31
26
  /**
32
27
  * Extended header background: a gradient that collapses on scroll and, with
@@ -43,7 +38,6 @@ const HeaderExtendHeader: React.FC<{
43
38
  useShadowHeader?: boolean;
44
39
  gradientColor?: string;
45
40
  headerBackground?: string;
46
- contentLayout?: HeaderContentLayout;
47
41
  }> = ({
48
42
  headerType = 'default',
49
43
  animatedValue,
@@ -54,7 +48,6 @@ const HeaderExtendHeader: React.FC<{
54
48
  useShadowHeader: useShadowHeaderProp = true,
55
49
  gradientColor: customGradientColor,
56
50
  headerBackground: customBackground,
57
- contentLayout,
58
51
  }) => {
59
52
  const { theme } = useContext(ApplicationContext);
60
53
  const context = useContext<any>(MiniAppContext);
@@ -62,32 +55,29 @@ const HeaderExtendHeader: React.FC<{
62
55
  const gradientColor = customGradientColor ?? theme.colors.gradient;
63
56
  const headerBackground = customBackground ?? theme.assets?.headerBackground;
64
57
  const layout = useScreenLayout();
65
- const headerLayout = useHeaderLayout(contentLayout);
66
- // the stretch frame pulls the search in from both bar edges
67
- const frameShift = headerLayout.frameInset - sidePadding;
68
58
 
69
- const backLeft = buttonSize + sidePadding + titleGap;
70
- const defaultLeft = layout.sideBar ? SCREEN_PADDING : backLeft;
59
+ // the iPhone Duo status bar row keeps side bar sized back and toolkit
60
+ const isStatusBarRow = !!layout.statusBarClearance;
61
+ const duoToolkitWidth = DuoMetrics.itemPitch * 2 + SCREEN_PADDING;
62
+ const backWidth = isStatusBarRow ? DuoMetrics.controlSize : BACK_WIDTH;
63
+ const toolkitWidth = isStatusBarRow ? duoToolkitWidth : TOOLKIT_WIDTH;
64
+ const defaultLeft = layout.sideBar ? SCREEN_PADDING : backWidth + 20;
71
65
  const leftPosition = inputSearchProps?.leftPosition || defaultLeft;
72
- const defaultRightWidth = layout.sideBar ? 0 : TOOLKIT_WIDTH;
66
+ const defaultRightWidth = layout.sideBar ? 0 : toolkitWidth;
73
67
  const headerRightWidth = customHeaderRightWidth ?? defaultRightWidth;
74
- const contentLeft = layout.insetLeft + SCREEN_PADDING + frameShift;
75
- const contentRight = layout.insetLeft + layout.contentWidth - frameShift;
68
+ const contentLeft = layout.insetLeft + SCREEN_PADDING;
69
+ const contentRight = layout.insetLeft + layout.contentWidth;
76
70
  // collapsed on the status bar row, the search stays between its clusters
77
71
  const clearanceLeft = layout.statusBarClearance?.left ?? 0;
78
72
  const clearanceRight = layout.statusBarClearance?.right ?? 0;
79
- const searchLeft = leftPosition + clearanceLeft + frameShift;
80
- const collapsedLeft = layout.insetLeft + searchLeft;
73
+ const collapsedLeft = layout.insetLeft + leftPosition + clearanceLeft;
81
74
  const collapsedRight = contentRight - clearanceRight - headerRightWidth;
82
- const searchWidth = contentRight - contentLeft - SCREEN_PADDING;
75
+ const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
83
76
  const collapsedSearchWidth = collapsedRight - collapsedLeft - SCREEN_PADDING;
84
77
  // a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
85
78
  const titleRowHeight = layout.headerHeight - layout.headerStatusBarHeight;
86
79
  const collapsedRaise = (titleRowHeight - HeaderMetrics.barHeight) / 2;
87
80
 
88
- // the iPhone Duo side bar keeps the content search size
89
- const searchSize = layout.sideBar ? 'default' : 'header';
90
-
91
81
  let useShadowHeader = useShadowHeaderProp;
92
82
  if (inputSearchProps && Platform.OS === 'android') {
93
83
  useShadowHeader = false;
@@ -179,7 +169,6 @@ const HeaderExtendHeader: React.FC<{
179
169
  <Animated.View
180
170
  style={[
181
171
  styles.extendedHeader,
182
- headerLayout.isWide && styles.phoneHeight,
183
172
  // fade out within the header box, as DuoScrollGradient does
184
173
  !!layout.sideBar && styles.fillHeaderBox,
185
174
  gradientOpacityStyle,
@@ -191,10 +180,7 @@ const HeaderExtendHeader: React.FC<{
191
180
  >
192
181
  {!!theme.assets?.headerBackground && (
193
182
  <Image
194
- style={[
195
- styles.headerBackground,
196
- headerLayout.isWide && styles.phoneHeight,
197
- ]}
183
+ style={styles.headerBackground}
198
184
  source={{ uri: theme.assets?.headerBackground }}
199
185
  loading={false}
200
186
  />
@@ -203,7 +189,9 @@ const HeaderExtendHeader: React.FC<{
203
189
  </Animated.View>
204
190
  )}
205
191
  </Animated.View>
206
- <Animated.View style={[styles.searchContainer, heightStyle]}>
192
+ <Animated.View
193
+ style={[styles.searchContainer, heightStyle]}
194
+ >
207
195
  <Animated.View
208
196
  style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
209
197
  >
@@ -213,7 +201,6 @@ const HeaderExtendHeader: React.FC<{
213
201
  <InputSearch
214
202
  {...inputSearchProps}
215
203
  ref={inputSearchRef}
216
- size={searchSize}
217
204
  backgroundColor={'transparent'}
218
205
  showButtonText={false}
219
206
  />
@@ -249,10 +236,7 @@ const HeaderExtendHeader: React.FC<{
249
236
  >
250
237
  {!!headerBackground && (
251
238
  <Image
252
- style={[
253
- styles.headerBackground,
254
- headerLayout.isWide && styles.phoneHeight,
255
- ]}
239
+ style={styles.headerBackground}
256
240
  source={{ uri: headerBackground }}
257
241
  loading={false}
258
242
  />
@@ -280,11 +264,6 @@ const styles = StyleSheet.create({
280
264
  position: 'absolute',
281
265
  width: '100%',
282
266
  },
283
- // wide windows keep the phone gradient height instead of scaling with the width
284
- phoneHeight: {
285
- aspectRatio: undefined,
286
- height: HeaderBarMetrics.gradientHeight,
287
- },
288
267
  fillHeaderBox: {
289
268
  aspectRatio: undefined,
290
269
  height: '100%',
@@ -294,6 +273,14 @@ const styles = StyleSheet.create({
294
273
  position: 'absolute',
295
274
  overflow: 'hidden',
296
275
  },
276
+ verifiedDot: {
277
+ width: 4,
278
+ height: 4,
279
+ borderRadius: 2,
280
+ backgroundColor: Colors.green_03,
281
+ position: 'absolute',
282
+ alignSelf: 'center',
283
+ },
297
284
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
298
285
  container: {
299
286
  zIndex: 0,
@@ -6,13 +6,12 @@ import { PopupNotify } from '../../Popup';
6
6
  import { NavigationButton } from './NavigationButton';
7
7
  import { SideBarButton } from './SideBarButton';
8
8
  import Animated from 'react-native-reanimated';
9
- import { useSideBarItemStyle } from '../../Layout/utils';
9
+ import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
10
10
  import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
11
- import { HeaderBarMetrics } from '../headerLayout';
12
11
 
13
12
  /**
14
13
  * Header back button, also bound to the Android hardware back. `preventBack`
15
- * confirms in a popup first; the iPhone Duo side bar uses the 48pt side bar button.
14
+ * confirms in a popup first; the iPhone Duo layouts use the 48pt side bar button.
16
15
  */
17
16
  const HeaderLeft: React.FC<HeaderBackProps> = ({
18
17
  tintColor,
@@ -25,6 +24,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
25
24
  const { navigator } = useContext(ApplicationContext);
26
25
  const sideBarStyle = useSideBarItemStyle('left', 0);
27
26
  const counterStyle = useSideBarCounterStyle();
27
+ const { statusBarClearance } = useScreenLayout();
28
28
 
29
29
  const goBack = useCallback(() => {
30
30
  const canGoBack = navigator?.ref?.current?.canGoBack?.();
@@ -97,6 +97,20 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
97
97
  );
98
98
  }
99
99
 
100
+ // on the status bar row the controls keep the side bar size
101
+ if (statusBarClearance) {
102
+ return (
103
+ <View style={styles.headerLeft}>
104
+ <SideBarButton
105
+ accessibilityLabel="btn_navigation_back"
106
+ icon="arrow-back"
107
+ tintColor={tintColor}
108
+ onPress={goBackSafe}
109
+ />
110
+ </View>
111
+ );
112
+ }
113
+
100
114
  return (
101
115
  <View style={styles.headerLeft}>
102
116
  <NavigationButton
@@ -111,7 +125,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
111
125
 
112
126
  const styles = StyleSheet.create({
113
127
  headerLeft: {
114
- marginLeft: HeaderBarMetrics.sidePadding,
128
+ marginLeft: 12,
115
129
  },
116
130
  });
117
131
 
@@ -1,14 +1,11 @@
1
1
  import { Platform, StyleSheet } from 'react-native';
2
2
  import { Colors, Spacing } from '../../Consts';
3
- import { HeaderBarMetrics } from '../headerLayout';
4
-
5
- const { buttonSize, pillWidth, pillHeight, sidePadding } = HeaderBarMetrics;
6
3
 
7
4
  export const styles = StyleSheet.create({
8
5
  navigationButton: {
9
- height: buttonSize,
10
- width: buttonSize,
11
- borderRadius: buttonSize / 2,
6
+ height: 28,
7
+ width: 28,
8
+ borderRadius: 14,
12
9
  justifyContent: 'center',
13
10
  alignItems: 'center',
14
11
  backgroundColor: Colors.black_01 + '99',
@@ -48,11 +45,11 @@ export const styles = StyleSheet.create({
48
45
  flexDirection: 'row',
49
46
  justifyContent: 'center',
50
47
  alignItems: 'center',
51
- paddingRight: sidePadding,
48
+ paddingRight: Spacing.M,
52
49
  },
53
50
  toolkitContainer: {
54
- height: pillHeight,
55
- borderRadius: pillHeight / 2,
51
+ height: 28,
52
+ borderRadius: 14,
56
53
  justifyContent: 'center',
57
54
  alignItems: 'center',
58
55
  flexDirection: 'row',
@@ -62,10 +59,7 @@ export const styles = StyleSheet.create({
62
59
  marginLeft: Spacing.S,
63
60
  },
64
61
  toolkitButton: {
65
- flex: 1,
66
- height: '100%',
67
- justifyContent: 'center',
68
- alignItems: 'center',
62
+ padding: 4,
69
63
  },
70
64
  divider: {
71
65
  width: 0.5,
@@ -74,6 +68,10 @@ export const styles = StyleSheet.create({
74
68
  sideBarColumn: {
75
69
  gap: Spacing.M,
76
70
  },
71
+ sideBarRow: {
72
+ flexDirection: 'row',
73
+ gap: Spacing.M,
74
+ },
77
75
  headerLeft: {
78
76
  marginLeft: 12,
79
77
  },
@@ -138,9 +136,9 @@ export const styles = StyleSheet.create({
138
136
  marginRight: Spacing.M,
139
137
  },
140
138
  toolkitWide: {
141
- width: pillWidth,
139
+ width: 65,
142
140
  },
143
141
  toolkitNarrow: {
144
- width: buttonSize,
142
+ width: 28,
145
143
  },
146
144
  });