@momo-kits/foundation 0.169.7-beta.4 → 0.169.7-beta.6

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 (146) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +44 -34
  3. package/Application/BottomSheet.tsx +40 -37
  4. package/Application/BottomTab/ActiveIndicator.tsx +15 -14
  5. package/Application/BottomTab/BottomTabBar.tsx +49 -62
  6. package/Application/BottomTab/BottomTabInset.tsx +8 -4
  7. package/Application/BottomTab/CurvedContainer.tsx +14 -12
  8. package/Application/BottomTab/CustomBottomTabItem.tsx +13 -13
  9. package/Application/BottomTab/DuoBarCompression.tsx +13 -19
  10. package/Application/BottomTab/DuoRailTabBar.tsx +20 -24
  11. package/Application/BottomTab/DuoTabBar.tsx +8 -10
  12. package/Application/BottomTab/TabBarIcon.tsx +15 -29
  13. package/Application/BottomTab/TabScreen.tsx +1 -5
  14. package/Application/BottomTab/index.tsx +33 -30
  15. package/Application/BottomTab/metrics.ts +23 -23
  16. package/Application/BottomTab/tabItems.ts +1 -1
  17. package/Application/BottomTab/usePressLift.ts +2 -2
  18. package/Application/Components/BackgroundImageView.tsx +4 -0
  19. package/Application/Components/DuoGlassBackground.tsx +5 -4
  20. package/Application/Components/HeaderAnimated.tsx +40 -12
  21. package/Application/Components/HeaderBackground.tsx +19 -2
  22. package/Application/Components/HeaderExtendHeader.tsx +56 -34
  23. package/Application/Components/HeaderLeft.tsx +4 -2
  24. package/Application/Components/HeaderRight.styles.ts +15 -9
  25. package/Application/Components/HeaderRight.tsx +2 -1
  26. package/Application/Components/HeaderTitle.shared.ts +19 -12
  27. package/Application/Components/HeaderTitle.tsx +4 -3
  28. package/Application/Components/HeaderToolkitAction.tsx +28 -22
  29. package/Application/Components/NavigationButton.tsx +10 -6
  30. package/Application/Components/SearchHeader.tsx +41 -15
  31. package/Application/Components/TitleJourney.tsx +2 -2
  32. package/Application/Components/TitleLocation.tsx +2 -2
  33. package/Application/Components/TitleUser.tsx +2 -2
  34. package/Application/Components/useToolkitActions.tsx +17 -32
  35. package/Application/Localize.ts +7 -0
  36. package/Application/ModalScreen.tsx +10 -10
  37. package/Application/Navigation.ts +14 -16
  38. package/Application/NavigationContainer.tsx +19 -18
  39. package/Application/Navigator.ts +20 -48
  40. package/Application/ScaleSizeProvider.tsx +13 -6
  41. package/Application/StackScreen.tracking.ts +11 -33
  42. package/Application/StackScreen.tsx +19 -54
  43. package/Application/TooltipPortal.tsx +12 -5
  44. package/Application/WidgetContainer.tsx +10 -4
  45. package/Application/headerLayout.ts +158 -0
  46. package/Application/index.ts +11 -1
  47. package/Application/modalLayout.ts +13 -14
  48. package/Application/navigationOptions.tsx +27 -32
  49. package/Application/types.header.ts +7 -2
  50. package/Application/types.ts +7 -0
  51. package/Badge/Badge.tsx +5 -0
  52. package/Badge/BadgeDot.tsx +1 -0
  53. package/Badge/BadgeDotAnimation.tsx +15 -7
  54. package/Badge/BadgeRibbon.tsx +46 -38
  55. package/Badge/Shape.tsx +2 -0
  56. package/Badge/types.ts +2 -2
  57. package/Button/index.tsx +5 -16
  58. package/Button/utils.ts +9 -15
  59. package/CheckBox/index.tsx +4 -0
  60. package/Consts/theme.ts +10 -10
  61. package/Context/index.ts +5 -1
  62. package/Divider/DashDivider.tsx +1 -0
  63. package/Divider/index.tsx +4 -0
  64. package/FoundationList/index.tsx +4 -0
  65. package/Icon/index.tsx +6 -6
  66. package/Icon/types.ts +1 -1
  67. package/IconButton/index.tsx +4 -12
  68. package/Image/index.tsx +9 -7
  69. package/Input/Input.tsx +8 -9
  70. package/Input/InputDropDown.tsx +10 -12
  71. package/Input/InputMoney.tsx +12 -8
  72. package/Input/InputMoneyView.tsx +14 -7
  73. package/Input/InputOTP.tsx +35 -33
  74. package/Input/InputPhoneNumber.tsx +8 -9
  75. package/Input/InputSearch.tsx +22 -8
  76. package/Input/InputTextArea.tsx +4 -0
  77. package/Input/SearchTextTyping.tsx +4 -0
  78. package/Input/SystemTextInput.tsx +7 -2
  79. package/Input/TextTyping.tsx +4 -0
  80. package/Input/common.tsx +26 -12
  81. package/Input/index.tsx +24 -21
  82. package/Input/moneyUtils.ts +10 -4
  83. package/Input/styles.ts +0 -5
  84. package/Input/utils.ts +14 -12
  85. package/Layout/Card.tsx +7 -4
  86. package/Layout/FloatingButton.tsx +4 -0
  87. package/Layout/Item.tsx +6 -2
  88. package/Layout/ItemList.tsx +9 -7
  89. package/Layout/ItemSectionList.tsx +2 -1
  90. package/Layout/ReservedRegions.tsx +25 -25
  91. package/Layout/Screen.tsx +59 -58
  92. package/Layout/Section.tsx +8 -5
  93. package/Layout/TrackingScope.tsx +1 -0
  94. package/Layout/screenContent.tsx +4 -2
  95. package/Layout/screenFooter.tsx +28 -18
  96. package/Layout/screenHeader.tsx +88 -82
  97. package/Layout/screenQuickReturn.tsx +17 -20
  98. package/Layout/screenScroll.ts +4 -7
  99. package/Layout/screenTypes.ts +13 -4
  100. package/Layout/types.ts +2 -4
  101. package/Layout/utils.ts +31 -37
  102. package/Loader/DotLoader.tsx +4 -0
  103. package/Loader/Loader.tsx +4 -0
  104. package/Loader/ProgressBar.tsx +1 -0
  105. package/Loader/Spinner.tsx +4 -0
  106. package/Loader/utils.ts +11 -7
  107. package/Pagination/Dot.tsx +4 -0
  108. package/Pagination/PaginationDot.tsx +1 -0
  109. package/Pagination/PaginationNumber.tsx +5 -3
  110. package/Pagination/PaginationScroll.tsx +5 -0
  111. package/Pagination/PaginationWhiteDot.tsx +1 -0
  112. package/Pagination/index.tsx +4 -0
  113. package/Pagination/types.ts +2 -2
  114. package/Popup/PopupNotify.tsx +44 -48
  115. package/Popup/PopupPromotion.tsx +4 -7
  116. package/Popup/types.ts +4 -4
  117. package/Popup/utils.ts +4 -0
  118. package/Radio/index.tsx +4 -0
  119. package/Skeleton/index.tsx +8 -0
  120. package/Switch/index.tsx +1 -0
  121. package/Tag/index.tsx +5 -1
  122. package/Tag/types.ts +3 -3
  123. package/Text/index.tsx +14 -3
  124. package/Text/styles.ts +1 -1
  125. package/Text/types.ts +1 -6
  126. package/Text/utils.ts +20 -4
  127. package/Title/index.tsx +41 -32
  128. package/ViewBoundary/index.tsx +4 -2
  129. package/index.ts +0 -6
  130. package/package.json +1 -1
  131. package/Application/KitAdoption/AdoptionBadge.tsx +0 -119
  132. package/Application/KitAdoption/AdoptionProbe.tsx +0 -166
  133. package/Application/KitAdoption/AdoptionStore.ts +0 -80
  134. package/Application/KitAdoption/Authorship/utils.ts +0 -137
  135. package/Application/KitAdoption/Slots/utils.ts +0 -68
  136. package/Application/KitAdoption/constants.ts +0 -120
  137. package/Application/KitAdoption/index.ts +0 -12
  138. package/Application/KitAdoption/styles.ts +0 -55
  139. package/Application/KitAdoption/types.ts +0 -61
  140. package/Application/KitAdoption/utils.ts +0 -376
  141. package/Application/__tests__/KitAdoption.integration.test.tsx +0 -253
  142. package/Application/__tests__/KitAdoption.test.ts +0 -283
  143. package/Application/__tests__/KitAdoptionAuthorship.test.ts +0 -132
  144. package/Application/__tests__/KitAdoptionStore.test.ts +0 -110
  145. package/Application/__tests__/KitAdoptionStructural.test.ts +0 -193
  146. package/Application/__tests__/modalLayout.test.ts +0 -128
