@momo-kits/foundation 0.169.7-beta.1 → 0.169.7-beta.10

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 (145) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +34 -44
  3. package/Application/BottomSheet.tsx +37 -40
  4. package/Application/BottomTab/ActiveIndicator.tsx +14 -15
  5. package/Application/BottomTab/BottomTabBar.styles.ts +1 -18
  6. package/Application/BottomTab/BottomTabBar.tsx +72 -57
  7. package/Application/BottomTab/BottomTabInset.tsx +4 -8
  8. package/Application/BottomTab/CurvedContainer.tsx +14 -22
  9. package/Application/BottomTab/CustomBottomTabItem.tsx +15 -15
  10. package/Application/BottomTab/DuoBarCompression.tsx +19 -13
  11. package/Application/BottomTab/DuoRailTabBar.tsx +24 -20
  12. package/Application/BottomTab/DuoTabBar.tsx +13 -23
  13. package/Application/BottomTab/TabBarIcon.tsx +29 -15
  14. package/Application/BottomTab/TabScreen.tsx +5 -1
  15. package/Application/BottomTab/index.tsx +30 -33
  16. package/Application/BottomTab/metrics.ts +26 -46
  17. package/Application/BottomTab/tabItems.ts +1 -1
  18. package/Application/BottomTab/usePressLift.ts +2 -2
  19. package/Application/Components/BackgroundImageView.tsx +0 -4
  20. package/Application/Components/DuoGlassBackground.tsx +4 -5
  21. package/Application/Components/HeaderAnimated.tsx +0 -4
  22. package/Application/Components/HeaderBackground.tsx +0 -4
  23. package/Application/Components/HeaderExtendHeader.tsx +10 -11
  24. package/Application/Components/HeaderLeft.tsx +1 -2
  25. package/Application/Components/HeaderRight.tsx +1 -2
  26. package/Application/Components/HeaderTitle.shared.ts +9 -11
  27. package/Application/Components/HeaderTitle.tsx +3 -4
  28. package/Application/Components/HeaderToolkitAction.tsx +20 -17
  29. package/Application/Components/NavigationButton.tsx +1 -2
  30. package/Application/Components/SearchHeader.tsx +10 -17
  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 +32 -17
  35. package/Application/KitAdoption/AdoptionBadge.tsx +119 -0
  36. package/Application/KitAdoption/AdoptionProbe.tsx +166 -0
  37. package/Application/KitAdoption/AdoptionStore.ts +80 -0
  38. package/Application/KitAdoption/Authorship/utils.ts +137 -0
  39. package/Application/KitAdoption/Slots/utils.ts +68 -0
  40. package/Application/KitAdoption/constants.ts +120 -0
  41. package/Application/KitAdoption/index.ts +12 -0
  42. package/Application/KitAdoption/styles.ts +55 -0
  43. package/Application/KitAdoption/types.ts +61 -0
  44. package/Application/KitAdoption/utils.ts +376 -0
  45. package/Application/Localize.ts +0 -7
  46. package/Application/ModalScreen.tsx +10 -10
  47. package/Application/Navigation.ts +16 -14
  48. package/Application/NavigationContainer.tsx +18 -19
  49. package/Application/Navigator.ts +48 -20
  50. package/Application/ScaleSizeProvider.tsx +6 -13
  51. package/Application/StackScreen.tracking.ts +33 -11
  52. package/Application/StackScreen.tsx +54 -10
  53. package/Application/TooltipPortal.tsx +5 -12
  54. package/Application/WidgetContainer.tsx +4 -10
  55. package/Application/__tests__/KitAdoption.integration.test.tsx +253 -0
  56. package/Application/__tests__/KitAdoption.test.ts +283 -0
  57. package/Application/__tests__/KitAdoptionAuthorship.test.ts +132 -0
  58. package/Application/__tests__/KitAdoptionStore.test.ts +110 -0
  59. package/Application/__tests__/KitAdoptionStructural.test.ts +193 -0
  60. package/Application/__tests__/modalLayout.test.ts +128 -0
  61. package/Application/modalLayout.ts +14 -13
  62. package/Application/navigationOptions.tsx +17 -21
  63. package/Application/types.tabs.ts +1 -0
  64. package/Badge/Badge.tsx +0 -5
  65. package/Badge/BadgeDot.tsx +0 -1
  66. package/Badge/BadgeDotAnimation.tsx +7 -15
  67. package/Badge/BadgeRibbon.tsx +38 -46
  68. package/Badge/Shape.tsx +0 -2
  69. package/Badge/types.ts +2 -2
  70. package/Button/index.tsx +16 -5
  71. package/Button/utils.ts +15 -9
  72. package/CheckBox/index.tsx +0 -4
  73. package/Consts/theme.ts +10 -10
  74. package/Context/index.ts +1 -5
  75. package/Divider/DashDivider.tsx +0 -1
  76. package/Divider/index.tsx +0 -4
  77. package/FoundationList/index.tsx +0 -4
  78. package/Icon/index.tsx +6 -6
  79. package/Icon/types.ts +1 -1
  80. package/IconButton/index.tsx +12 -4
  81. package/Image/index.tsx +7 -9
  82. package/Input/Input.tsx +9 -8
  83. package/Input/InputDropDown.tsx +12 -10
  84. package/Input/InputMoney.tsx +8 -12
  85. package/Input/InputMoneyView.tsx +7 -14
  86. package/Input/InputOTP.tsx +33 -35
  87. package/Input/InputPhoneNumber.tsx +9 -8
  88. package/Input/InputSearch.tsx +6 -4
  89. package/Input/InputTextArea.tsx +0 -4
  90. package/Input/SearchTextTyping.tsx +0 -4
  91. package/Input/SystemTextInput.tsx +2 -7
  92. package/Input/TextTyping.tsx +0 -4
  93. package/Input/common.tsx +12 -26
  94. package/Input/index.tsx +20 -17
  95. package/Input/moneyUtils.ts +4 -10
  96. package/Input/styles.ts +5 -0
  97. package/Input/utils.ts +12 -14
  98. package/Layout/Card.tsx +13 -16
  99. package/Layout/FloatingButton.tsx +0 -4
  100. package/Layout/Item.tsx +2 -6
  101. package/Layout/ItemList.tsx +7 -9
  102. package/Layout/ItemSectionList.tsx +1 -2
  103. package/Layout/ReservedRegions.tsx +25 -25
  104. package/Layout/Screen.tsx +58 -56
  105. package/Layout/Section.tsx +13 -17
  106. package/Layout/TrackingScope.tsx +0 -1
  107. package/Layout/constants.ts +19 -0
  108. package/Layout/screenContent.tsx +2 -4
  109. package/Layout/screenFooter.tsx +18 -28
  110. package/Layout/screenHeader.tsx +31 -34
  111. package/Layout/screenQuickReturn.tsx +20 -17
  112. package/Layout/screenScroll.ts +7 -4
  113. package/Layout/screenTypes.ts +2 -3
  114. package/Layout/types.ts +41 -3
  115. package/Layout/useGridSystem.ts +49 -0
  116. package/Layout/utils.ts +37 -38
  117. package/Loader/DotLoader.tsx +0 -4
  118. package/Loader/Loader.tsx +0 -4
  119. package/Loader/ProgressBar.tsx +0 -1
  120. package/Loader/Spinner.tsx +0 -4
  121. package/Loader/utils.ts +7 -11
  122. package/Pagination/Dot.tsx +0 -4
  123. package/Pagination/PaginationDot.tsx +0 -1
  124. package/Pagination/PaginationNumber.tsx +3 -5
  125. package/Pagination/PaginationScroll.tsx +0 -5
  126. package/Pagination/PaginationWhiteDot.tsx +0 -1
  127. package/Pagination/index.tsx +0 -4
  128. package/Pagination/types.ts +2 -2
  129. package/Popup/PopupNotify.tsx +48 -44
  130. package/Popup/PopupPromotion.tsx +7 -4
  131. package/Popup/types.ts +4 -4
  132. package/Popup/utils.ts +0 -4
  133. package/Radio/index.tsx +0 -4
  134. package/Skeleton/index.tsx +0 -8
  135. package/Switch/index.tsx +0 -1
  136. package/Tag/index.tsx +1 -5
  137. package/Tag/types.ts +3 -3
  138. package/Text/index.tsx +3 -14
  139. package/Text/styles.ts +1 -1
  140. package/Text/types.ts +6 -1
  141. package/Text/utils.ts +4 -20
  142. package/Title/index.tsx +32 -41
  143. package/ViewBoundary/index.tsx +2 -4
  144. package/index.ts +6 -0
  145. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  import {
2
- BottomTabBarProps,
3
2
  BottomTabNavigationOptions,
4
3
  createBottomTabNavigator,
5
4
  } from '@react-navigation/bottom-tabs';
