@momo-kits/foundation 0.169.5 → 0.169.6-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +34 -44
  3. package/Application/BottomSheet.tsx +26 -35
  4. package/Application/BottomTab/ActiveIndicator.tsx +14 -15
  5. package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
  6. package/Application/BottomTab/BottomTabBar.tsx +40 -63
  7. package/Application/BottomTab/BottomTabInset.tsx +4 -8
  8. package/Application/BottomTab/CurvedContainer.tsx +20 -16
  9. package/Application/BottomTab/CustomBottomTabItem.tsx +13 -16
  10. package/Application/BottomTab/DuoBarCompression.tsx +19 -13
  11. package/Application/BottomTab/DuoRailTabBar.tsx +13 -17
  12. package/Application/BottomTab/DuoTabBar.tsx +24 -12
  13. package/Application/BottomTab/TabBarIcon.tsx +8 -13
  14. package/Application/BottomTab/TabScreen.tsx +5 -1
  15. package/Application/BottomTab/index.tsx +30 -38
  16. package/Application/BottomTab/metrics.ts +46 -24
  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/Localize.ts +0 -7
  36. package/Application/ModalScreen.tsx +3 -9
  37. package/Application/Navigation.ts +16 -14
  38. package/Application/NavigationContainer.tsx +16 -19
  39. package/Application/Navigator.ts +48 -20
  40. package/Application/StackScreen.tracking.ts +26 -11
  41. package/Application/StackScreen.tsx +35 -8
  42. package/Application/TooltipPortal.tsx +5 -12
  43. package/Application/WidgetContainer.tsx +4 -10
  44. package/Application/__tests__/modalLayout.test.ts +128 -0
  45. package/Application/modalLayout.ts +14 -13
  46. package/Application/navigationOptions.tsx +17 -21
  47. package/Application/types.tabs.ts +0 -1
  48. package/Badge/Badge.tsx +0 -5
  49. package/Badge/BadgeDot.tsx +0 -1
  50. package/Badge/BadgeDotAnimation.tsx +7 -15
  51. package/Badge/BadgeRibbon.tsx +38 -46
  52. package/Badge/Shape.tsx +0 -2
  53. package/Badge/types.ts +2 -2
  54. package/Button/index.tsx +16 -5
  55. package/Button/utils.ts +15 -9
  56. package/CheckBox/index.tsx +0 -4
  57. package/Consts/theme.ts +10 -10
  58. package/Context/index.ts +0 -1
  59. package/Divider/DashDivider.tsx +0 -1
  60. package/Divider/index.tsx +0 -4
  61. package/FoundationList/index.tsx +0 -4
  62. package/Icon/index.tsx +6 -6
  63. package/Icon/types.ts +1 -1
  64. package/IconButton/index.tsx +12 -4
  65. package/Image/index.tsx +7 -9
  66. package/Input/Input.tsx +9 -8
  67. package/Input/InputDropDown.tsx +12 -10
  68. package/Input/InputMoney.tsx +8 -12
  69. package/Input/InputMoneyView.tsx +7 -14
  70. package/Input/InputOTP.tsx +33 -35
  71. package/Input/InputPhoneNumber.tsx +9 -8
  72. package/Input/InputSearch.tsx +6 -4
  73. package/Input/InputTextArea.tsx +0 -4
  74. package/Input/SearchTextTyping.tsx +0 -4
  75. package/Input/SystemTextInput.tsx +2 -7
  76. package/Input/TextTyping.tsx +0 -4
  77. package/Input/common.tsx +12 -26
  78. package/Input/index.tsx +20 -17
  79. package/Input/moneyUtils.ts +4 -10
  80. package/Input/styles.ts +5 -0
  81. package/Input/utils.ts +12 -14
  82. package/Layout/Card.tsx +4 -7
  83. package/Layout/FloatingButton.tsx +0 -4
  84. package/Layout/Item.tsx +2 -6
  85. package/Layout/ItemList.tsx +7 -9
  86. package/Layout/ItemSectionList.tsx +1 -2
  87. package/Layout/ReservedRegions.tsx +25 -25
  88. package/Layout/Screen.tsx +58 -56
  89. package/Layout/Section.tsx +5 -8
  90. package/Layout/TrackingScope.tsx +0 -1
  91. package/Layout/screenContent.tsx +2 -4
  92. package/Layout/screenFooter.tsx +18 -28
  93. package/Layout/screenHeader.tsx +31 -34
  94. package/Layout/screenQuickReturn.tsx +20 -17
  95. package/Layout/screenScroll.ts +7 -4
  96. package/Layout/screenTypes.ts +2 -3
  97. package/Layout/types.ts +4 -2
  98. package/Layout/utils.ts +37 -31
  99. package/Loader/DotLoader.tsx +0 -4
  100. package/Loader/Loader.tsx +0 -4
  101. package/Loader/ProgressBar.tsx +0 -1
  102. package/Loader/Spinner.tsx +0 -4
  103. package/Loader/utils.ts +7 -11
  104. package/Pagination/Dot.tsx +0 -4
  105. package/Pagination/PaginationDot.tsx +0 -1
  106. package/Pagination/PaginationNumber.tsx +3 -5
  107. package/Pagination/PaginationScroll.tsx +0 -5
  108. package/Pagination/PaginationWhiteDot.tsx +0 -1
  109. package/Pagination/index.tsx +0 -4
  110. package/Pagination/types.ts +2 -2
  111. package/Popup/PopupNotify.tsx +48 -44
  112. package/Popup/PopupPromotion.tsx +7 -4
  113. package/Popup/types.ts +4 -4
  114. package/Popup/utils.ts +0 -4
  115. package/Radio/index.tsx +0 -4
  116. package/Skeleton/index.tsx +0 -8
  117. package/Switch/index.tsx +0 -1
  118. package/Tag/index.tsx +1 -5
  119. package/Tag/types.ts +3 -3
  120. package/Text/index.tsx +3 -14
  121. package/Text/styles.ts +1 -1
  122. package/Text/types.ts +6 -1
  123. package/Text/utils.ts +0 -9
  124. package/Title/index.tsx +32 -41
  125. package/ViewBoundary/index.tsx +2 -4
  126. package/index.ts +6 -0
  127. package/package.json +1 -1