@@ -1,4 +1,5 @@
1
- import { StyleSheet } from 'react-native';
1
+ import { StyleSheet, View } from 'react-native';
2
+ import LinearGradient from 'react-native-linear-gradient';
2
3
  import Animated, {
3
4
  Extrapolation,
4
5
  interpolate,
@@ -9,6 +10,7 @@ import React from 'react';
9
10
  import { Image } from '../../Image';
10
11
  import { Colors } from '../../Consts';
11
12
  import { useNormalizedSharedValue } from '../hooks';
13
+ import { HeaderBarMetrics, useHeaderLayout } from '../headerLayout';
12
14
 
13
15
  const ASPECT_RATIOS: Record<
14
16
  NonNullable<HeaderAnimatedProps['aspectRatio']>,
@@ -19,14 +21,21 @@ const ASPECT_RATIOS: Record<
19
21
  '1/1': 1 / 1,
20
22
  };
21
23
 
24
+ /**
25
+ * 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.
27
+ */
22
28
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
23
29
  animatedValue,
24
30
  image,
25
31
  useScale = true,
26
32
  aspectRatio = '16/9',
33
+ bannerBackgroundColor,
27
34
  children,
28
35
  }) => {
29
36
  const sharedValue = useNormalizedSharedValue(animatedValue);
37
+ const { isWide, bannerWidth } = useHeaderLayout();
38
+ const ratio = ASPECT_RATIOS[aspectRatio];
30
39
  const animatedStyle = useAnimatedStyle(() => {
31
40
  let scale = 1;
32
41
  if (useScale) {
@@ -55,22 +64,36 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
55
64
  };
56
65
  });
57
66
 
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
+
58
86
  return (
59
87
  <Animated.View
60
- style={[
61
- styles.container,
62
- { aspectRatio: ASPECT_RATIOS[aspectRatio] },
63
- animatedStyle,
64
- ]}
88
+ style={[styles.container, { height: bannerWidth / ratio }, animatedStyle]}
65
89
  >
66
- {children ?? (
67
- <Image
68
- source={{
69
- uri: image,
70
- }}
71
- style={styles.full}
90
+ {!!bannerBackgroundColor && (
91
+ <LinearGradient
92
+ colors={[bannerBackgroundColor, bannerBackgroundColor + '00']}
93
+ style={StyleSheet.absoluteFill}
72
94
  />
73
95
  )}
96
+ <View style={styles.phoneBanner}>{content}</View>
74
97
  </Animated.View>
75
98
  );
76
99
  };
@@ -86,6 +109,11 @@ const styles = StyleSheet.create({
86
109
  alignSelf: 'center',
87
110
  },
88
111
  full: { width: '100%', height: '100%' },
112
+ phoneBanner: {
113
+ width: HeaderBarMetrics.bannerWidth,
114
+ height: '100%',
115
+ alignSelf: 'center',
116
+ },
89
117
  });
90
118
 
91
119
  export { HeaderAnimated };
@@ -8,7 +8,13 @@ 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';
11
12
 
13
+ /**
14
+ * 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.
17
+ */
12
18
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
13
19
  animatedValue,
14
20
  useGradient = true,
@@ -21,6 +27,7 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
21
27
  const gradientColor = customGradientColor ?? theme.colors.gradient;
22
28
  const headerImage = headerBackground ?? theme.assets?.headerBackground;
23
29
  const sharedValue = useNormalizedSharedValue(animatedValue);
30
+ const { isWide } = useHeaderLayout();
24
31
 
25
32
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
26
33
 
@@ -34,14 +41,19 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
34
41
  />
35
42
  <View style={styles.gradientContainer}>
36
43
  {useGradient && !!gradientColor && (
37
- <Animated.View style={styles.extendedHeader}>
44
+ <Animated.View
45
+ style={[styles.extendedHeader, isWide && styles.phoneHeight]}
46
+ >
38
47
  <LinearGradient
39
48
  colors={[gradientColor, gradientColor + '00']}
40
49
  style={StyleSheet.absoluteFill}
41
50
  >
42
51
  {!!headerImage && (
43
52
  <Image
44
- style={styles.headerBackground}
53
+ style={[
54
+ styles.headerBackground,
55
+ isWide && styles.phoneHeight,
56
+ ]}
45
57
  source={{ uri: headerImage }}
46
58
  loading={false}
47
59
  />
@@ -71,6 +83,11 @@ const styles = StyleSheet.create({
71
83
  position: 'absolute',
72
84
  aspectRatio: 375 / 154,
73
85
  },
86
+ // wide windows keep the phone gradient height instead of scaling with the width
87
+ phoneHeight: {
88
+ aspectRatio: undefined,
89
+ height: HeaderBarMetrics.gradientHeight,
90
+ },
74
91
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
75
92
  });
76
93
 
@@ -18,11 +18,20 @@ import { type AnimatedCompatValue } from '../utils';
18
18
  import { useNormalizedSharedValue } from '../hooks';
19
19
  import { useScreenLayout } from '../../Layout';
20
20
  import { DuoMetrics, HeaderMetrics } from '../../Layout/utils';
21
+ import {
22
+ HeaderBarMetrics,
23
+ type HeaderContentLayout,
24
+ useHeaderLayout,
25
+ } from '../headerLayout';
21
26
 
22
- const SCREEN_PADDING = 12;
23
- const BACK_WIDTH = 28;
24
- const TOOLKIT_WIDTH = 73;
27
+ const { buttonSize, pillWidth, sidePadding, titleGap } = HeaderBarMetrics;
28
+ const SCREEN_PADDING = sidePadding;
29
+ const TOOLKIT_WIDTH = pillWidth + sidePadding;
25
30
 
31
+ /**
32
+ * Extended header background: a gradient that collapses on scroll and, with
33
+ * `inputSearchProps`, a full-width search that slides into the bar beside back/toolkit.
34
+ */
26
35
  const HeaderExtendHeader: React.FC<{
27
36
  headerType?: HeaderType;
28
37
  animatedValue: AnimatedCompatValue;
@@ -34,6 +43,7 @@ const HeaderExtendHeader: React.FC<{
34
43
  useShadowHeader?: boolean;
35
44
  gradientColor?: string;
36
45
  headerBackground?: string;
46
+ contentLayout?: HeaderContentLayout;
37
47
  }> = ({
38
48
  headerType = 'default',
39
49
  animatedValue,
@@ -44,6 +54,7 @@ const HeaderExtendHeader: React.FC<{
44
54
  useShadowHeader: useShadowHeaderProp = true,
45
55
  gradientColor: customGradientColor,
46
56
  headerBackground: customBackground,
57
+ contentLayout,
47
58
  }) => {
48
59
  const { theme } = useContext(ApplicationContext);
49
60
  const context = useContext<any>(MiniAppContext);
@@ -51,31 +62,39 @@ const HeaderExtendHeader: React.FC<{
51
62
  const gradientColor = customGradientColor ?? theme.colors.gradient;
52
63
  const headerBackground = customBackground ?? theme.assets?.headerBackground;
53
64
  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;
54
68
 
69
+ let backWidth = buttonSize;
70
+ let toolkitWidth = TOOLKIT_WIDTH;
55
71
  // the iPhone Duo status bar row keeps side bar sized back and toolkit
56
- const isStatusBarRow = !!layout.statusBarClearance;
57
- const duoToolkitWidth = DuoMetrics.itemPitch * 2 + SCREEN_PADDING;
58
- const backWidth = isStatusBarRow ? DuoMetrics.controlSize : BACK_WIDTH;
59
- const toolkitWidth = isStatusBarRow ? duoToolkitWidth : TOOLKIT_WIDTH;
60
- const defaultLeft = layout.sideBar ? SCREEN_PADDING : backWidth + 20;
72
+ if (layout.statusBarClearance) {
73
+ backWidth = DuoMetrics.controlSize;
74
+ toolkitWidth = DuoMetrics.itemPitch * 2 + SCREEN_PADDING;
75
+ }
76
+ const backLeft = backWidth + sidePadding + titleGap;
77
+ const defaultLeft = layout.sideBar ? SCREEN_PADDING : backLeft;
61
78
  const leftPosition = inputSearchProps?.leftPosition || defaultLeft;
62
79
  const defaultRightWidth = layout.sideBar ? 0 : toolkitWidth;
63
80
  const headerRightWidth = customHeaderRightWidth ?? defaultRightWidth;
64
- const contentLeft = layout.insetLeft + SCREEN_PADDING;
65
- const contentRight = layout.insetLeft + layout.contentWidth;
81
+ const contentLeft = layout.insetLeft + SCREEN_PADDING + frameShift;
82
+ const contentRight = layout.insetLeft + layout.contentWidth - frameShift;
66
83
  // collapsed on the status bar row, the search stays between its clusters
67
- const collapsedLeft =
68
- layout.insetLeft + leftPosition + (layout.statusBarClearance?.left ?? 0);
69
- const collapsedRight = contentRight - (layout.statusBarClearance?.right ?? 0);
70
- const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
71
- const collapsedSearchWidth =
72
- collapsedRight - collapsedLeft - SCREEN_PADDING - headerRightWidth;
84
+ const clearanceLeft = layout.statusBarClearance?.left ?? 0;
85
+ const clearanceRight = layout.statusBarClearance?.right ?? 0;
86
+ const searchLeft = leftPosition + clearanceLeft + frameShift;
87
+ const collapsedLeft = layout.insetLeft + searchLeft;
88
+ const collapsedRight = contentRight - clearanceRight - headerRightWidth;
89
+ const searchWidth = contentRight - contentLeft - SCREEN_PADDING;
90
+ const collapsedSearchWidth = collapsedRight - collapsedLeft - SCREEN_PADDING;
73
91
  // a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
74
- const collapsedRaise =
75
- (layout.headerHeight -
76
- layout.headerStatusBarHeight -
77
- HeaderMetrics.barHeight) /
78
- 2;
92
+ const titleRowHeight = layout.headerHeight - layout.headerStatusBarHeight;
93
+ const collapsedRaise = (titleRowHeight - HeaderMetrics.barHeight) / 2;
94
+
95
+ // the iPhone Duo side bar and status bar row keep the content search size
96
+ const isDuoRow = !!layout.sideBar || !!layout.statusBarClearance;
97
+ const searchSize = isDuoRow ? 'default' : 'header';
79
98
 
80
99
  let useShadowHeader = useShadowHeaderProp;
81
100
  if (inputSearchProps && Platform.OS === 'android') {
@@ -168,6 +187,7 @@ const HeaderExtendHeader: React.FC<{
168
187
  <Animated.View
169
188
  style={[
170
189
  styles.extendedHeader,
190
+ headerLayout.isWide && styles.phoneHeight,
171
191
  // fade out within the header box, as DuoScrollGradient does
172
192
  !!layout.sideBar && styles.fillHeaderBox,
173
193
  gradientOpacityStyle,
@@ -179,7 +199,10 @@ const HeaderExtendHeader: React.FC<{
179
199
  >
180
200
  {!!theme.assets?.headerBackground && (
181
201
  <Image
182
- style={styles.headerBackground}
202
+ style={[
203
+ styles.headerBackground,
204
+ headerLayout.isWide && styles.phoneHeight,
205
+ ]}
183
206
  source={{ uri: theme.assets?.headerBackground }}
184
207
  loading={false}
185
208
  />
@@ -188,9 +211,7 @@ const HeaderExtendHeader: React.FC<{
188
211
  </Animated.View>
189
212
  )}
190
213
  </Animated.View>
191
- <Animated.View
192
- style={[styles.searchContainer, heightStyle]}
193
- >
214
+ <Animated.View style={[styles.searchContainer, heightStyle]}>
194
215
  <Animated.View
195
216
  style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
196
217
  >
@@ -200,6 +221,7 @@ const HeaderExtendHeader: React.FC<{
200
221
  <InputSearch
201
222
  {...inputSearchProps}
202
223
  ref={inputSearchRef}
224
+ size={searchSize}
203
225
  backgroundColor={'transparent'}
204
226
  showButtonText={false}
205
227
  />
@@ -235,7 +257,10 @@ const HeaderExtendHeader: React.FC<{
235
257
  >
236
258
  {!!headerBackground && (
237
259
  <Image
238
- style={styles.headerBackground}
260
+ style={[
261
+ styles.headerBackground,
262
+ headerLayout.isWide && styles.phoneHeight,
263
+ ]}
239
264
  source={{ uri: headerBackground }}
240
265
  loading={false}
241
266
  />
@@ -263,6 +288,11 @@ const styles = StyleSheet.create({
263
288
  position: 'absolute',
264
289
  width: '100%',
265
290
  },
291
+ // wide windows keep the phone gradient height instead of scaling with the width
292
+ phoneHeight: {
293
+ aspectRatio: undefined,
294
+ height: HeaderBarMetrics.gradientHeight,
295
+ },
266
296
  fillHeaderBox: {
267
297
  aspectRatio: undefined,
268
298
  height: '100%',
@@ -272,14 +302,6 @@ const styles = StyleSheet.create({
272
302
  position: 'absolute',
273
303
  overflow: 'hidden',
274
304
  },
275
- verifiedDot: {
276
- width: 4,
277
- height: 4,
278
- borderRadius: 2,
279
- backgroundColor: Colors.green_03,
280
- position: 'absolute',
281
- alignSelf: 'center',
282
- },
283
305
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
284
306
  container: {
285
307
  zIndex: 0,
@@ -8,9 +8,11 @@ import { SideBarButton } from './SideBarButton';
8
8
  import Animated from 'react-native-reanimated';
9
9
  import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
10
10
  import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
11
+ import { HeaderBarMetrics } from '../headerLayout';
11
12
 
12
13
  /**
13
- * default header left used for nav
14
+ * Header back button, also bound to the Android hardware back. `preventBack`
15
+ * confirms in a popup first; the iPhone Duo layouts use the 48pt side bar button.
14
16
  */
15
17
  const HeaderLeft: React.FC<HeaderBackProps> = ({
16
18
  tintColor,
@@ -124,7 +126,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
124
126
 
125
127
  const styles = StyleSheet.create({
126
128
  headerLeft: {
127
- marginLeft: 12,
129
+ marginLeft: HeaderBarMetrics.sidePadding,
128
130
  },
129
131
  });
130
132
 
@@ -1,11 +1,14 @@
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;
3
6
 
4
7
  export const styles = StyleSheet.create({
5
8
  navigationButton: {
6
- height: 28,
7
- width: 28,
8
- borderRadius: 14,
9
+ height: buttonSize,
10
+ width: buttonSize,
11
+ borderRadius: buttonSize / 2,
9
12
  justifyContent: 'center',
10
13
  alignItems: 'center',
11
14
  backgroundColor: Colors.black_01 + '99',
@@ -45,11 +48,11 @@ export const styles = StyleSheet.create({
45
48
  flexDirection: 'row',
46
49
  justifyContent: 'center',
47
50
  alignItems: 'center',
48
- paddingRight: Spacing.M,
51
+ paddingRight: sidePadding,
49
52
  },
50
53
  toolkitContainer: {
51
- height: 28,
52
- borderRadius: 14,
54
+ height: pillHeight,
55
+ borderRadius: pillHeight / 2,
53
56
  justifyContent: 'center',
54
57
  alignItems: 'center',
55
58
  flexDirection: 'row',
@@ -59,7 +62,10 @@ export const styles = StyleSheet.create({
59
62
  marginLeft: Spacing.S,
60
63
  },
61
64
  toolkitButton: {
62
- padding: 4,
65
+ flex: 1,
66
+ height: '100%',
67
+ justifyContent: 'center',
68
+ alignItems: 'center',
63
69
  },
64
70
  divider: {
65
71
  width: 0.5,
@@ -136,9 +142,9 @@ export const styles = StyleSheet.create({
136
142
  marginRight: Spacing.M,
137
143
  },
138
144
  toolkitWide: {
139
- width: 65,
145
+ width: pillWidth,
140
146
  },
141
147
  toolkitNarrow: {
142
- width: 28,
148
+ width: buttonSize,
143
149
  },
144
150
  });
@@ -9,7 +9,8 @@ import { HeaderToolkitAction } from './HeaderToolkitAction';
9
9
  import { styles } from './HeaderRight.styles';
10
10
 
11
11
  /**
12
- * main component for header right
12
+ * Header right slot: a custom `headerRight` render function, the onboarding skip
13
+ * button, or the toolkit (stacked vertically in the floating iPhone Duo side bar).
13
14
  */
14
15
  const HeaderRight: React.FC<any> = props => {
15
16
  const { translate } = useContext(ApplicationContext);
@@ -3,41 +3,48 @@ import { MiniAppContext } from '../../Context';
3
3
  import { Spacing } from '../../Consts';
4
4
  import { isWhiteListDesignSystem } from '../utils';
5
5
  import { useScreenLayout } from '../../Layout';
6
+ import { HeaderBarMetrics } from '../headerLayout';
6
7
 
8
+ const { sidePadding, buttonSize, titleGap, clusterWidth } = HeaderBarMetrics;
9
+
10
+ /**
11
+ * Header title font: MoMoTrustDisplay for design-system whitelisted mini apps,
12
+ * SFProText otherwise.
13
+ */
7
14
  const useHeaderFontFamily = (): string => {
8
15
  const context = useContext<any>(MiniAppContext);
9
16
  return isWhiteListDesignSystem(context) ? 'MoMoTrustDisplay' : 'SFProText';
10
17
  };
11
18
 
12
19
  /**
13
- * back button and toolkit sharing the navigation bar with the title
20
+ * side paddings, back button, title gaps and right cluster sharing the
21
+ * navigation bar with the title
14
22
  */
15
- const HEADER_ACTIONS_WIDTH = 172;
23
+ const HEADER_ACTIONS_WIDTH =
24
+ sidePadding * 2 + buttonSize + titleGap * 2 + clusterWidth;
16
25
 
17
26
  /**
18
27
  * extra width the 48pt back and toolkit take on the iPhone Duo status bar row
19
28
  */
20
- const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
29
+ const STATUS_BAR_ROW_ACTIONS_EXTRA = 43;
21
30
 
22
31
  /**
23
- * max title width, the iPhone Duo side bar takes back and toolkit out of the bar
32
+ * Max header title width. The iPhone Duo side bar takes back and toolkit out of
33
+ * the bar, so the title gets the content width; otherwise it shares the bar with them.
24
34
  */
25
35
  const useTitleMaxWidth = () => {
26
- const { width, contentWidth, sideBar, statusBarClearance } =
27
- useScreenLayout();
36
+ const layout = useScreenLayout();
37
+ const { width, contentWidth, sideBar, statusBarClearance } = layout;
28
38
  let clearance = 0;
39
+ let rowActionsExtra = 0;
29
40
  if (statusBarClearance) {
30
41
  clearance = statusBarClearance.left + statusBarClearance.right;
42
+ rowActionsExtra = STATUS_BAR_ROW_ACTIONS_EXTRA;
31
43
  }
32
44
  if (sideBar) {
33
45
  return contentWidth - Spacing.M * 2 - clearance;
34
46
  }
35
- return (
36
- width -
37
- HEADER_ACTIONS_WIDTH -
38
- (statusBarClearance ? STATUS_BAR_ROW_ACTIONS_EXTRA : 0) -
39
- clearance
40
- );
47
+ return width - HEADER_ACTIONS_WIDTH - rowActionsExtra - clearance;
41
48
  };
42
49
 
43
50
  export { useHeaderFontFamily, HEADER_ACTIONS_WIDTH, useTitleMaxWidth };
@@ -33,7 +33,8 @@ type HeaderTitleExtraProps = {
33
33
  };
34
34
 
35
35
  /**
36
- * default header title used for nav
36
+ * Default navigation header title. With `animatedValue` its opacity follows the
37
+ * scroll through `interpolate` (0 to 1 over the first 200pt by default).
37
38
  */
38
39
  const HeaderTitle: React.FC<
39
40
  HeaderTitleExtraProps & { [key: string]: any }
@@ -100,8 +101,8 @@ const HeaderTitle: React.FC<
100
101
  };
101
102
 
102
103
  /**
103
- * Title custom for header
104
- * @constructor
104
+ * Header title picked by `type` (user, location or journey) with `data` as its
105
+ * props; an empty view for an unknown type.
105
106
  */
106
107
  const TitleCustom: React.FC<any> = ({ type, data, ...props }) => {
107
108
  switch (type) {
@@ -9,12 +9,14 @@ import { SideBarButton, SideBarGroup, SideBarGroupItem } from './SideBarButton';
9
9
  import { useDuoBarCompression } from '../BottomTab/DuoBarCompression';
10
10
  import { styles } from './HeaderRight.styles';
11
11
  import { useToolkitActions } from './useToolkitActions';
12
+ import { HeaderBarMetrics } from '../headerLayout';
12
13
 
13
14
  const CLOSE_ICON = '16_navigation_close_circle';
14
15
  const HOME_ICON = '16_basic_home';
15
16
 
16
17
  /**
17
- * Header toolkit action
18
+ * Header toolkit: optional shortcut plus the help center / close capsule. When
19
+ * `vertical` (iPhone Duo side bar) it reports its height and may fold into one button.
18
20
  */
19
21
  const HeaderToolkitAction: React.FC<any> = ({
20
22
  tintColor,
@@ -27,8 +29,9 @@ const HeaderToolkitAction: React.FC<any> = ({
27
29
  vertical = false,
28
30
  }) => {
29
31
  const context = useContext<any>(MiniAppContext);
30
- const { toolbarCompressed, toolbarHidden, expandToolbar, setToolbarHeight } =
31
- useDuoBarCompression();
32
+ const compression = useDuoBarCompression();
33
+ const { toolbarCompressed, toolbarHidden } = compression;
34
+ const { expandToolbar, setToolbarHeight } = compression;
32
35
  const { statusBarClearance } = useScreenLayout();
33
36
  const {
34
37
  isLoading,
@@ -56,18 +59,22 @@ const HeaderToolkitAction: React.FC<any> = ({
56
59
 
57
60
  const useHelpCenter = context.appId !== 'vn.momo.helpcenter';
58
61
  const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
62
+ let toolkitWidthStyle: ViewStyle = styles.toolkitNarrow;
63
+ if (useHelpCenter) {
64
+ toolkitWidthStyle = styles.toolkitWide;
65
+ }
59
66
 
60
67
  const groupCount = useHelpCenter ? 2 : 1;
61
68
  let capsuleHeight = DuoMetrics.controlSize;
62
69
  if (groupCount > 1) {
63
70
  capsuleHeight = groupCount * DuoMetrics.itemPitch;
64
71
  }
65
- // Back slot, the optional shortcut, then the help/home capsule
66
- const fullToolbarHeight =
67
- DuoMetrics.controlSize +
68
- Spacing.M +
69
- (useShortcut ? DuoMetrics.controlSize + Spacing.M : 0) +
70
- capsuleHeight;
72
+ const backSlotHeight = DuoMetrics.controlSize + Spacing.M;
73
+ let shortcutHeight = 0;
74
+ if (useShortcut) {
75
+ shortcutHeight = DuoMetrics.controlSize + Spacing.M;
76
+ }
77
+ const fullToolbarHeight = backSlotHeight + shortcutHeight + capsuleHeight;
71
78
 
72
79
  useEffect(() => {
73
80
  if (vertical) {
@@ -80,8 +87,7 @@ const HeaderToolkitAction: React.FC<any> = ({
80
87
  return null;
81
88
  }
82
89
 
83
- // HIG "Toolbar compressed": tools fold into one button; tapping it expands the
84
- // toolbar and compresses the tab bar instead. Not the useMore icon, to tell them apart
90
+ // HIG "Toolbar compressed": a menu icon (not the "more" one) that expands the toolbar
85
91
  if (vertical && toolbarCompressed) {
86
92
  return (
87
93
  <SideBarButton
@@ -170,15 +176,7 @@ const HeaderToolkitAction: React.FC<any> = ({
170
176
  showBadge={showBadge}
171
177
  />
172
178
  )}
173
- <View
174
- style={[
175
- styles.toolkitContainer,
176
- buttonStyle,
177
- context.appId !== 'vn.momo.helpcenter'
178
- ? styles.toolkitWide
179
- : styles.toolkitNarrow,
180
- ]}
181
- >
179
+ <View style={[styles.toolkitContainer, buttonStyle, toolkitWidthStyle]}>
182
180
  {useHelpCenter && (
183
181
  <>
184
182
  <TouchableOpacity
@@ -187,7 +185,11 @@ const HeaderToolkitAction: React.FC<any> = ({
187
185
  onPress={onPressHelpCenter}
188
186
  hitSlop={{ top: 7, bottom: 7, left: 7, right: 0 }}
189
187
  >
190
- <Icon color={tintColor} source={'help_center'} size={20} />
188
+ <Icon
189
+ color={tintColor}
190
+ source={'help_center'}
191
+ size={HeaderBarMetrics.iconSize}
192
+ />
191
193
  </TouchableOpacity>
192
194
  <View style={[styles.divider, { backgroundColor: tintColor }]} />
193
195
  </>
@@ -198,7 +200,11 @@ const HeaderToolkitAction: React.FC<any> = ({
198
200
  style={styles.toolkitButton}
199
201
  hitSlop={{ top: 7, bottom: 7, left: 0, right: 7 }}
200
202
  >
201
- <Icon color={tintColor} source={closeIcon} size={20} />
203
+ <Icon
204
+ color={tintColor}
205
+ source={closeIcon}
206
+ size={HeaderBarMetrics.iconSize}
207
+ />
202
208
  </TouchableOpacity>
203
209
  </View>
204
210
  </View>
@@ -5,9 +5,13 @@ import { StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native';
5
5
  import { Colors, Spacing } from '../../Consts';
6
6
  import { Icon } from '../../Icon';
7
7
  import { BadgeDot } from '../../Badge';
8
+ import { HeaderBarMetrics } from '../headerLayout';
9
+
10
+ const { buttonSize, iconSize } = HeaderBarMetrics;
8
11
 
9
12
  /**
10
- * default navigation button used header nav
13
+ * Round 32pt icon button of the header (back, shortcut) with an optional badge
14
+ * dot; switches to a dark fill under a white tint so the icon stays readable.
11
15
  */
12
16
  const NavigationButton: React.FC<NavigationButtonProps> = ({
13
17
  icon,
@@ -34,7 +38,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
34
38
  <Icon
35
39
  source={icon}
36
40
  color={tintColor ?? theme.colors.text.default}
37
- size={20}
41
+ size={iconSize}
38
42
  />
39
43
  </TouchableOpacity>
40
44
  {showBadge && <BadgeDot size={'small'} style={styles.badgeDot} />}
@@ -44,12 +48,12 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
44
48
 
45
49
  const styles = StyleSheet.create({
46
50
  container: {
47
- width: 28,
51
+ width: buttonSize,
48
52
  },
49
53
  navigationButton: {
50
- height: 28,
51
- width: 28,
52
- borderRadius: 14,
54
+ height: buttonSize,
55
+ width: buttonSize,
56
+ borderRadius: buttonSize / 2,
53
57
  justifyContent: 'center',
54
58
  alignItems: 'center',
55
59
  backgroundColor: Colors.black_01 + '99',