@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
@@ -51,10 +51,8 @@ export const useBottomSheetLayout = () => {
51
51
  };
52
52
  };
53
53
 
54
- /**
55
- * Keyboard lift of the sheet on iPhone Duo, where KeyboardAvoidingView pushes
56
- * the whole sheet past the top; the sheet lifts by it and shrinks its maxHeight.
57
- */
54
+ // KeyboardAvoidingView lifts the whole sheet past the top on iPhone Duo,
55
+ // so lift it here and shrink maxHeight by the same amount instead
58
56
  export const useDuoKeyboardLift = (
59
57
  isDuo: boolean,
60
58
  useKeyboardAvoidingView: boolean,
@@ -138,10 +136,6 @@ type SheetDismissParams = {
138
136
  action: MutableRefObject<undefined | string>;
139
137
  };
140
138
 
141
- /**
142
- * Close handlers of a bottom sheet. A non-forced dismiss (scrim tap, hardware
143
- * back) is ignored while `barrierDismissible` is set.
144
- */
145
139
  export const useSheetDismiss = ({
146
140
  barrierDismissible,
147
141
  closeAnimation,
@@ -149,6 +143,9 @@ export const useSheetDismiss = ({
149
143
  leftOptions,
150
144
  action,
151
145
  }: SheetDismissParams) => {
146
+ /**
147
+ * handler dismiss
148
+ */
152
149
  const onDismiss = useCallback(
153
150
  (force = false, callback?: () => void) => {
154
151
  if (barrierDismissible && !force) {
@@ -164,6 +161,9 @@ export const useSheetDismiss = ({
164
161
  [barrierDismissible, closeAnimation, navigator],
165
162
  );
166
163
 
164
+ /**
165
+ * on request close
166
+ */
167
167
  const onRequestClose = useCallback(
168
168
  (callback?: () => void) => {
169
169
  onDismiss(true, callback);
@@ -34,52 +34,42 @@ export const renderSheetHeader = ({
34
34
  iconBack,
35
35
  options,
36
36
  leftOptions,
37
- }: SheetHeaderParams) => {
38
- let backButton: React.ReactNode = null;
39
- if (showBack) {
40
- backButton = <ModalIconButton icon={iconBack} onPress={onPressBack} />;
41
- }
42
-
43
- let headerContent: React.ReactNode;
44
- if (options.header) {
45
- headerContent = <View style={Styles.flex}>{options.header}</View>;
46
- } else {
47
- headerContent = (
48
- <>
49
- {!leftOptions ? <View style={styles.iconButtonSpace} /> : null}
50
- <Text
51
- style={[Styles.flex, styles.title]}
52
- numberOfLines={1}
53
- typography="header_default_bold"
54
- >
55
- {options.title ?? ''}
56
- </Text>
57
- </>
58
- );
59
- }
60
-
61
- return (
62
- <View {...panHandlers}>
63
- <View style={{ height: INDICATOR_HEIGHT }}>
64
- <View style={styles.indicator} />
65
- </View>
66
- <View
67
- style={[
68
- styles.header,
69
- useDivider && styles.headerDivider,
70
- useDivider && {
71
- borderBottomColor: theme.colors.border.default,
72
- },
73
- ]}
74
- >
75
- {backButton}
37
+ }: SheetHeaderParams) => (
38
+ <View {...panHandlers}>
39
+ <View style={{ height: INDICATOR_HEIGHT }}>
40
+ <View style={styles.indicator} />
41
+ </View>
42
+ <View
43
+ style={[
44
+ styles.header,
45
+ useDivider && styles.headerDivider,
46
+ useDivider && {
47
+ borderBottomColor: theme.colors.border.default,
48
+ },
49
+ ]}
50
+ >
51
+ {showBack ? (
52
+ <ModalIconButton icon={iconBack} onPress={onPressBack} />
53
+ ) : null}
76
54
 
77
- {headerContent}
78
- <ModalIconButton icon="navigation_close" onPress={onPressClose} />
79
- </View>
55
+ {options.header ? (
56
+ <View style={Styles.flex}>{options.header}</View>
57
+ ) : (
58
+ <>
59
+ {!leftOptions ? <View style={styles.iconButtonSpace} /> : null}
60
+ <Text
61
+ style={[Styles.flex, styles.title]}
62
+ numberOfLines={1}
63
+ typography="header_default_bold"
64
+ >
65
+ {options.title ?? ''}
66
+ </Text>
67
+ </>
68
+ )}
69
+ <ModalIconButton icon="navigation_close" onPress={onPressClose} />
80
70
  </View>
81
- );
82
- };
71
+ </View>
72
+ );
83
73
 
84
74
  /**
85
75
  * Footer pinned under the sheet content, `null` when the sheet has none.
@@ -30,10 +30,6 @@ import {
30
30
  renderSheetHeader,
31
31
  } from './BottomSheet.parts';
32
32
 
33
- /**
34
- * Bottom sheet route of the modal stack: slides the given screen up under a
35
- * drag handle and header, and reports how it was closed through `onDismiss`.
36
- */
37
33
  const BottomSheet: React.FC<BottomSheetParams> = props => {
38
34
  const { theme, navigator } = useContext(ApplicationContext);
39
35
  const context = useContext<any>(MiniAppContext);
@@ -102,9 +98,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
102
98
  const panResponder = useRef(
103
99
  PanResponder.create({
104
100
  onStartShouldSetPanResponder: () => draggable,
105
- onMoveShouldSetPanResponder: (_, gestureState) => {
106
- return draggable && gestureState.dy > 0;
107
- },
101
+ onMoveShouldSetPanResponder: (_, gestureState) =>
102
+ draggable && gestureState.dy > 0,
108
103
  onPanResponderMove: (_, gestureState) => {
109
104
  if (gestureState.dy > 0) {
110
105
  translateY.value = gestureState.dy;
@@ -136,7 +131,10 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
136
131
  backgroundColor = theme.colors.background.surface;
137
132
  }
138
133
 
139
- // Mount only, so folding or rotating iPhone Duo doesn't emit a dismiss.
134
+ /**
135
+ * emit dismiss event, once: folding or rotating iPhone Duo changes the
136
+ * window height while the sheet stays open
137
+ */
140
138
  useEffect(() => {
141
139
  openAnimation();
142
140
  return () => {
@@ -144,14 +142,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
144
142
  };
145
143
  }, [openAnimation]);
146
144
 
147
- const handlers = useSheetDismiss({
148
- barrierDismissible,
149
- closeAnimation,
150
- navigator,
151
- leftOptions,
152
- action,
153
- });
154
- const { onDismiss, onRequestClose, onPressBack, onPressClose } = handlers;
145
+ const { onDismiss, onRequestClose, onPressBack, onPressClose } =
146
+ useSheetDismiss({
147
+ barrierDismissible,
148
+ closeAnimation,
149
+ navigator,
150
+ leftOptions,
151
+ action,
152
+ });
155
153
 
156
154
  const showBack = !!leftOptions?.onPressIconLeft;
157
155
  const iconBack = leftOptions?.iconLeft ?? 'ic_back';
@@ -182,24 +180,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
182
180
  />
183
181
  );
184
182
 
185
- let content: React.ReactNode;
186
- if (isFoldable) {
187
- // Content shrinks so a tall sheet keeps its header and footer.
188
- content = (
189
- <View style={styles.shrink}>
190
- {renderSheetHeader(headerParams)}
191
- <View style={styles.shrink}>{screen}</View>
192
- </View>
193
- );
194
- } else {
195
- content = (
196
- <>
197
- {renderSheetHeader(headerParams)}
198
- {screen}
199
- </>
200
- );
201
- }
202
-
203
183
  return (
204
184
  <Container
205
185
  transparent
@@ -245,7 +225,18 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
245
225
  }),
246
226
  ]}
247
227
  >
248
- {content}
228
+ {isFoldable ? (
229
+ // content shrinks so a tall sheet keeps its header and footer
230
+ <View style={styles.shrink}>
231
+ {renderSheetHeader(headerParams)}
232
+ <View style={styles.shrink}>{screen}</View>
233
+ </View>
234
+ ) : (
235
+ <>
236
+ {renderSheetHeader(headerParams)}
237
+ {screen}
238
+ </>
239
+ )}
249
240
  {renderSheetFooter({ footerComponent, theme })}
250
241
  {useBottomInset && (
251
242
  <View
@@ -19,8 +19,8 @@ type Props = {
19
19
  };
20
20
 
21
21
  /**
22
- * The single active indicator of a bar (Background/tonal with inner shadows) that slides
23
- * to the selected item. It lands in place on the first measure and slides from then on.
22
+ * one active indicator per bar (Background/tonal with its inner shadows) sliding to the
23
+ * selected item; it lands in place on the first measure and slides from then on
24
24
  */
25
25
  const ActiveIndicator: React.FC<Props> = ({
26
26
  axis,
@@ -49,19 +49,18 @@ const ActiveIndicator: React.FC<Props> = ({
49
49
  position.value = withTiming(offset, TabBarMotion.indicator);
50
50
  }, [offset, extent, position, size, visible]);
51
51
 
52
- const animatedStyle = useAnimatedStyle(() => {
53
- if (axis === 'x') {
54
- return {
55
- opacity: visible.value,
56
- width: size.value,
57
- transform: [{ translateX: position.value }],
58
- };
59
- }
60
- return {
61
- opacity: visible.value,
62
- transform: [{ translateY: position.value }],
63
- };
64
- });
52
+ const animatedStyle = useAnimatedStyle(() =>
53
+ axis === 'x'
54
+ ? {
55
+ opacity: visible.value,
56
+ width: size.value,
57
+ transform: [{ translateX: position.value }],
58
+ }
59
+ : {
60
+ opacity: visible.value,
61
+ transform: [{ translateY: position.value }],
62
+ },
63
+ );
65
64
 
66
65
  return (
67
66
  <Animated.View
@@ -2,6 +2,20 @@ import { StyleSheet } from 'react-native';
2
2
  import { Colors, Spacing } from '../../Consts';
3
3
  import { CTA_SHADOW, TabBarMetrics } from './metrics';
4
4
 
5
+ const ITEM_HEIGHT =
6
+ TabBarMetrics.indicatorHeight +
7
+ TabBarMetrics.itemIconLabelGap +
8
+ TabBarMetrics.labelHeight;
9
+ const CONTENT_HEIGHT = TabBarMetrics.height - 2 * TabBarMetrics.paddingVertical;
10
+ // the items are centered in the content, so their label starts this far below the content top
11
+ const ITEM_LABEL_TOP =
12
+ (CONTENT_HEIGHT - ITEM_HEIGHT) / 2 +
13
+ TabBarMetrics.indicatorHeight +
14
+ TabBarMetrics.itemIconLabelGap;
15
+ // the center action column is placed so its label starts on the same line as the tab labels
16
+ const CTA_TOP =
17
+ ITEM_LABEL_TOP - TabBarMetrics.ctaLabelGap - TabBarMetrics.ctaSize;
18
+
5
19
  export const styles = StyleSheet.create({
6
20
  bar: {
7
21
  position: 'absolute',
@@ -37,7 +51,7 @@ export const styles = StyleSheet.create({
37
51
  position: 'absolute',
38
52
  left: 0,
39
53
  right: 0,
40
- top: -TabBarMetrics.ctaLift - TabBarMetrics.paddingVertical,
54
+ top: CTA_TOP,
41
55
  alignItems: 'center',
42
56
  gap: TabBarMetrics.ctaLabelGap,
43
57
  },
@@ -46,6 +60,9 @@ export const styles = StyleSheet.create({
46
60
  width: TabBarMetrics.ctaSize,
47
61
  height: TabBarMetrics.ctaSize,
48
62
  borderRadius: TabBarMetrics.ctaSize / 2,
63
+ // Figma Icon Button border: white 60% hairline
64
+ borderWidth: 1,
65
+ borderColor: `rgba(255, 255, 255, ${TabBarMetrics.ctaBorderOpacity})`,
49
66
  alignItems: 'center',
50
67
  justifyContent: 'center',
51
68
  },
@@ -17,7 +17,6 @@ import { CurvedContainer } from './CurvedContainer';
17
17
  import { ActiveIndicator } from './ActiveIndicator';
18
18
  import { getHorizontalBarWidth, TabBarMetrics, TabBarMotion } from './metrics';
19
19
  import { getTabItems, TabItem } from './tabItems';
20
- import { FixedScaleText } from './TabBarIcon';
21
20
  import { styles } from './BottomTabBar.styles';
22
21
 
23
22
  type Props = BottomTabBarProps & {
@@ -27,28 +26,24 @@ type Props = BottomTabBarProps & {
27
26
  };
28
27
 
29
28
  /**
30
- * Whether the bar shows the center action. It needs a tab on each side, so the
31
- * Special type takes 2 or 4 tabs.
29
+ * the center action needs a tab on each side: Special type takes 2 or 4 tabs
32
30
  */
33
- const hasCenterAction = (floatingButton: unknown, tabCount: number) => {
34
- return !!floatingButton && tabCount % 2 === 0;
35
- };
31
+ const hasCenterAction = (floatingButton: unknown, tabCount: number) =>
32
+ !!floatingButton && tabCount % 2 === 0;
36
33
 
37
34
  /**
38
- * Gap under the bar container for the home indicator / gesture bar area, at
39
- * least `Spacing.S`; iOS caps it at the indicator height.
35
+ * gap under the container: the home indicator / gesture bar area
40
36
  */
41
- const getSystemIndicatorHeight = (bottomInset: number) => {
42
- let height = bottomInset;
43
- if (Platform.OS === 'ios') {
44
- height = Math.min(bottomInset, TabBarMetrics.systemIndicatorHeight);
45
- }
46
- return Math.max(height, Spacing.S);
47
- };
37
+ const getSystemIndicatorHeight = (bottomInset: number) =>
38
+ Math.max(
39
+ Platform.OS === 'ios'
40
+ ? Math.min(bottomInset, TabBarMetrics.systemIndicatorHeight)
41
+ : bottomInset,
42
+ Spacing.S,
43
+ );
48
44
 
49
45
  /**
50
- * Press feedback of the center action: the button shrinks while held, the label
51
- * stays still.
46
+ * center action press: the button shrinks while held; the label stays plain text
52
47
  */
53
48
  const useCenterActionPress = () => {
54
49
  const scale = useSharedValue(1);
@@ -68,12 +63,11 @@ const useCenterActionPress = () => {
68
63
  };
69
64
 
70
65
  /**
71
- * Center action (.Nav/CTA): a button rising out of the notch, its label below.
66
+ * center action (.Nav/CTA): button rising out of the notch, label under it
72
67
  */
73
68
  const CenterAction: React.FC<{
74
69
  floatingButton: FloatingButtonProps;
75
- fixedFontScale?: boolean;
76
- }> = ({ floatingButton, fixedFontScale }) => {
70
+ }> = ({ floatingButton }) => {
77
71
  const { theme } = useContext(ApplicationContext);
78
72
  const context = useContext<any>(MiniAppContext);
79
73
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
@@ -89,22 +83,6 @@ const CenterAction: React.FC<{
89
83
  </Text>
90
84
  );
91
85
 
92
- let iconNode = icon;
93
- if (typeof icon === 'string') {
94
- iconNode = (
95
- <Icon
96
- source={icon}
97
- size={TabBarMetrics.ctaIconSize}
98
- color={Colors.black_01}
99
- />
100
- );
101
- }
102
-
103
- let labelNode = labelText;
104
- if (fixedFontScale) {
105
- labelNode = <FixedScaleText>{labelText}</FixedScaleText>;
106
- }
107
-
108
86
  return (
109
87
  <View style={[styles.ctaSlot, showBaseLineDebug && styles.debugBaseLine]}>
110
88
  <Pressable
@@ -122,10 +100,18 @@ const CenterAction: React.FC<{
122
100
  buttonStyle,
123
101
  ]}
124
102
  >
125
- {iconNode}
103
+ {typeof icon === 'string' ? (
104
+ <Icon
105
+ source={icon}
106
+ size={TabBarMetrics.ctaIconSize}
107
+ color={Colors.black_01}
108
+ />
109
+ ) : (
110
+ icon
111
+ )}
126
112
  </Animated.View>
127
113
  <View style={styles.ctaLabel}>
128
- {labelNode}
114
+ {labelText}
129
115
  {labelContent && (
130
116
  <View pointerEvents={'none'} style={styles.ctaLabelOverlay}>
131
117
  {labelContent}
@@ -138,8 +124,8 @@ const CenterAction: React.FC<{
138
124
  };
139
125
 
140
126
  /**
141
- * Horizontal navigation bar: a floating container with evenly split items; the
142
- * Special type puts the center action in a notch in the middle.
127
+ * horizontal navigation bar: floating container, items split evenly, Special type puts
128
+ * the center action in a notch in the middle
143
129
  */
144
130
  const HorizontalTabBar: React.FC<{
145
131
  items: TabItem[];
@@ -147,14 +133,12 @@ const HorizontalTabBar: React.FC<{
147
133
  activeTintColor: string;
148
134
  inactiveTintColor: string;
149
135
  floatingButton?: FloatingButtonProps;
150
- fixedFontScale?: boolean;
151
136
  }> = ({
152
137
  items,
153
138
  width,
154
139
  activeTintColor,
155
140
  inactiveTintColor,
156
141
  floatingButton,
157
- fixedFontScale,
158
142
  }) => {
159
143
  const withCta = hasCenterAction(floatingButton, items.length);
160
144
  const [layouts, setLayouts] = useState<
@@ -162,8 +146,8 @@ const HorizontalTabBar: React.FC<{
162
146
  >({});
163
147
  const selected = layouts[items.find(item => item.focused)?.route.key ?? ''];
164
148
 
165
- const renderItems = (data: TabItem[]) => {
166
- return data.map(item => (
149
+ const renderItems = (data: TabItem[]) =>
150
+ data.map(item => (
167
151
  <CustomBottomTabItem
168
152
  key={item.route.key}
169
153
  onLayout={e => {
@@ -181,10 +165,8 @@ const HorizontalTabBar: React.FC<{
181
165
  onLongPress={item.onLongPress}
182
166
  activeTintColor={activeTintColor}
183
167
  inactiveTintColor={inactiveTintColor}
184
- fixedFontScale={fixedFontScale}
185
168
  />
186
169
  ));
187
- };
188
170
 
189
171
  const renderContent = () => {
190
172
  if (withCta && floatingButton) {
@@ -192,10 +174,7 @@ const HorizontalTabBar: React.FC<{
192
174
  return (
193
175
  <>
194
176
  {renderItems(items.slice(0, middle))}
195
- <CenterAction
196
- floatingButton={floatingButton}
197
- fixedFontScale={fixedFontScale}
198
- />
177
+ <CenterAction floatingButton={floatingButton} />
199
178
  {renderItems(items.slice(middle))}
200
179
  </>
201
180
  );
@@ -203,21 +182,17 @@ const HorizontalTabBar: React.FC<{
203
182
  return renderItems(items);
204
183
  };
205
184
 
206
- const customContainer = withCta && floatingButton?.container;
207
-
208
185
  return (
209
186
  <View
210
187
  style={[styles.container, { width }]}
211
188
  accessibilityLabel={'bottom_tab_bar'}
212
189
  >
213
- {customContainer || (
214
- <CurvedContainer
215
- width={width}
216
- height={TabBarMetrics.height}
217
- radius={TabBarMetrics.height / 2}
218
- notchCenterX={withCta ? width / 2 : undefined}
219
- />
220
- )}
190
+ <CurvedContainer
191
+ width={width}
192
+ height={TabBarMetrics.height}
193
+ radius={TabBarMetrics.height / 2}
194
+ notchCenterX={withCta ? width / 2 : undefined}
195
+ />
221
196
  <View style={styles.content}>
222
197
  <ActiveIndicator
223
198
  axis={'x'}
@@ -236,9 +211,9 @@ const HorizontalTabBar: React.FC<{
236
211
  };
237
212
 
238
213
  /**
239
- * Phone tab bar: the horizontal bar floats over the tab content above the home indicator,
240
- * so content shows around it and through the notch. The transparent strip above it keeps
241
- * the rising center action touchable.
214
+ * phone tab bar: the horizontal navigation bar floating over the tab content above the home
215
+ * indicator, so the content shows around it and through the notch. The transparent strip
216
+ * above it keeps the rising center action touchable
242
217
  */
243
218
  export default function BottomTabBar(props: Props) {
244
219
  const { state, floatingButton, activeTintColor, inactiveTintColor } = props;
@@ -250,6 +225,8 @@ export default function BottomTabBar(props: Props) {
250
225
  window.height,
251
226
  insets.left,
252
227
  insets.right,
228
+ state.routes.length,
229
+ withCta,
253
230
  );
254
231
 
255
232
  return (
@@ -1,15 +1,11 @@
1
1
  import { createContext } from 'react';
2
2
 
3
3
  type BottomTabInset = {
4
- /**
5
- * Space the floating tab bar takes over the bottom of a tab's content (bar + home
6
- * indicator area, plus the rising center action when shown); 0 on the iPhone Duo rail.
7
- */
4
+ // space the floating tab bar takes over the bottom of a tab's content (bar + home
5
+ // indicator area, plus the rising center action when shown); 0 on the iPhone Duo rail
8
6
  inset: number;
9
- /**
10
- * Called by a Screen in the tab that keeps its content clear of the bar itself (inside
11
- * its scroll view, or above the bar when static), so the tab stops padding its content.
12
- */
7
+ // a Screen in the tab keeps its content clear of the bar itself (inside its scroll view,
8
+ // or above the bar when static), so the tab stops padding its content
13
9
  claim?: () => void;
14
10
  };
15
11
 
@@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
3
3
  import Svg, { Defs, FeGaussianBlur, Filter, Path } from 'react-native-svg';
4
4
  import { Colors } from '../../Consts';
5
5
  import { ApplicationContext } from '../../Context';
6
- import { NOTCH_CURVES, NOTCH_HALF_WIDTH } from './metrics';
6
+ import { NOTCH_CURVES, NOTCH_HALF_WIDTH, TabBarMetrics } from './metrics';
7
7
 
8
8
  type Props = {
9
9
  width: number;
@@ -15,18 +15,16 @@ type Props = {
15
15
 
16
16
  // room for the floating shadow around the shape: the key shadow reaches 12 + 3 × 16 below it
17
17
  const SHADOW_PADDING = 64;
18
- /**
19
- * Figma Shadow/floating: 12% blur 24, plus 4% blur 32 spread -16 offset 12. SVG blur takes
20
- * half the CSS blur radius; the spread shrinks the shape the key shadow is cast from.
21
- */
18
+ // Figma Shadow/floating: 12% blur 24, plus 4% blur 32 spread -16 offset 12; svg blur takes
19
+ // half the CSS blur radius, the spread shrinks the shape the key shadow is cast from
22
20
  const AMBIENT_BLUR = 12;
23
21
  const KEY_BLUR = 16;
24
22
  const KEY_SPREAD = 16;
25
23
  const KEY_OFFSET_Y = 12;
26
24
 
27
25
  /**
28
- * SVG path of the rounded container placed at (`x`, `y`) on the canvas, with the notch
29
- * dipped into the top edge around `notchCenterX`.
26
+ * rounded container path placed at (`x`, `y`) on the canvas, with the notch dipped into the
27
+ * top edge around `notchCenterX`
30
28
  */
31
29
  const getContainerPath = (
32
30
  width: number,
@@ -45,10 +43,11 @@ const getContainerPath = (
45
43
  if (notchCenterX !== undefined) {
46
44
  const cx = x + notchCenterX;
47
45
  const point = ([dx, dy]: [number, number]) => `${cx + dx} ${top + dy}`;
48
- const curves = NOTCH_CURVES.map(([c1, c2, end]) => {
49
- return ` C ${point(c1)} ${point(c2)} ${point(end)}`;
50
- });
51
- notch = ` L ${cx - NOTCH_HALF_WIDTH} ${top}${curves.join('')}`;
46
+ notch =
47
+ ` L ${cx - NOTCH_HALF_WIDTH} ${top}` +
48
+ NOTCH_CURVES.map(
49
+ ([c1, c2, end]) => ` C ${point(c1)} ${point(c2)} ${point(end)}`,
50
+ ).join('');
52
51
  }
53
52
  return (
54
53
  `M ${left + r} ${top}` +
@@ -65,8 +64,8 @@ const getContainerPath = (
65
64
  };
66
65
 
67
66
  /**
68
- * Navigation bar container: surface fill with the floating shadow, drawn as an SVG shape
69
- * so the shadow follows the notch.
67
+ * navigation bar container: surface fill with the floating shadow, drawn as a shape so
68
+ * the shadow follows the notch
70
69
  */
71
70
  const CurvedContainer: React.FC<Props> = ({
72
71
  width,
@@ -88,7 +87,6 @@ const CurvedContainer: React.FC<Props> = ({
88
87
  SHADOW_PADDING,
89
88
  notchCenterX,
90
89
  );
91
- const hasKeyShadow = width > 2 * KEY_SPREAD && height > 2 * KEY_SPREAD;
92
90
 
93
91
  return (
94
92
  <Svg
@@ -125,7 +123,7 @@ const CurvedContainer: React.FC<Props> = ({
125
123
  fillOpacity={0.12}
126
124
  filter={'url(#ambientShadow)'}
127
125
  />
128
- {hasKeyShadow && (
126
+ {width > 2 * KEY_SPREAD && height > 2 * KEY_SPREAD && (
129
127
  <Path
130
128
  d={getContainerPath(
131
129
  width - 2 * KEY_SPREAD,
@@ -139,7 +137,13 @@ const CurvedContainer: React.FC<Props> = ({
139
137
  filter={'url(#keyShadow)'}
140
138
  />
141
139
  )}
142
- <Path d={path} fill={theme.colors.background.surface} />
140
+ <Path
141
+ d={path}
142
+ fill={theme.colors.background.surface}
143
+ fillOpacity={TabBarMetrics.backgroundOpacity}
144
+ stroke={theme.colors.border.default}
145
+ strokeWidth={1}
146
+ />
143
147
  </Svg>
144
148
  );
145
149
  };