@@ -14,7 +13,6 @@ import {
14
13
  } from 'react-native';
15
14
  import { Icon } from '../../Icon';
16
15
  import { BottomTabProps } from '../types';
17
- import ScaleSizeProvider from '../ScaleSizeProvider';
18
16
  import BottomTabBar, { hasCenterAction } from './BottomTabBar';
19
17
  import { TabBarMetrics } from './metrics';
20
18
  import { getTabBadge } from './TabBarIcon';
@@ -25,8 +23,6 @@ import TabScreen from './TabScreen';
25
23
  import { ApplicationContext } from '../../Context';
26
24
  import { useScreenLayout } from '../../Layout';
27
25
 
28
- type SceneInterpolator = BottomTabNavigationOptions['sceneStyleInterpolator'];
29
-
30
26
  const Tab = createBottomTabNavigator();
31
27
 
32
28
  // the tab screens slide in index order over 280ms, CSS `ease`
@@ -36,11 +32,12 @@ const TAB_SWITCH_SPEC = {
36
32
  };
37
33
 
38
34
  /**
39
- * Tab slide interpolator. Scene progress is -1 for tabs before the active one and 1 for
40
- * tabs after it, so they wait a full width off screen on their side.
35
+ * scene progress is -1 for tabs before the active one, 1 for tabs after it: they wait a full
36
+ * width off screen on their side
41
37
  */