@@ -10,10 +10,10 @@ import Animated, {
10
10
  useSharedValue,
11
11
  withTiming,
12
12
  } from 'react-native-reanimated';
13
- import { FixedScaleText, TabBarIcon } from './TabBarIcon';
13
+ import { TabBarIcon } from './TabBarIcon';
14
14
  import { Text } from '../../Text';
15
15
  import { MiniAppContext } from '../../Context';
16
- import { Colors, Spacing } from '../../Consts';
16
+ import { Colors } from '../../Consts';
17
17
  import { TabBarMetrics, TabBarMotion } from './metrics';
18
18
 
19
19
  type IconRenderer = (props: {
@@ -31,14 +31,12 @@ type TabItemContentProps = {
31
31
  badge?: string | number;
32
32
  // rail item (.Nav/Duo/Item) instead of the horizontal one (.Nav/itemTab)
33
33
  vertical?: boolean;
34
- // keep the label at its design size, the Duo displays otherwise scale it up
35
- fixedFontScale?: boolean;
36
34
  hideLabel?: boolean;
37
35
  };
38
36
 
39
37
  /**
40
- * Tab item body: the icon in its indicator slot with the label under it. The bar slides one
41
- * shared active indicator behind the slots; icon and label tints cross-fade on focus.
38
+ * icon in its indicator slot, label under it. The bar slides one shared active indicator
39
+ * behind the slots; the icon and label tint cross-fade on focus
42
40
  */
43
41
  const TabItemContent: React.FC<TabItemContentProps> = ({
44
42
  label,
@@ -48,7 +46,6 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
48
46
  icon,
49
47
  badge,
50
48
  vertical,
51
- fixedFontScale,
52
49
  hideLabel,
53
50
  }) => {
54
51
  const progress = useSharedValue(focused ? 1 : 0);
@@ -72,7 +69,7 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
72
69
  </Text>
73
70
  );
74
71
 
75
- // The active label fades in over the inactive one.
72
+ // the active label fades in over the inactive one
76
73
  const text = (
77
74
  <View style={!vertical && styles.labelBox}>
78
75
  {renderText(inactiveTintColor)}
@@ -85,11 +82,6 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
85
82
  </View>
86
83
  );
87
84
 
88
- let labelNode = text;
89
- if (fixedFontScale) {
90
- labelNode = <FixedScaleText>{text}</FixedScaleText>;
91
- }
92
-
93
85
  return (
94
86
  <>
95
87
  <View style={vertical ? styles.railSlot : styles.slot}>
@@ -102,7 +94,7 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
102
94
  renderIcon={icon}
103
95
  />
104
96
  </View>
105
- {!hideLabel && labelNode}
97
+ {!hideLabel && text}
106
98
  </>
107
99
  );
108
100
  };
@@ -116,7 +108,7 @@ type CustomBottomTabItemProps = TabItemContentProps & {
116
108
  };
117
109
 
118
110
  /**
119
- * Horizontal navigation item (.Nav/itemTab), filling an even share of the container.
111
+ * horizontal navigation item (.Nav/itemTab): fills an even share of the container
120
112
  */
121
113
  const CustomBottomTabItem: React.FC<CustomBottomTabItemProps> = ({
122
114
  onPress,
@@ -151,7 +143,7 @@ const styles = StyleSheet.create({
151
143
  minWidth: 1,
152
144
  alignItems: 'center',
153
145
  justifyContent: 'center',
154
- gap: Spacing.XXS,
146
+ gap: TabBarMetrics.itemIconLabelGap,
155
147
  },
156
148
  slot: {
157
149
  height: TabBarMetrics.indicatorHeight,
@@ -170,6 +162,11 @@ const styles = StyleSheet.create({
170
162
  },
171
163
  labelBox: {
172
164
  alignSelf: 'stretch',
165
+ // fixed to the design line height so a capped-scale label overflows downward and the
166
+ // icon above it stays put
167
+ height: TabBarMetrics.labelHeight,
168
+ justifyContent: 'flex-start',
169
+ overflow: 'visible',
173
170
  },
174
171
  label: {
175
172
  textAlign: 'center',
@@ -28,9 +28,9 @@ type DuoBarCompression = {
28
28
 
29
29
  // space kept between the top and the bottom groups (HIG Vertical controls)
30
30
  const GROUP_GAP = Spacing.M;
31
- const BACK_SLOT_HEIGHT = DuoMetrics.controlSize + Spacing.M;
32
31
  // Back, then the right toolkit: help + home capsule
33
- const DEFAULT_TOOLBAR_HEIGHT = BACK_SLOT_HEIGHT + 2 * DuoMetrics.itemPitch;
32
+ const DEFAULT_TOOLBAR_HEIGHT =
33
+ DuoMetrics.controlSize + Spacing.M + 2 * DuoMetrics.itemPitch;
34
34
  // Back, then the single button the toolbar folds into
35
35
  const COMPRESSED_TOOLBAR_HEIGHT = 2 * DuoMetrics.controlSize + Spacing.M;
36
36
 
@@ -39,12 +39,16 @@ const NO_COMPRESSION: DuoBarCompression = {
39
39
  tabBarCompressed: false,
40
40
  };
41
41
 
42
- const DuoBarCompressionContext = createContext(NO_COMPRESSION);
42
+ const DuoBarCompressionContext =
43
+ createContext<DuoBarCompression>(NO_COMPRESSION);
43
44
 
44
45
  /**
45
- * Keeps the Duo side bar's toolbar and tab bar clear of each other (HIG Vertical controls):
46
- * both stay whole if they fit, else one is compressed and tapping it swaps them, else both
47
- * rest compressed and tapping one opens it until a tab is picked.
46
+ * keeps the side bar's toolbar and tab bar clear of each other (HIG, Designing for
47
+ * iPhone Duo: Vertical controls):
48
+ * - both fit: both stay whole
49
+ * - one fits whole beside the other compressed: one is compressed, tapping it swaps them
50
+ * - otherwise both rest compressed; tapping one opens it for a moment (the rail closes
51
+ * again once a tab is picked)
48
52
  */
49
53
  const DuoBarCompressionProvider: React.FC<{
50
54
  behavior?: DuoBarCompressionBehavior;
@@ -67,16 +71,18 @@ const DuoBarCompressionProvider: React.FC<{
67
71
  }
68
72
  const room = height - sideBar.bottom - sideBar.top - GROUP_GAP;
69
73
  const tabBar = getRailHeight(tabCount, hasQr);
70
- const qrHeight = TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight;
71
- const compressedTabBar = DuoMetrics.controlSize + (hasQr ? qrHeight : 0);
74
+ const compressedTabBar =
75
+ DuoMetrics.controlSize +
76
+ (hasQr ? TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight : 0);
72
77
  const fits = (toolbar: number, tabs: number) => toolbar + tabs <= room;
73
78
 
74
79
  if (fits(toolbarHeight, tabBar)) {
75
80
  return { ...NO_COMPRESSION, setToolbarHeight };
76
81
  }
77
- const canCompressToolbar = fits(COMPRESSED_TOOLBAR_HEIGHT, tabBar);
78
- const canCompressTabBar = fits(toolbarHeight, compressedTabBar);
79
- if (canCompressToolbar && canCompressTabBar) {
82
+ const canSwap =
83
+ fits(COMPRESSED_TOOLBAR_HEIGHT, tabBar) &&
84
+ fits(toolbarHeight, compressedTabBar);
85
+ if (canSwap) {
80
86
  return {
81
87
  toolbarCompressed: expanded === 'tabBar',
82
88
  tabBarCompressed: expanded === 'toolbar',
@@ -91,7 +97,8 @@ const DuoBarCompressionProvider: React.FC<{
91
97
  expandTabBar: () => setOpened('tabBar'),
92
98
  setToolbarHeight,
93
99
  };
94
- // An opened rail shows even if only the group gap is short; picking a tab closes it.
100
+ // an opened rail always shows, even when only the gap between the groups is short:
101
+ // it closes again once a tab is picked
95
102
  if (opened === 'tabBar') {
96
103
  return {
97
104
  ...open,
@@ -115,7 +122,6 @@ const DuoBarCompressionProvider: React.FC<{
115
122
  );
116
123
  };
117
124
 
118
- /** Compression state of the Duo side bars; no compression outside the provider. */
119
125
  const useDuoBarCompression = () => useContext(DuoBarCompressionContext);
120
126
 
121
127
  export { DuoBarCompressionProvider, useDuoBarCompression };
@@ -13,7 +13,6 @@ import { TabItemContent } from './CustomBottomTabItem';
13
13
  import { CurvedContainer } from './CurvedContainer';
14
14
  import { TabBarMetrics } from './metrics';
15
15
  import { getTabItems, TabItem } from './tabItems';
16
- import { FixedScaleText } from './TabBarIcon';
17
16
  import { usePressLift } from './usePressLift';
18
17
  import { ActiveIndicator } from './ActiveIndicator';
19
18
  import {
@@ -24,7 +23,7 @@ import {
24
23
  import type { DuoTabBarProps } from './DuoTabBar';
25
24
 
26
25
  /**
27
- * Center action icon: an icon name drawn at the CTA size, or a custom node.
26
+ * Center action icon: an icon name drawn at the CTA size, or a custom node
28
27
  */
29
28
  const CtaIcon: React.FC<{ icon: FloatingButtonProps['icon'] }> = ({ icon }) => {
30
29
  if (typeof icon === 'string') {
@@ -40,7 +39,7 @@ const CtaIcon: React.FC<{ icon: FloatingButtonProps['icon'] }> = ({ icon }) => {
40
39
  };
41
40
 
42
41
  /**
43
- * Vertical container (.Nav/Duo/Item column) with the sliding active indicator.
42
+ * vertical container (.Nav/Duo/Item column) with the sliding active indicator
44
43
  */
45
44
  const Rail: React.FC<{
46
45
  items: TabItem[];
@@ -76,7 +75,6 @@ const Rail: React.FC<{
76
75
  >
77
76
  <TabItemContent
78
77
  vertical
79
- fixedFontScale
80
78
  label={item.label}
81
79
  focused={item.focused}
82
80
  icon={item.icon}
@@ -90,7 +88,7 @@ const Rail: React.FC<{
90
88
  );
91
89
 
92
90
  /**
93
- * Center action of the rail: a round button with its label, under the container.
91
+ * center action of the rail: round button with its label, under the container
94
92
  */
95
93
  const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
96
94
  floatingButton,
@@ -116,7 +114,6 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
116
114
  <CtaIcon icon={floatingButton.icon} />
117
115
  </Animated.View>
118
116
  <View style={styles.railCtaLabel}>
119
- <FixedScaleText>
120
117
  <Text
121
118
  numberOfLines={1}
122
119
  typography={'label_xs_medium'}
@@ -124,7 +121,6 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
124
121
  >
125
122
  {floatingButton.label}
126
123
  </Text>
127
- </FixedScaleText>
128
124
  {floatingButton.labelContent && (
129
125
  <View pointerEvents={'none'} style={styles.railCtaLabelOverlay}>
130
126
  {floatingButton.labelContent}
@@ -136,14 +132,14 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
136
132
  };
137
133
 
138
134
  /**
139
- * Vertical bar anchored to the bottom of the Duo side bar, center action below it. When the
140
- * side bar is short it can shrink to one button showing the current tab (HIG "Tab bar
141
- * compressed"); tapping it expands the rail and compresses the toolbar.
135
+ * vertical bar anchored to the bottom of the Duo side bar, center action below it.
136
+ * When the side bar is short, it can shrink to one button showing the current tab
137
+ * (HIG "Tab bar compressed"); tapping it expands the rail and compresses the toolbar
142
138
  */
143
139
  const DuoRailTabBar: React.FC<DuoTabBarProps> = props => {
144
140
  const { sideBar } = useScreenLayout();
145
- const compression = useDuoBarCompression();
146
- const { tabBarCompressed, expandTabBar, onTabSelected } = compression;
141
+ const { tabBarCompressed, expandTabBar, onTabSelected } =
142
+ useDuoBarCompression();
147
143
  const compactLift = usePressLift();
148
144
  const { state, floatingButton, activeTintColor, inactiveTintColor } = props;
149
145
  const items = getTabItems(props);
@@ -153,7 +149,8 @@ const DuoRailTabBar: React.FC<DuoTabBarProps> = props => {
153
149
  return null;
154
150
  }
155
151
 
156
- // Lines up with the side bar header buttons: a 48 lane kept 12 from the inner edge.
152
+ // the rail lines up with the header buttons in the side bar: centered on a 48 lane kept 12
153
+ // from the strip's inner edge
157
154
  const columnStyle = [
158
155
  styles.railColumn,
159
156
  {
@@ -163,10 +160,9 @@ const DuoRailTabBar: React.FC<DuoTabBarProps> = props => {
163
160
  },
164
161
  sideBar.edge === 'right' ? styles.railColumnRight : styles.railColumnLeft,
165
162
  ];
166
- let cta: React.ReactNode;
167
- if (floatingButton) {
168
- cta = <RailCenterAction floatingButton={floatingButton} />;
169
- }
163
+ const cta = floatingButton && (
164
+ <RailCenterAction floatingButton={floatingButton} />
165
+ );
170
166
 
171
167
  if (!tabBarCompressed) {
172
168
  return (
@@ -3,7 +3,11 @@ 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 { getSystemIndicatorHeight, HorizontalTabBar } from './BottomTabBar';
6
+ import {
7
+ getSystemIndicatorHeight,
8
+ hasCenterAction,
9
+ HorizontalTabBar,
10
+ } from './BottomTabBar';
7
11
  import { getHorizontalBarWidth, TabBarMetrics } from './metrics';
8
12
  import { getTabItems } from './tabItems';
9
13
  import { DuoRailTabBar } from './DuoRailTabBar';
@@ -16,8 +20,8 @@ type DuoTabBarProps = BottomTabBarProps & {
16
20
  };
17
21
 
18
22
  /**
19
- * Space tab content leaves under the floating horizontal tab bar (phone and unfolded Duo);
20
- * 0 when the Duo rail sits in the side bar.
23
+ * space tab content leaves under the floating horizontal tab bar (phone and unfolded Duo);
24
+ * 0 when the Duo rail sits in the side bar
21
25
  */
22
26
  const useDuoTabBarInset = () => {
23
27
  const { isIPhoneDuo, sideBar, insets } = useScreenLayout();
@@ -28,12 +32,13 @@ const useDuoTabBarInset = () => {
28
32
  };
29
33
 
30
34
  /**
31
- * Horizontal navigation bar of the unfolded display: 65% of the short side, centered.
35
+ * horizontal navigation bar of the unfolded display: 65% of the short side, centered
32
36
  */
33
37
  const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
34
38
  const { insets } = useScreenLayout();
35
39
  const { width, height } = useWindowDimensions();
36
40
  const { floatingButton, activeTintColor, inactiveTintColor } = props;
41
+ const tabItems = getTabItems(props);
37
42
 
38
43
  return (
39
44
  <View
@@ -44,9 +49,15 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
44
49
  ]}
45
50
  >
46
51
  <HorizontalTabBar
47
- fixedFontScale
48
- items={getTabItems(props)}
49
- width={getHorizontalBarWidth(width, height, insets.left, insets.right)}
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
+ )}
50
61
  floatingButton={floatingButton}
51
62
  activeTintColor={activeTintColor}
52
63
  inactiveTintColor={inactiveTintColor}
@@ -56,14 +67,15 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
56
67
  };
57
68
 
58
69
  /**
59
- * iPhone Duo tab bar: the rail in the side bar, or the horizontal bar when unfolded portrait.
70
+ * iPhone Duo tab bar: rail in the side bar, or the horizontal bar when unfolded portrait
60
71
  */
61
72
  const DuoTabBar: React.FC<DuoTabBarProps> = props => {
62
73
  const { sideBar } = useScreenLayout();
63
- if (sideBar) {
64
- return <DuoRailTabBar {...props} />;
65
- }
66
- return <DuoFloatingTabBar {...props} />;
74
+ return sideBar ? (
75
+ <DuoRailTabBar {...props} />
76
+ ) : (
77
+ <DuoFloatingTabBar {...props} />
78
+ );
67
79
  };
68
80
 
69
81
  export type { DuoTabBarProps };
@@ -28,8 +28,7 @@ type Props = {
28
28
  };
29
29
 
30
30
  /**
31
- * Badge value of a tab: undefined hides it, '' is a dot (also 0 when `showDot`), numbers
32
- * cap at 99+.
31
+ * badge value shown on a tab: undefined hides it, '' is a dot, numbers cap at 99+
33
32
  */
34
33
  export const getTabBadge = (
35
34
  badgeLabel?: string | number,
@@ -53,8 +52,8 @@ export const getTabBadge = (
53
52
  };
54
53
 
55
54
  /**
56
- * Keeps text at its design size: like the system tab bar it doesn't grow with the OS font
57
- * scale or the wide Duo displays, which Text otherwise scales up to 5pt.
55
+ * text at its design size: like the system tab bar it doesn't grow with the OS font scale or
56
+ * the wide Duo displays, which Text otherwise scales up to 5pt
58
57
  */
59
58
  export const FixedScaleText: React.FC<{ children: React.ReactNode }> = ({
60
59
  children,
@@ -73,8 +72,7 @@ export const FixedScaleText: React.FC<{ children: React.ReactNode }> = ({
73
72
  };
74
73
 
75
74
  /**
76
- * Label badge of a tab: numbers use the error color, text the warning color (Figma Badge
77
- * round numeric / text).
75
+ * numbers use the error color, text the warning color (Figma Badge round numeric / text)
78
76
  */
79
77
  const TabBadge: React.FC<{ label: string; vertical?: boolean }> = ({
80
78
  label,
@@ -82,16 +80,14 @@ const TabBadge: React.FC<{ label: string; vertical?: boolean }> = ({
82
80
  }) => {
83
81
  const { theme } = useContext(ApplicationContext);
84
82
  const isNumber = /^\d+\+?$/.test(label);
85
- let backgroundColor = theme.colors.warning.primary;
86
- if (isNumber) {
87
- backgroundColor = theme.colors.error.primary;
88
- }
89
83
  const badge = (
90
84
  <View
91
85
  style={[
92
86
  styles.badge,
93
87
  {
94
- backgroundColor,
88
+ backgroundColor: isNumber
89
+ ? theme.colors.error.primary
90
+ : theme.colors.warning.primary,
95
91
  borderColor: theme.colors.background.surface,
96
92
  },
97
93
  ]}
@@ -114,8 +110,7 @@ const TabBadge: React.FC<{ label: string; vertical?: boolean }> = ({
114
110
  };
115
111
 
116
112
  /**
117
- * Tab icon (24) with its badge; active and inactive icons are stacked and cross-fade on
118
- * focus.
113
+ * 24 icon; active and inactive icons are stacked and cross-faded on focus
119
114
  */
120
115
  export function TabBarIcon({
121
116
  focused,
@@ -29,7 +29,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
29
29
 
30
30
  const screenName = screen?.name || screen?.type?.name || 'Invalid';
31
31
  const { isBackgroundToForeground } = useAppState();
32
- // Pad clear of the floating bar unless a Screen inside claims the inset to scroll under it.
32
+ // the floating tab bar covers the bottom of the tab: pad the content unless a Screen in it
33
+ // keeps clear of the bar itself (scroll content running under the bar)
33
34
  const { inset: tabBarInset } = useContext(BottomTabInsetContext);
34
35
  const [insetClaimed, setInsetClaimed] = useState(false);
35
36
  const tabInsetValue = useMemo(
@@ -53,6 +54,9 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
53
54
  ...data,
54
55
  });
55
56
 
57
+ /**
58
+ * debug toast
59
+ */
56
60
  navigator?.maxApi?.showToastDebug?.({
57
61
  appId: context.appId,
58
62
  message: `${screenName} screen_navigated`,
@@ -1,5 +1,4 @@
1
1
  import {
2
- BottomTabBarProps,
3
2
  BottomTabNavigationOptions,
4
3
  createBottomTabNavigator,
5
4
  } from '@react-navigation/bottom-tabs';
@@ -24,8 +23,6 @@ import TabScreen from './TabScreen';
24
23
  import { ApplicationContext } from '../../Context';
25
24
  import { useScreenLayout } from '../../Layout';
26
25
 
27
- type SceneInterpolator = BottomTabNavigationOptions['sceneStyleInterpolator'];
28
-
29
26
  const Tab = createBottomTabNavigator();
30
27
 
31
28
  // the tab screens slide in index order over 280ms, CSS `ease`
@@ -35,11 +32,12 @@ const TAB_SWITCH_SPEC = {
35
32
  };
36
33
 
37
34
  /**
38
- * Tab slide interpolator. Scene progress is -1 for tabs before the active one and 1 for
39
- * 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
40
37
  */
41
- const getTabSlide = (width: number): SceneInterpolator => {
42
- return ({ current }) => ({
38
+ const getTabSlide =
39
+ (width: number): BottomTabNavigationOptions['sceneStyleInterpolator'] =>
40
+ ({ current }) => ({
43
41
  sceneStyle: {
44
42
  transform: [
45
43
  {
@@ -51,7 +49,6 @@ const getTabSlide = (width: number): SceneInterpolator => {
51
49
  ],
52
50
  },
53
51
  });
54
- };
55
52
 
56
53
  /**
57
54
  * App tab navigator: the floating bar on phones, the rail or floating bar on
@@ -70,11 +67,11 @@ const BottomTab: React.FC<BottomTabProps> = ({
70
67
  const { isIPhoneDuo } = useScreenLayout();
71
68
  const { width: windowWidth } = useWindowDimensions();
72
69
  const barInset = useDuoTabBarInset();
73
- // The center action's button rises above the bar; tab content leaves room for it too.
74
- let tabBarInset = barInset;
75
- if (barInset && hasCenterAction(floatingButton, tabs.length)) {
76
- tabBarInset = barInset + TabBarMetrics.ctaLift;
77
- }
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;
78
75
  const initialIndex = tabs.findIndex(i => i.name === initialRouteName);
79
76
  const activeIndex = React.useRef(initialIndex > 0 ? initialIndex : 0);
80
77
 
@@ -86,15 +83,15 @@ const BottomTab: React.FC<BottomTabProps> = ({
86
83
  });
87
84
  }, [navigation]);
88
85
 
89
- // 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.
90
88
  useFocusEffect(
91
89
  React.useCallback(() => {
92
90
  NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(
93
91
  activeIndex.current !== 0,
94
92
  );
95
- return () => {
93
+ return () =>
96
94
  NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(false);
97
- };
98
95
  }, []),
99
96
  );
100
97
 
@@ -131,27 +128,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
131
128
  },
132
129
  };
133
130
 
134
- const renderTabBar = (props: BottomTabBarProps) => {
135
- if (isIPhoneDuo) {
136
- return (
137
- <DuoTabBar
138
- {...props}
139
- floatingButton={floatingButton}
140
- inactiveTintColor={theme.colors.text.secondary}
141
- activeTintColor={theme.colors.primary}
142
- />
143
- );
144
- }
145
- return (
146
- <BottomTabBar
147
- {...props}
148
- floatingButton={floatingButton}
149
- inactiveTintColor={theme.colors.text.secondary}
150
- activeTintColor={theme.colors.primary}
151
- />
152
- );
153
- };
154
-
155
131
  return (
156
132
  <DuoBarCompressionProvider
157
133
  behavior={compressionBehavior}
@@ -169,7 +145,23 @@ const BottomTab: React.FC<BottomTabProps> = ({
169
145
  transitionSpec: TAB_SWITCH_SPEC,
170
146
  sceneStyleInterpolator: getTabSlide(windowWidth),
171
147
  }}
172
- 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
+ }
173
165
  initialRouteName={initialRouteName}
174
166
  >
175
167
  {tabs.map((item, index) => (