@momo-kits/foundation 0.169.4-beta.7 → 0.169.4

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.
@@ -46,9 +46,6 @@ export const styles = StyleSheet.create({
46
46
  width: TabBarMetrics.ctaSize,
47
47
  height: TabBarMetrics.ctaSize,
48
48
  borderRadius: TabBarMetrics.ctaSize / 2,
49
- // Figma Icon Button border: white 60% hairline
50
- borderWidth: 1,
51
- borderColor: `rgba(255, 255, 255, ${TabBarMetrics.ctaBorderOpacity})`,
52
49
  alignItems: 'center',
53
50
  justifyContent: 'center',
54
51
  },
@@ -17,7 +17,7 @@ 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 { CappedScaleText } from './TabBarIcon';
20
+ import { FixedScaleText } from './TabBarIcon';
21
21
  import { styles } from './BottomTabBar.styles';
22
22
 
23
23
  type Props = BottomTabBarProps & {
@@ -114,7 +114,7 @@ const CenterAction: React.FC<{
114
114
  </Animated.View>
115
115
  <View style={styles.ctaLabel}>
116
116
  {fixedFontScale ? (
117
- <CappedScaleText>{labelText}</CappedScaleText>
117
+ <FixedScaleText>{labelText}</FixedScaleText>
118
118
  ) : (
119
119
  labelText
120
120
  )}
@@ -194,17 +194,21 @@ const HorizontalTabBar: React.FC<{
194
194
  return renderItems(items);
195
195
  };
196
196
 
197
+ const customContainer = withCta && floatingButton?.container;
198
+
197
199
  return (
198
200
  <View
199
201
  style={[styles.container, { width }]}
200
202
  accessibilityLabel={'bottom_tab_bar'}
201
203
  >
202
- <CurvedContainer
203
- width={width}
204
- height={TabBarMetrics.height}
205
- radius={TabBarMetrics.height / 2}
206
- notchCenterX={withCta ? width / 2 : undefined}
207
- />
204
+ {customContainer || (
205
+ <CurvedContainer
206
+ width={width}
207
+ height={TabBarMetrics.height}
208
+ radius={TabBarMetrics.height / 2}
209
+ notchCenterX={withCta ? width / 2 : undefined}
210
+ />
211
+ )}
208
212
  <View style={styles.content}>
209
213
  <ActiveIndicator
210
214
  axis={'x'}
@@ -237,8 +241,6 @@ export default function BottomTabBar(props: Props) {
237
241
  window.height,
238
242
  insets.left,
239
243
  insets.right,
240
- state.routes.length,
241
- withCta,
242
244
  );
243
245
 
244
246
  return (
@@ -256,7 +258,6 @@ export default function BottomTabBar(props: Props) {
256
258
  ]}
257
259
  >
258
260
  <HorizontalTabBar
259
- fixedFontScale
260
261
  items={getTabItems(props)}
261
262
  width={width}
262
263
  floatingButton={floatingButton}
@@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
3
3
  import Svg, { Defs, FeGaussianBlur, Filter, Path } from 'react-native-svg';
4
4
  import { Colors } from '../../Consts';
5
5
  import { ApplicationContext } from '../../Context';
6
- import { NOTCH_CURVES, NOTCH_HALF_WIDTH, TabBarMetrics } from './metrics';
6
+ import { NOTCH_CURVES, NOTCH_HALF_WIDTH } from './metrics';
7
7
 
8
8
  type Props = {
9
9
  width: number;
@@ -137,13 +137,7 @@ const CurvedContainer: React.FC<Props> = ({
137
137
  filter={'url(#keyShadow)'}
138
138
  />
139
139
  )}
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
- />
140
+ <Path d={path} fill={theme.colors.background.surface} />
147
141
  </Svg>
148
142
  );
149
143
  };
@@ -10,10 +10,10 @@ import Animated, {
10
10
  useSharedValue,
11
11
  withTiming,
12
12
  } from 'react-native-reanimated';
13
- import { CappedScaleText, TabBarIcon } from './TabBarIcon';
13
+ import { FixedScaleText, TabBarIcon } from './TabBarIcon';
14
14
  import { Text } from '../../Text';
15
15
  import { MiniAppContext } from '../../Context';
16
- import { Colors } from '../../Consts';
16
+ import { Colors, Spacing } from '../../Consts';
17
17
  import { TabBarMetrics, TabBarMotion } from './metrics';
18
18
 
19
19
  type IconRenderer = (props: {
@@ -98,7 +98,7 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
98
98
  />
99
99
  </View>
100
100
  {!hideLabel &&
101
- (fixedFontScale ? <CappedScaleText>{text}</CappedScaleText> : text)}
101
+ (fixedFontScale ? <FixedScaleText>{text}</FixedScaleText> : text)}
102
102
  </>
103
103
  );
104
104
  };
@@ -147,7 +147,7 @@ const styles = StyleSheet.create({
147
147
  minWidth: 1,
148
148
  alignItems: 'center',
149
149
  justifyContent: 'center',
150
- gap: TabBarMetrics.itemIconLabelGap,
150
+ gap: Spacing.XXS,
151
151
  },
152
152
  slot: {
153
153
  height: TabBarMetrics.indicatorHeight,
@@ -166,11 +166,6 @@ const styles = StyleSheet.create({
166
166
  },
167
167
  labelBox: {
168
168
  alignSelf: 'stretch',
169
- // fixed to the design line height so a capped-scale label overflows downward and the
170
- // icon above it stays put
171
- height: TabBarMetrics.labelHeight,
172
- justifyContent: 'flex-start',
173
- overflow: 'visible',
174
169
  },
175
170
  label: {
176
171
  textAlign: 'center',
@@ -13,7 +13,7 @@ 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 { CappedScaleText } from './TabBarIcon';
16
+ import { FixedScaleText } from './TabBarIcon';
17
17
  import { usePressLift } from './usePressLift';
18
18
  import { ActiveIndicator } from './ActiveIndicator';
19
19
  import {
@@ -116,7 +116,7 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
116
116
  <CtaIcon icon={floatingButton.icon} />
117
117
  </Animated.View>
118
118
  <View style={styles.railCtaLabel}>
119
- <CappedScaleText>
119
+ <FixedScaleText>
120
120
  <Text
121
121
  numberOfLines={1}
122
122
  typography={'label_xs_medium'}
@@ -124,7 +124,7 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
124
124
  >
125
125
  {floatingButton.label}
126
126
  </Text>
127
- </CappedScaleText>
127
+ </FixedScaleText>
128
128
  {floatingButton.labelContent && (
129
129
  <View pointerEvents={'none'} style={styles.railCtaLabelOverlay}>
130
130
  {floatingButton.labelContent}
@@ -3,11 +3,7 @@ import React from 'react';
3
3
  import { useWindowDimensions, View } from 'react-native';
4
4
  import { useScreenLayout } from '../../Layout/utils';
5
5
  import { FloatingButtonProps } from '../types';
6
- import {
7
- getSystemIndicatorHeight,
8
- hasCenterAction,
9
- HorizontalTabBar,
10
- } from './BottomTabBar';
6
+ import { getSystemIndicatorHeight, HorizontalTabBar } from './BottomTabBar';
11
7
  import { getHorizontalBarWidth, TabBarMetrics } from './metrics';
12
8
  import { getTabItems } from './tabItems';
13
9
  import { DuoRailTabBar } from './DuoRailTabBar';
@@ -38,7 +34,6 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
38
34
  const { insets } = useScreenLayout();
39
35
  const { width, height } = useWindowDimensions();
40
36
  const { floatingButton, activeTintColor, inactiveTintColor } = props;
41
- const tabItems = getTabItems(props);
42
37
 
43
38
  return (
44
39
  <View
@@ -50,15 +45,8 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
50
45
  >
51
46
  <HorizontalTabBar
52
47
  fixedFontScale
53
- items={tabItems}
54
- width={getHorizontalBarWidth(
55
- width,
56
- height,
57
- insets.left,
58
- insets.right,
59
- tabItems.length,
60
- hasCenterAction(floatingButton, tabItems.length),
61
- )}
48
+ items={getTabItems(props)}
49
+ width={getHorizontalBarWidth(width, height, insets.left, insets.right)}
62
50
  floatingButton={floatingButton}
63
51
  activeTintColor={activeTintColor}
64
52
  inactiveTintColor={inactiveTintColor}
@@ -71,30 +71,6 @@ export const FixedScaleText: React.FC<{ children: React.ReactNode }> = ({
71
71
  );
72
72
  };
73
73
 
74
- // the tab labels follow the OS/host font scale but never grow past this, so the fixed-height
75
- // container and the notch stay aligned
76
- const TAB_LABEL_MAX_SCALE = 1.2;
77
-
78
- /**
79
- * caps the label scale at `TAB_LABEL_MAX_SCALE`: it keeps following the host/OS font scale but
80
- * stops growing past 1.2× so it fits the fixed bar geometry
81
- */
82
- export const CappedScaleText: React.FC<{ children: React.ReactNode }> = ({
83
- children,
84
- }) => {
85
- const context = useContext<any>(MiniAppContext);
86
- return (
87
- <MiniAppContext.Provider
88
- value={{
89
- ...context,
90
- fontScale: { ...context?.fontScale, maxScaleRate: TAB_LABEL_MAX_SCALE },
91
- }}
92
- >
93
- {children}
94
- </MiniAppContext.Provider>
95
- );
96
- };
97
-
98
74
  /**
99
75
  * numbers use the error color, text the warning color (Figma Badge round numeric / text)
100
76
  */
@@ -9,36 +9,23 @@ import { Colors, Radius, Spacing } from '../../Consts';
9
9
  * - vertical: iPhone Duo rail, center action under the container
10
10
  */
11
11
  const TabBarMetrics = {
12
- height: 60,
12
+ height: 68,
13
13
  margin: Spacing.S,
14
- paddingVertical: Spacing.XS,
14
+ paddingVertical: Spacing.S,
15
15
  paddingHorizontal: Spacing.M,
16
- itemGap: 1,
17
- // gap between the icon slot and the label inside a tab (Figma: none)
18
- itemIconLabelGap: 0,
19
- // .Nav/Background: no backdrop blur, so use the Figma Blur=Off spec (opacity 95%, no blur) plus the 1px
20
- // border — nearly solid, matching the frosted sample; surface keeps dark-mode support
21
- backgroundOpacity: 0.95,
22
- // white 60% hairline around the center action button (Figma Icon Button border)
23
- ctaBorderOpacity: 0.6,
16
+ itemGap: Spacing.XS,
24
17
  iconSize: 24,
25
18
  // active indicator behind the icon: 24 icon + XS·M padding
26
19
  indicatorPaddingVertical: Spacing.XS,
27
20
  indicatorPaddingHorizontal: Spacing.M,
28
21
  indicatorRadius: Radius.XL,
29
22
  indicatorHeight: 32,
30
- // label_s_medium line height: the label box keeps this height so a scaled label grows
31
- // downward instead of pushing the icon up
32
- labelHeight: 18,
33
23
  // home indicator / gesture bar area under the container
34
24
  systemIndicatorHeight: 24,
35
25
  // unfolded foldables (short side from 600, the smallest in the spec) use 65% of the short
36
26
  // side, centered; phones and folded outer displays fill the window
37
27
  foldableMinShortSide: 600,
38
28
  foldableWidthRatio: 0.65,
39
- // a 2-tab bar doesn't stretch to the full width: each item is this fixed width and the bar is
40
- // sized to its content, centered (Figma Navigation · 2 Tab: item 100, bar ~225)
41
- twoTabItemWidth: 100,
42
29
  ctaSlotWidth: 92,
43
30
  ctaSize: 48,
44
31
  // the icon sits 10 inside the 48 button (prototype .cta: 28 icon), 12 inside the 52 rail one
@@ -149,17 +136,7 @@ const getHorizontalBarWidth = (
149
136
  height: number,
150
137
  insetLeft: number,
151
138
  insetRight: number,
152
- itemCount: number,
153
- hasCta: boolean,
154
139
  ) => {
155
- // 2 tabs (no center action) stay content-sized and centered instead of filling the window
156
- if (itemCount === 2 && !hasCta) {
157
- return (
158
- TabBarMetrics.twoTabItemWidth * 2 +
159
- TabBarMetrics.paddingHorizontal * 2 +
160
- TabBarMetrics.itemGap
161
- );
162
- }
163
140
  const shortSide = Math.min(width, height);
164
141
  if (shortSide >= TabBarMetrics.foldableMinShortSide) {
165
142
  return shortSide * TabBarMetrics.foldableWidthRatio;
@@ -16,10 +16,10 @@ describe('getModalLayout', () => {
16
16
  expect(layout.isFoldable).toBe(false);
17
17
  expect(layout.popup.width).toBe(342);
18
18
  expect(layout.popup.maxHeight).toBeUndefined();
19
- expect(layout.popup.radius).toBe(16);
19
+ expect(layout.popup.radius).toBe(24);
20
20
  expect(layout.sheet.width).toBeUndefined();
21
21
  expect(layout.sheet.maxHeight).toBeUndefined();
22
- expect(layout.sheet.radius).toBe(12);
22
+ expect(layout.sheet.radius).toBe(24);
23
23
  expect(layout.scrimOpacity).toBe(0.6);
24
24
  });
25
25
 
@@ -33,7 +33,7 @@ describe('getModalLayout', () => {
33
33
  expect(layout.isFoldable).toBe(false);
34
34
  expect(layout.popup.width).toBe(844 - 48);
35
35
  expect(layout.popup.maxHeight).toBeUndefined();
36
- expect(layout.popup.radius).toBe(16);
36
+ expect(layout.popup.radius).toBe(24);
37
37
  expect(layout.sheet.width).toBeUndefined();
38
38
  expect(layout.scrimOpacity).toBe(0.6);
39
39
  });
@@ -112,9 +112,9 @@ const getModalLayout = ({
112
112
  minWidth: popupWidth,
113
113
  maxWidth: popupWidth,
114
114
  margin: Spacing.XL,
115
- radius: Radius.L,
115
+ radius: Radius.XL,
116
116
  },
117
- sheet: { margin: 0, radius: Radius.M },
117
+ sheet: { margin: 0, radius: Radius.XL },
118
118
  scrimOpacity: SCRIM_OPACITY,
119
119
  };
120
120
  }
@@ -36,6 +36,7 @@ export type BottomTabProps = {
36
36
  };
37
37
 
38
38
  export type FloatingButtonProps = {
39
+ container?: React.ReactNode;
39
40
  icon: string | React.ReactNode;
40
41
  label: string;
41
42
  onPress: () => void;
@@ -183,18 +183,12 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
183
183
  const body = (
184
184
  <>
185
185
  {!!image && (
186
- <Image
187
- source={{ uri: image }}
188
- style={[
189
- styles.image,
190
- { borderTopLeftRadius: radius, borderTopRightRadius: radius },
191
- ]}
192
- />
186
+ <Image source={{ uri: image }} style={styles.image} />
193
187
  )}
194
188
  <View style={[styles.content, isFoldable && styles.shrink]}>
195
189
  <Text
196
190
  typography={'header_default_bold'}
197
- numberOfLines={3}
191
+ numberOfLines={2}
198
192
  accessibilityLabel={'title_popup_notify'}
199
193
  // keeps the title clear of the close button on a text only popup
200
194
  style={
@@ -250,6 +244,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
250
244
  width: popupWidth,
251
245
  maxHeight,
252
246
  },
247
+ styles.card,
253
248
  showBaseLineDebug && styles.debugBaseLine,
254
249
  ]}
255
250
  >
@@ -262,9 +257,10 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
262
257
 
263
258
  const styles = StyleSheet.create({
264
259
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
260
+ // clips the image and content to the card radius
261
+ card: { overflow: 'hidden' },
265
262
  image: {
266
263
  aspectRatio: 1.7777,
267
- overflow: 'hidden',
268
264
  },
269
265
  shrink: { flexShrink: 1 },
270
266
  content: { padding: Spacing.XL },
package/Text/utils.ts CHANGED
@@ -9,18 +9,13 @@ const MAX_DEVICE_SCALE = 5;
9
9
  const useScaleSize = (size: number, scaleRate?: number) => {
10
10
  const { width } = useWindowDimensions();
11
11
  const context = useContext<any>(MiniAppContext);
12
- // opt-in ceiling: when set (arg or context), the final size never exceeds size × capRate,
13
- // in both the mini-app userScaleRate path and the OS path. Undefined keeps the old behavior.
14
- const capRate = scaleRate ?? context?.fontScale?.maxScaleRate;
15
- const cap = (scaled: number) =>
16
- capRate != null ? Math.min(scaled, size * capRate) : scaled;
17
12
  if (context?.fontScale?.useOSScaleRate === false) {
18
- return cap(size * (context.fontScale.userScaleRate ?? 1));
13
+ return size * (context.fontScale.userScaleRate ?? 1);
19
14
  }
20
15
  const fontScale = PixelRatio.getFontScale();
21
16
  const deviceScale = width / DEFAULT_SCREEN_SIZE;
22
17
 
23
- const maxScaleRate = capRate ?? MAX_FONT_SCALE;
18
+ const maxScaleRate = scaleRate ?? MAX_FONT_SCALE;
24
19
 
25
20
  let fontSizeDeviceScale = size;
26
21
  let fontSizeOSScale = size;
@@ -39,7 +34,7 @@ const useScaleSize = (size: number, scaleRate?: number) => {
39
34
  );
40
35
  }
41
36
 
42
- return cap(Math.max(fontSizeDeviceScale, fontSizeOSScale));
37
+ return Math.max(fontSizeDeviceScale, fontSizeOSScale);
43
38
  };
44
39
 
45
40
  const scaleSize = (size: number) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.169.4-beta.7",
3
+ "version": "0.169.4",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},