42
- const getTabSlide = (width: number): SceneInterpolator => {
43
- return ({ current }) => ({
38
+ const getTabSlide =
39
+ (width: number): BottomTabNavigationOptions['sceneStyleInterpolator'] =>
40
+ ({ current }) => ({
44
41
  sceneStyle: {
45
42
  transform: [
46
43
  {
@@ -52,7 +49,6 @@ const getTabSlide = (width: number): SceneInterpolator => {
52
49
  ],
53
50
  },
54
51
  });
55
- };
56
52
 
57
53
  /**
58
54
  * App tab navigator: the floating bar on phones, the rail or floating bar on
@@ -71,11 +67,11 @@ const BottomTab: React.FC<BottomTabProps> = ({
71
67
  const { isIPhoneDuo } = useScreenLayout();
72
68
  const { width: windowWidth } = useWindowDimensions();
73
69
  const barInset = useDuoTabBarInset();
74
- // The center action's button rises above the bar; tab content leaves room for it too.
75
- let tabBarInset = barInset;
76
- if (barInset && hasCenterAction(floatingButton, tabs.length)) {
77
- tabBarInset = barInset + TabBarMetrics.ctaLift;
78
- }
70
+ // the center action's button rises above the bar, tab content leaves room for it too
71
+ const tabBarInset =
72
+ barInset && hasCenterAction(floatingButton, tabs.length)
73
+ ? barInset + TabBarMetrics.ctaLift
74
+ : barInset;
79
75
  const initialIndex = tabs.findIndex(i => i.name === initialRouteName);
80
76
  const activeIndex = React.useRef(initialIndex > 0 ? initialIndex : 0);
81
77
 
@@ -87,15 +83,15 @@ const BottomTab: React.FC<BottomTabProps> = ({
87
83
  });
88
84
  }, [navigation]);
89
85
 
90
- // Focus, not mount/onFocus: popping back from a pushed screen only refocuses BottomTab.
86
+ // Focus, not mount: pushing a screen from a tab leaves BottomTab mounted but blurred, and
87
+ // onFocus only fires on tab changes.
91
88
  useFocusEffect(
92
89
  React.useCallback(() => {
93
90
  NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(
94
91
  activeIndex.current !== 0,
95
92
  );
96
- return () => {
93
+ return () =>
97
94
  NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(false);
98
- };
99
95
  }, []),
100
96
  );
101
97
 
@@ -132,21 +128,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
132
128
  },
133
129
  };
134
130
 
135
- // One cap for the whole bar (labels, CTA, custom content) so it fits the fixed bar and notch.
136
- const renderTabBar = (props: BottomTabBarProps) => {
137
- const TabBar = isIPhoneDuo ? DuoTabBar : BottomTabBar;
138
- return (
139
- <ScaleSizeProvider scaleSizeMaxRate={TabBarMetrics.labelMaxScale}>
140
- <TabBar
141
- {...props}
142
- floatingButton={floatingButton}
143
- inactiveTintColor={theme.colors.text.secondary}
144
- activeTintColor={theme.colors.primary}
145
- />
146
- </ScaleSizeProvider>
147
- );
148
- };
149
-
150
131
  return (
151
132
  <DuoBarCompressionProvider
152
133
  behavior={compressionBehavior}
@@ -164,7 +145,23 @@ const BottomTab: React.FC<BottomTabProps> = ({
164
145
  transitionSpec: TAB_SWITCH_SPEC,
165
146
  sceneStyleInterpolator: getTabSlide(windowWidth),
166
147
  }}
167
- tabBar={renderTabBar}
148
+ tabBar={props =>
149
+ isIPhoneDuo ? (
150
+ <DuoTabBar
151
+ {...props}
152
+ floatingButton={floatingButton}
153
+ inactiveTintColor={theme.colors.text.secondary}
154
+ activeTintColor={theme.colors.primary}
155
+ />
156
+ ) : (
157
+ <BottomTabBar
158
+ {...props}
159
+ floatingButton={floatingButton}
160
+ inactiveTintColor={theme.colors.text.secondary}
161
+ activeTintColor={theme.colors.primary}
162
+ />
163
+ )
164
+ }
168
165
  initialRouteName={initialRouteName}
169
166
  >
170
167
  {tabs.map((item, index) => (
@@ -3,41 +3,34 @@ import { Easing } from 'react-native-reanimated';
3
3
  import { Colors, Radius, Spacing } from '../../Consts';
4
4
 
5
5
  /**
6
- * Navigation bar measurements (Figma "Specs Navigation", DS-984), in sync with momo-native-kits
7
- * `TabBarMetrics`: the horizontal floating bar (center action in a notch) and the vertical
8
- * iPhone Duo rail (center action under the container).
6
+ * Navigation bar measurements (Figma "Specs Navigation", DS-984), kept in sync with
7
+ * momo-native-kits `TabBarMetrics`
8
+ * - horizontal: floating container with items split evenly, center action in a notch
9
+ * - vertical: iPhone Duo rail, center action under the container
9
10
  */
10
11
  const TabBarMetrics = {
11
- height: 60,
12
+ height: 68,
12
13
  margin: Spacing.S,
13
- paddingVertical: Spacing.XS,
14
+ paddingVertical: Spacing.S,
14
15
  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,
16
+ itemGap: Spacing.XS,
23
17
  iconSize: 24,
24
18
  // active indicator behind the icon: 24 icon + XS·M padding
25
19
  indicatorPaddingVertical: Spacing.XS,
26
20
  indicatorPaddingHorizontal: Spacing.M,
27
21
  indicatorRadius: Radius.XL,
28
22
  indicatorHeight: 32,
29
- labelMaxScale: 1.2,
30
- labelHeight: 18,
31
- railLabelHeight: 14,
23
+ // home indicator / gesture bar area under the container
32
24
  systemIndicatorHeight: 24,
25
+ // unfolded foldables (short side from 600, the smallest in the spec) use 65% of the short
26
+ // side, centered; phones and folded outer displays fill the window
33
27
  foldableMinShortSide: 600,
34
28
  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
29
  ctaSlotWidth: 92,
39
30
  ctaSize: 48,
31
+ // the icon sits 10 inside the 48 button (prototype .cta: 28 icon), 12 inside the 52 rail one
40
32
  ctaIconSize: 28,
33
+ // the button rises 22 above the container top edge
41
34
  ctaLift: 22,
42
35
  ctaLabelGap: Spacing.M,
43
36
  ctaLabelHeight: 18,
@@ -58,9 +51,10 @@ const TabBarMetrics = {
58
51
  };
59
52
 
60
53
  /**
61
- * Navigation bar motion (MoMo Navigation Prototype): the active indicator slides to the
62
- * selected item with a little overshoot, icon and label tints cross-fade, and the center
63
- * action shrinks while pressed.
54
+ * motion of the navigation bar (MoMo Navigation Prototype):
55
+ * - one active indicator slides to the selected item, overshooting a little
56
+ * - icons cross-fade and the label color follows
57
+ * - the center action shrinks while pressed
64
58
  */
65
59
  const TabBarMotion = {
66
60
  indicator: { duration: 550, easing: Easing.bezier(0.34, 1.4, 0.5, 1) },
@@ -91,7 +85,7 @@ const CTA_SHADOW: BoxShadowValue[] = [
91
85
  ];
92
86
 
93
87
  /**
94
- * Inner light and tint of the active indicator: white 12% from below, primary 12% from above.
88
+ * inner light and tint of the active indicator: white 12% from below, primary 12% from above
95
89
  */
96
90
  const getIndicatorShadow = (primary: string): BoxShadowValue[] => [
97
91
  {
@@ -105,9 +99,9 @@ const getIndicatorShadow = (primary: string): BoxShadowValue[] => [
105
99
  ];
106
100
 
107
101
  /**
108
- * Notch dipped into the container top edge under the center action (Figma "Backgroud" path):
102
+ * notch dipped into the container top edge under the center action (Figma "Backgroud" path):
109
103
  * starts NOTCH_HALF_WIDTH left of the center, then cubic segments as [control1, control2, end],
110
- * each point [dx from the center, dy below the top edge].
104
+ * each point [dx from the center, dy below the top edge]
111
105
  */
112
106
  const NOTCH_HALF_WIDTH = 50.758;
113
107
  const NOTCH_CURVES: [number, number][][] = [
@@ -134,25 +128,15 @@ const NOTCH_CURVES: [number, number][][] = [
134
128
  ];
135
129
 
136
130
  /**
137
- * Width of the horizontal bar: 65% of the short side on an unfolded foldable, otherwise the
138
- * window minus the side safe areas and margins.
131
+ * width of the horizontal bar: 65% of the short side on an unfolded foldable, otherwise the
132
+ * window minus the side safe areas and margins
139
133
  */
140
134
  const getHorizontalBarWidth = (
141
135
  width: number,
142
136
  height: number,
143
137
  insetLeft: number,
144
138
  insetRight: number,
145
- itemCount: number,
146
- hasCta: boolean,
147
139
  ) => {
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
140
  const shortSide = Math.min(width, height);
157
141
  if (shortSide >= TabBarMetrics.foldableMinShortSide) {
158
142
  return shortSide * TabBarMetrics.foldableWidthRatio;
@@ -161,16 +145,12 @@ const getHorizontalBarWidth = (
161
145
  };
162
146
 
163
147
  /**
164
- * Height of the vertical rail container, plus the center action under it.
148
+ * height of the vertical rail container, plus the center action under it
165
149
  */
166
- const getRailHeight = (tabCount: number, hasCta: boolean) => {
167
- const itemsHeight = tabCount * TabBarMetrics.railItemHeight;
168
- let ctaHeight = 0;
169
- if (hasCta) {
170
- ctaHeight = TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight;
171
- }
172
- return itemsHeight + TabBarMetrics.railPaddingBottom + ctaHeight;
173
- };
150
+ const getRailHeight = (tabCount: number, hasCta: boolean) =>
151
+ tabCount * TabBarMetrics.railItemHeight +
152
+ TabBarMetrics.railPaddingBottom +
153
+ (hasCta ? TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight : 0);
174
154
 
175
155
  export {
176
156
  TabBarMetrics,
@@ -2,7 +2,7 @@ import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
2
2
  import { CommonActions, Route } from '@react-navigation/native';
3
3
 
4
4
  /**
5
- * Navigation items of the tab bar, with the press handlers of the system tab bar.
5
+ * navigation items of the tab bar, with the press handlers of the system tab bar
6
6
  */
7
7
  const getTabItems = ({ state, descriptors, navigation }: BottomTabBarProps) => {
8
8
  return state.routes.map((route: Route<string>, index: number) => {
@@ -6,11 +6,11 @@ import {
6
6
  } from 'react-native-reanimated';
7
7
 
8
8
  const SPRING = { damping: 18, stiffness: 220, mass: 0.8 };
9
+ // the button grows by a tenth while held
9
10
  const PRESSED_SCALE = 1.1;
10
11
 
11
12
  /**
12
- * Press lift for a single button (e.g. the compressed rail): it scales up while held,
13
- * unless the OS asks for reduced motion.
13
+ * lift for a single button, e.g. the compressed rail
14
14
  */
15
15
  const usePressLift = () => {
16
16
  const reduceMotion = useReducedMotion();
@@ -17,10 +17,6 @@ type BackgroundImageViewProps = {
17
17
  animatedValue: SharedValue<number>;
18
18
  };
19
19
 
20
- /**
21
- * Header surface (shadow or divider) and image, fading in over the first 52pt of
22
- * scroll. iOS keeps the surface out of the clipped view so its shadow isn't cut.
23
- */
24
20
  const BackgroundImageView: FC<BackgroundImageViewProps> = ({
25
21
  useShadowHeader = true,
26
22
  headerBackground = null,
@@ -30,7 +30,6 @@ const DuoGlassBackground: React.FC<{
30
30
  const { headerHeight } = useScreenLayout();
31
31
  const sharedValue = useNormalizedSharedValue(animatedValue);
32
32
  const fadeStart = Math.max(bannerBottom - headerHeight, 0);
33
- const translucentColor = theme.colors.background.surface + TRANSLUCENT_ALPHA;
34
33
 
35
34
  const fadeStyle = useAnimatedStyle(() => ({
36
35
  opacity: interpolate(
@@ -48,7 +47,8 @@ const DuoGlassBackground: React.FC<{
48
47
  StyleSheet.absoluteFill,
49
48
  styles.translucentBar,
50
49
  {
51
- backgroundColor: translucentColor,
50
+ backgroundColor:
51
+ theme.colors.background.surface + TRANSLUCENT_ALPHA,
52
52
  borderBottomColor: theme.colors.border.default,
53
53
  },
54
54
  fadeStyle,
@@ -59,9 +59,8 @@ const DuoGlassBackground: React.FC<{
59
59
  };
60
60
 
61
61
  /**
62
- * Header gradient (and theme image) drawn under the content, fading out on scroll:
63
- * header-high, or 52pt taller and collapsing when `extended`. `followScroll`
64
- * scrolls it away with the content instead of fading it.
62
+ * pink header gradient drawn under the content, fading out on scroll as main's extended
63
+ * header does: header-high for default headers, 52 taller (collapsing) for extended
65
64
  */
66
65
  const DuoScrollGradient: React.FC<{
67
66
  animatedValue?: AnimatedCompatValue;
@@ -19,10 +19,6 @@ const ASPECT_RATIOS: Record<
19
19
  '1/1': 1 / 1,
20
20
  };
21
21
 
22
- /**
23
- * Parallax header image (or `children`): stretches when pulled down and fades out
24
- * as the content scrolls up.
25
- */
26
22
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
27
23
  animatedValue,
28
24
  image,
@@ -9,10 +9,6 @@ import { Image } from '../../Image';
9
9
  import BackgroundImageView from './BackgroundImageView';
10
10
  import { useNormalizedSharedValue } from '../hooks';
11
11
 
12
- /**
13
- * Header background: surface and image that fade in on scroll, with an optional
14
- * gradient (custom or theme) on top. Outlined when `showBaseLineDebug` is on.
15
- */
16
12
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
17
13
  animatedValue,
18
14
  useGradient = true,
@@ -23,10 +23,6 @@ const SCREEN_PADDING = 12;
23
23
  const BACK_WIDTH = 28;
24
24
  const TOOLKIT_WIDTH = 73;
25
25
 
26
- /**
27
- * Extended header background: a gradient that collapses on scroll and, with
28
- * `inputSearchProps`, a full-width search that slides into the bar beside back/toolkit.
29
- */
30
26
  const HeaderExtendHeader: React.FC<{
31
27
  headerType?: HeaderType;
32
28
  animatedValue: AnimatedCompatValue;
@@ -68,15 +64,18 @@ const HeaderExtendHeader: React.FC<{
68
64
  const contentLeft = layout.insetLeft + SCREEN_PADDING;
69
65
  const contentRight = layout.insetLeft + layout.contentWidth;
70
66
  // collapsed on the status bar row, the search stays between its clusters
71
- const clearanceLeft = layout.statusBarClearance?.left ?? 0;
72
- const clearanceRight = layout.statusBarClearance?.right ?? 0;
73
- const collapsedLeft = layout.insetLeft + leftPosition + clearanceLeft;
74
- const collapsedRight = contentRight - clearanceRight - headerRightWidth;
67
+ const collapsedLeft =
68
+ layout.insetLeft + leftPosition + (layout.statusBarClearance?.left ?? 0);
69
+ const collapsedRight = contentRight - (layout.statusBarClearance?.right ?? 0);
75
70
  const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
76
- const collapsedSearchWidth = collapsedRight - collapsedLeft - SCREEN_PADDING;
71
+ const collapsedSearchWidth =
72
+ collapsedRight - collapsedLeft - SCREEN_PADDING - headerRightWidth;
77
73
  // a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
78
- const titleRowHeight = layout.headerHeight - layout.headerStatusBarHeight;
79
- const collapsedRaise = (titleRowHeight - HeaderMetrics.barHeight) / 2;
74
+ const collapsedRaise =
75
+ (layout.headerHeight -
76
+ layout.headerStatusBarHeight -
77
+ HeaderMetrics.barHeight) /
78
+ 2;
80
79
 
81
80
  let useShadowHeader = useShadowHeaderProp;
82
81
  if (inputSearchProps && Platform.OS === 'android') {
@@ -10,8 +10,7 @@ import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
10
10
  import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
11
11
 
12
12
  /**
13
- * Header back button, also bound to the Android hardware back. `preventBack`
14
- * confirms in a popup first; the iPhone Duo layouts use the 48pt side bar button.
13
+ * default header left used for nav
15
14
  */
16
15
  const HeaderLeft: React.FC<HeaderBackProps> = ({
17
16
  tintColor,
@@ -9,8 +9,7 @@ import { HeaderToolkitAction } from './HeaderToolkitAction';
9
9
  import { styles } from './HeaderRight.styles';
10
10
 
11
11
  /**
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).
12
+ * main component for header right
14
13
  */
15
14
  const HeaderRight: React.FC<any> = props => {
16
15
  const { translate } = useContext(ApplicationContext);
@@ -4,10 +4,6 @@ import { Spacing } from '../../Consts';
4
4
  import { isWhiteListDesignSystem } from '../utils';
5
5
  import { useScreenLayout } from '../../Layout';
6
6
 
7
- /**
8
- * Header title font: MoMoTrustDisplay for design-system whitelisted mini apps,
9
- * SFProText otherwise.
10
- */
11
7
  const useHeaderFontFamily = (): string => {
12
8
  const context = useContext<any>(MiniAppContext);
13
9
  return isWhiteListDesignSystem(context) ? 'MoMoTrustDisplay' : 'SFProText';
@@ -24,22 +20,24 @@ const HEADER_ACTIONS_WIDTH = 172;
24
20
  const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
25
21
 
26
22
  /**
27
- * Max header title width. The iPhone Duo side bar takes back and toolkit out of
28
- * the bar, so the title gets the content width; otherwise it shares the bar with them.
23
+ * max title width, the iPhone Duo side bar takes back and toolkit out of the bar
29
24
  */
30
25
  const useTitleMaxWidth = () => {
31
- const layout = useScreenLayout();
32
- const { width, contentWidth, sideBar, statusBarClearance } = layout;
26
+ const { width, contentWidth, sideBar, statusBarClearance } =
27
+ useScreenLayout();
33
28
  let clearance = 0;
34
- let rowActionsExtra = 0;
35
29
  if (statusBarClearance) {
36
30
  clearance = statusBarClearance.left + statusBarClearance.right;
37
- rowActionsExtra = STATUS_BAR_ROW_ACTIONS_EXTRA;
38
31
  }
39
32
  if (sideBar) {
40
33
  return contentWidth - Spacing.M * 2 - clearance;
41
34
  }
42
- return width - HEADER_ACTIONS_WIDTH - rowActionsExtra - clearance;
35
+ return (
36
+ width -
37
+ HEADER_ACTIONS_WIDTH -
38
+ (statusBarClearance ? STATUS_BAR_ROW_ACTIONS_EXTRA : 0) -
39
+ clearance
40
+ );
43
41
  };
44
42
 
45
43
  export { useHeaderFontFamily, HEADER_ACTIONS_WIDTH, useTitleMaxWidth };
@@ -33,8 +33,7 @@ type HeaderTitleExtraProps = {
33
33
  };
34
34
 
35
35
  /**
36
- * Default navigation header title. With `animatedValue` its opacity follows the
37
- * scroll through `interpolate` (0 to 1 over the first 200pt by default).
36
+ * default header title used for nav
38
37
  */
39
38
  const HeaderTitle: React.FC<
40
39
  HeaderTitleExtraProps & { [key: string]: any }
@@ -101,8 +100,8 @@ const HeaderTitle: React.FC<
101
100
  };
102
101
 
103
102
  /**
104
- * Header title picked by `type` (user, location or journey) with `data` as its
105
- * props; an empty view for an unknown type.
103
+ * Title custom for header
104
+ * @constructor
106
105
  */
107
106
  const TitleCustom: React.FC<any> = ({ type, data, ...props }) => {
108
107
  switch (type) {
@@ -14,8 +14,7 @@ const CLOSE_ICON = '16_navigation_close_circle';
14
14
  const HOME_ICON = '16_basic_home';
15
15
 
16
16
  /**
17
- * Header toolkit: optional shortcut plus the help center / close capsule. When
18
- * `vertical` (iPhone Duo side bar) it reports its height and may fold into one button.
17
+ * Header toolkit action
19
18
  */
20
19
  const HeaderToolkitAction: React.FC<any> = ({
21
20
  tintColor,
@@ -28,9 +27,8 @@ const HeaderToolkitAction: React.FC<any> = ({
28
27
  vertical = false,
29
28
  }) => {
30
29
  const context = useContext<any>(MiniAppContext);
31
- const compression = useDuoBarCompression();
32
- const { toolbarCompressed, toolbarHidden } = compression;
33
- const { expandToolbar, setToolbarHeight } = compression;
30
+ const { toolbarCompressed, toolbarHidden, expandToolbar, setToolbarHeight } =
31
+ useDuoBarCompression();
34
32
  const { statusBarClearance } = useScreenLayout();
35
33
  const {
36
34
  isLoading,
@@ -58,22 +56,18 @@ const HeaderToolkitAction: React.FC<any> = ({
58
56
 
59
57
  const useHelpCenter = context.appId !== 'vn.momo.helpcenter';
60
58
  const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
61
- let toolkitWidthStyle: ViewStyle = styles.toolkitNarrow;
62
- if (useHelpCenter) {
63
- toolkitWidthStyle = styles.toolkitWide;
64
- }
65
59
 
66
60
  const groupCount = useHelpCenter ? 2 : 1;
67
61
  let capsuleHeight = DuoMetrics.controlSize;
68
62
  if (groupCount > 1) {
69
63
  capsuleHeight = groupCount * DuoMetrics.itemPitch;
70
64
  }
71
- const backSlotHeight = DuoMetrics.controlSize + Spacing.M;
72
- let shortcutHeight = 0;
73
- if (useShortcut) {
74
- shortcutHeight = DuoMetrics.controlSize + Spacing.M;
75
- }
76
- const fullToolbarHeight = backSlotHeight + shortcutHeight + capsuleHeight;
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;
77
71
 
78
72
  useEffect(() => {
79
73
  if (vertical) {
@@ -86,7 +80,8 @@ const HeaderToolkitAction: React.FC<any> = ({
86
80
  return null;
87
81
  }
88
82
 
89
- // HIG "Toolbar compressed": a menu icon (not the "more" one) that expands the toolbar
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
85
  if (vertical && toolbarCompressed) {
91
86
  return (
92
87
  <SideBarButton
@@ -175,7 +170,15 @@ const HeaderToolkitAction: React.FC<any> = ({
175
170
  showBadge={showBadge}
176
171
  />
177
172
  )}
178
- <View style={[styles.toolkitContainer, buttonStyle, toolkitWidthStyle]}>
173
+ <View
174
+ style={[
175
+ styles.toolkitContainer,
176
+ buttonStyle,
177
+ context.appId !== 'vn.momo.helpcenter'
178
+ ? styles.toolkitWide
179
+ : styles.toolkitNarrow,
180
+ ]}
181
+ >
179
182
  {useHelpCenter && (
180
183
  <>
181
184
  <TouchableOpacity
@@ -7,8 +7,7 @@ import { Icon } from '../../Icon';
7
7
  import { BadgeDot } from '../../Badge';
8
8
 
9
9
  /**
10
- * Round 28pt icon button of the header (back, shortcut) with an optional badge
11
- * dot; switches to a dark fill under a white tint so the icon stays readable.
10
+ * default navigation button used header nav
12
11
  */
13
12
  const NavigationButton: React.FC<NavigationButtonProps> = ({
14
13
  icon,
@@ -10,10 +10,6 @@ import { useNormalizedSharedValue } from '../hooks';
10
10
  import { useScreenLayout } from '../../Layout';
11
11
  import { DuoMetrics } from '../../Layout/utils';
12
12
 
13
- /**
14
- * Header search bar whose fill turns from surface to default once scrolled, with
15
- * a cancel button that goes back (or dismisses the mini app) unless `renderButtons`.
16
- */
17
13
  const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
18
14
  (
19
15
  {
@@ -40,9 +36,9 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
40
36
  if (layout.sideBar) {
41
37
  searchMaxWidth = layout.contentWidth - Spacing.M * 2 - headerRightWidth;
42
38
  }
43
- const clearance = layout.statusBarClearance;
44
- if (clearance) {
45
- searchMaxWidth -= clearance.left + clearance.right;
39
+ if (layout.statusBarClearance) {
40
+ searchMaxWidth -=
41
+ layout.statusBarClearance.left + layout.statusBarClearance.right;
46
42
  }
47
43
 
48
44
  const backgroundStyle = useAnimatedStyle(() => {
@@ -80,15 +76,6 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
80
76
 
81
77
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
82
78
 
83
- let buttons: React.ReactNode = (
84
- <TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
85
- <Text typography={'action_default_bold'}>{buttonText}</Text>
86
- </TouchableOpacity>
87
- );
88
- if (typeof renderButtons === 'function') {
89
- buttons = renderButtons();
90
- }
91
-
92
79
  return (
93
80
  <View
94
81
  style={[styles.container, showBaseLineDebug && styles.debugBaseLine]}
@@ -110,7 +97,13 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
110
97
  />
111
98
  </Animated.View>
112
99
  </View>
113
- {buttons}
100
+ {renderButtons && typeof renderButtons === 'function' ? (
101
+ renderButtons()
102
+ ) : (
103
+ <TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
104
+ <Text typography={'action_default_bold'}>{buttonText}</Text>
105
+ </TouchableOpacity>
106
+ )}
114
107
  </View>
115
108
  );
116
109
  },
@@ -14,8 +14,8 @@ import {
14
14
  } from './HeaderTitle.shared';
15
15
 
16
16
  /**
17
- * Header title for a journey: `start` and `end` around an icon, with an optional
18
- * description below; a shimmer while `isLoading`.
17
+ * Header user title
18
+ * @constructor
19
19
  */
20
20
  const TitleJourney: React.FC<TitleJourneyProps> = ({
21
21
  start,
@@ -9,8 +9,8 @@ import { styles } from './HeaderTitle.styles';
9
9
  import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
10
10
 
11
11
  /**
12
- * Header title showing a location with a dropdown chevron and an optional
13
- * description above it; a shimmer while `isLoading`.
12
+ * Header user title
13
+ * @constructor
14
14
  */
15
15
  const TitleLocation: React.FC<TitleLocationProps> = ({
16
16
  tintColor,
@@ -10,8 +10,8 @@ import { styles } from './HeaderTitle.styles';
10
10
  import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
11
11
 
12
12
  /**
13
- * Header title for a user or group: avatar (1 to 4 images, a `+N` stack, or name
14
- * initials), title with verified icons and subtitle; a shimmer while `isLoading`.
13
+ * Header user title
14
+ * @constructor
15
15
  */
16
16
  const TitleUser: React.FC<TitleUserProps> = ({
17
17
  title,