@momo-kits/foundation 0.169.5 → 0.169.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 & {
@@ -68,27 +67,17 @@ const useCenterActionPress = () => {
68
67
  };
69
68
 
70
69
  /**
71
- * Center action (.Nav/CTA): a button rising out of the notch, its label below.
70
+ * Center action (.Nav/CTA): a button rising out of the notch, its label below
71
+ * (growing downward when scaled).
72
72
  */
73
- const CenterAction: React.FC<{
74
- floatingButton: FloatingButtonProps;
75
- fixedFontScale?: boolean;
76
- }> = ({ floatingButton, fixedFontScale }) => {
73
+ const CenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
74
+ floatingButton,
75
+ }) => {
77
76
  const { theme } = useContext(ApplicationContext);
78
77
  const context = useContext<any>(MiniAppContext);
79
78
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
80
79
  const { icon, label, onPress, labelContent } = floatingButton;
81
80
  const { buttonStyle, onPressIn, onPressOut } = useCenterActionPress();
82
- const labelText = (
83
- <Text
84
- numberOfLines={1}
85
- typography={'label_s_medium'}
86
- color={theme.colors.text.secondary}
87
- >
88
- {label}
89
- </Text>
90
- );
91
-
92
81
  let iconNode = icon;
93
82
  if (typeof icon === 'string') {
94
83
  iconNode = (
@@ -100,11 +89,6 @@ const CenterAction: React.FC<{
100
89
  );
101
90
  }
102
91
 
103
- let labelNode = labelText;
104
- if (fixedFontScale) {
105
- labelNode = <FixedScaleText>{labelText}</FixedScaleText>;
106
- }
107
-
108
92
  return (
109
93
  <View style={[styles.ctaSlot, showBaseLineDebug && styles.debugBaseLine]}>
110
94
  <Pressable
@@ -125,7 +109,13 @@ const CenterAction: React.FC<{
125
109
  {iconNode}
126
110
  </Animated.View>
127
111
  <View style={styles.ctaLabel}>
128
- {labelNode}
112
+ <Text
113
+ numberOfLines={1}
114
+ typography={'label_s_medium'}
115
+ color={theme.colors.text.secondary}
116
+ >
117
+ {label}
118
+ </Text>
129
119
  {labelContent && (
130
120
  <View pointerEvents={'none'} style={styles.ctaLabelOverlay}>
131
121
  {labelContent}
@@ -147,15 +137,7 @@ const HorizontalTabBar: React.FC<{
147
137
  activeTintColor: string;
148
138
  inactiveTintColor: string;
149
139
  floatingButton?: FloatingButtonProps;
150
- fixedFontScale?: boolean;
151
- }> = ({
152
- items,
153
- width,
154
- activeTintColor,
155
- inactiveTintColor,
156
- floatingButton,
157
- fixedFontScale,
158
- }) => {
140
+ }> = ({ items, width, activeTintColor, inactiveTintColor, floatingButton }) => {
159
141
  const withCta = hasCenterAction(floatingButton, items.length);
160
142
  const [layouts, setLayouts] = useState<
161
143
  Record<string, { x: number; y: number; width: number }>
@@ -181,7 +163,6 @@ const HorizontalTabBar: React.FC<{
181
163
  onLongPress={item.onLongPress}
182
164
  activeTintColor={activeTintColor}
183
165
  inactiveTintColor={inactiveTintColor}
184
- fixedFontScale={fixedFontScale}
185
166
  />
186
167
  ));
187
168
  };
@@ -192,10 +173,7 @@ const HorizontalTabBar: React.FC<{
192
173
  return (
193
174
  <>
194
175
  {renderItems(items.slice(0, middle))}
195
- <CenterAction
196
- floatingButton={floatingButton}
197
- fixedFontScale={fixedFontScale}
198
- />
176
+ <CenterAction floatingButton={floatingButton} />
199
177
  {renderItems(items.slice(middle))}
200
178
  </>
201
179
  );
@@ -10,7 +10,7 @@ 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
16
  import { Colors, Spacing } from '../../Consts';
@@ -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
+ * Tab item body: the icon in its indicator slot, the label in a fixed slot under it so a
39
+ * scaled label grows down. The bar slides one shared active indicator behind the icon slots.
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,9 +69,9 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
72
69
  </Text>
73
70
  );
74
71
 
75
- // The active label fades in over the inactive one.
76
- const text = (
77
- <View style={!vertical && styles.labelBox}>
72
+ // Active label fades in over the inactive one; a scaled label overflows down.
73
+ const labelNode = (
74
+ <View style={vertical ? styles.railLabelSlot : styles.labelSlot}>
78
75
  {renderText(inactiveTintColor)}
79
76
  <Animated.View
80
77
  pointerEvents={'none'}
@@ -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}>
@@ -168,9 +160,13 @@ const styles = StyleSheet.create({
168
160
  paddingVertical: TabBarMetrics.railIndicatorPaddingVertical,
169
161
  paddingHorizontal: TabBarMetrics.indicatorPaddingHorizontal,
170
162
  },
171
- labelBox: {
163
+ labelSlot: {
164
+ height: TabBarMetrics.labelHeight,
172
165
  alignSelf: 'stretch',
173
166
  },
167
+ railLabelSlot: {
168
+ height: TabBarMetrics.railLabelHeight,
169
+ },
174
170
  label: {
175
171
  textAlign: 'center',
176
172
  },
@@ -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 {
@@ -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}
@@ -116,15 +114,13 @@ 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
- <Text
121
- numberOfLines={1}
122
- typography={'label_xs_medium'}
123
- color={theme.colors.text.secondary}
124
- >
125
- {floatingButton.label}
126
- </Text>
127
- </FixedScaleText>
117
+ <Text
118
+ numberOfLines={1}
119
+ typography={'label_xs_medium'}
120
+ color={theme.colors.text.secondary}
121
+ >
122
+ {floatingButton.label}
123
+ </Text>
128
124
  {floatingButton.labelContent && (
129
125
  <View pointerEvents={'none'} style={styles.railCtaLabelOverlay}>
130
126
  {floatingButton.labelContent}
@@ -44,7 +44,6 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
44
44
  ]}
45
45
  >
46
46
  <HorizontalTabBar
47
- fixedFontScale
48
47
  items={getTabItems(props)}
49
48
  width={getHorizontalBarWidth(width, height, insets.left, insets.right)}
50
49
  floatingButton={floatingButton}
@@ -7,9 +7,10 @@ import Animated, {
7
7
  } from 'react-native-reanimated';
8
8
 
9
9
  import { Colors, Spacing } from '../../Consts';
10
- import { ApplicationContext, MiniAppContext } from '../../Context';
10
+ import { ApplicationContext } from '../../Context';
11
11
  import { BadgeDot } from '../../Badge';
12
12
  import { Text } from '../../Text';
13
+ import ScaleSizeProvider from '../ScaleSizeProvider';
13
14
  import { TabBarMetrics, TabBarMotion } from './metrics';
14
15
 
15
16
  type Props = {
@@ -52,29 +53,9 @@ export const getTabBadge = (
52
53
  return text;
53
54
  };
54
55
 
55
- /**
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.
58
- */
59
- export const FixedScaleText: React.FC<{ children: React.ReactNode }> = ({
60
- children,
61
- }) => {
62
- const context = useContext<any>(MiniAppContext);
63
- return (
64
- <MiniAppContext.Provider
65
- value={{
66
- ...context,
67
- fontScale: { useOSScaleRate: false, userScaleRate: 1 },
68
- }}
69
- >
70
- {children}
71
- </MiniAppContext.Provider>
72
- );
73
- };
74
-
75
56
  /**
76
57
  * Label badge of a tab: numbers use the error color, text the warning color (Figma Badge
77
- * round numeric / text).
58
+ * round numeric / text). The text keeps its design size to fit the 16 height.
78
59
  */
79
60
  const TabBadge: React.FC<{ label: string; vertical?: boolean }> = ({
80
61
  label,
@@ -96,7 +77,7 @@ const TabBadge: React.FC<{ label: string; vertical?: boolean }> = ({
96
77
  },
97
78
  ]}
98
79
  >
99
- <FixedScaleText>
80
+ <ScaleSizeProvider scaleSizeMaxRate={1}>
100
81
  <Text
101
82
  numberOfLines={1}
102
83
  typography={'action_xxs_bold'}
@@ -104,7 +85,7 @@ const TabBadge: React.FC<{ label: string; vertical?: boolean }> = ({
104
85
  >
105
86
  {label}
106
87
  </Text>
107
- </FixedScaleText>
88
+ </ScaleSizeProvider>
108
89
  </View>
109
90
  );
110
91
  if (vertical) {
@@ -14,6 +14,7 @@ import {
14
14
  } from 'react-native';
15
15
  import { Icon } from '../../Icon';
16
16
  import { BottomTabProps } from '../types';
17
+ import ScaleSizeProvider from '../ScaleSizeProvider';
17
18
  import BottomTabBar, { hasCenterAction } from './BottomTabBar';
18
19
  import { TabBarMetrics } from './metrics';
19
20
  import { getTabBadge } from './TabBarIcon';
@@ -131,24 +132,18 @@ const BottomTab: React.FC<BottomTabProps> = ({
131
132
  },
132
133
  };
133
134
 
135
+ // One cap for the whole bar (labels, CTA, custom content) so it fits the fixed bar and notch.
134
136
  const renderTabBar = (props: BottomTabBarProps) => {
135
- if (isIPhoneDuo) {
136
- return (
137
- <DuoTabBar
137
+ const TabBar = isIPhoneDuo ? DuoTabBar : BottomTabBar;
138
+ return (
139
+ <ScaleSizeProvider scaleSizeMaxRate={TabBarMetrics.labelMaxScale}>
140
+ <TabBar
138
141
  {...props}
139
142
  floatingButton={floatingButton}
140
143
  inactiveTintColor={theme.colors.text.secondary}
141
144
  activeTintColor={theme.colors.primary}
142
145
  />
143
- );
144
- }
145
- return (
146
- <BottomTabBar
147
- {...props}
148
- floatingButton={floatingButton}
149
- inactiveTintColor={theme.colors.text.secondary}
150
- activeTintColor={theme.colors.primary}
151
- />
146
+ </ScaleSizeProvider>
152
147
  );
153
148
  };
154
149
 
@@ -19,16 +19,15 @@ const TabBarMetrics = {
19
19
  indicatorPaddingHorizontal: Spacing.M,
20
20
  indicatorRadius: Radius.XL,
21
21
  indicatorHeight: 32,
22
- // home indicator / gesture bar area under the container
22
+ labelMaxScale: 1.2,
23
+ labelHeight: 18,
24
+ railLabelHeight: 14,
23
25
  systemIndicatorHeight: 24,
24
- // Unfolded foldables (short side from 600) use 65% of it, centered; others fill the window
25
26
  foldableMinShortSide: 600,
26
27
  foldableWidthRatio: 0.65,
27
28
  ctaSlotWidth: 92,
28
29
  ctaSize: 48,
29
- // the icon sits 10 inside the 48 button (prototype .cta: 28 icon), 12 inside the 52 rail one
30
30
  ctaIconSize: 28,
31
- // the button rises 22 above the container top edge
32
31
  ctaLift: 22,
33
32
  ctaLabelGap: Spacing.M,
34
33
  ctaLabelHeight: 18,
@@ -1,16 +1,23 @@
1
- import React, { FC } from 'react';
2
- import { ScaleSizeContext } from '../Context';
1
+ import React, { FC, useContext, useMemo } from 'react';
2
+ import { MiniAppContext } from '../Context';
3
3
  import { ScaleSizeProviderProps } from './types';
4
4
 
5
- /** @deprecated Max font scale is fixed at 1.5; this provider no longer affects scaling. */
5
+ /**
6
+ * Caps the font scale of its subtree at `scaleSizeMaxRate` × the design size
7
+ * (`useScaleSize`), passed down in MiniAppContext. The nearest provider wins;
8
+ * an undefined rate lifts an outer cap.
9
+ */
6
10
  const ScaleSizeProvider: FC<ScaleSizeProviderProps> = ({
7
11
  scaleSizeMaxRate,
8
12
  children,
9
13
  }) => {
14
+ const context = useContext<object | undefined>(MiniAppContext);
15
+ const value = useMemo(() => {
16
+ return { ...context, scaleSizeMaxRate };
17
+ }, [context, scaleSizeMaxRate]);
18
+
10
19
  return (
11
- <ScaleSizeContext.Provider value={{ scaleSizeMaxRate }}>
12
- {children}
13
- </ScaleSizeContext.Provider>
20
+ <MiniAppContext.Provider value={value}>{children}</MiniAppContext.Provider>
14
21
  );
15
22
  };
16
23
 
package/Context/index.ts CHANGED
@@ -10,7 +10,10 @@ const MiniAppContext = (Platform as any).MiniAppContext ?? Context;
10
10
  const ScreenContext = (Platform as any).ScreenContext ?? Context;
11
11
  const ComponentContext = (Platform as any).ComponentContext ?? Context;
12
12
  const SkeletonContext = createContext({ loading: false });
13
- /** @deprecated Max font scale is fixed at 1.5; this context is no longer read. */
13
+ /**
14
+ * @deprecated Not read: wrap the subtree in `ScaleSizeProvider`, which caps the
15
+ * font scale through MiniAppContext. Kept for mini apps built on older kits.
16
+ */
14
17
  const ScaleSizeContext = createContext<{ scaleSizeMaxRate?: number }>({
15
18
  scaleSizeMaxRate: undefined,
16
19
  });
package/Text/utils.ts CHANGED
@@ -6,21 +6,28 @@ const DEFAULT_SCREEN_SIZE = 375;
6
6
  const MAX_FONT_SCALE = 1.5;
7
7
  const MAX_DEVICE_SCALE = 5;
8
8
 
9
+ type MiniAppScale = {
10
+ scaleSizeMaxRate?: number;
11
+ fontScale?: { useOSScaleRate?: boolean; userScaleRate?: number };
12
+ };
13
+
9
14
  /**
10
- * Scales `size` by screen width (at most +5) and OS font scale (at most
11
- * `scaleRate`, default 1.5), taking the larger; a mini app can opt out of the
12
- * OS rate and use its own `fontScale.userScaleRate` instead.
15
+ * Scales `size` by screen width (+5 max) and OS font scale (max `scaleRate`,
16
+ * else the ScaleSizeProvider rate, else 1.5), taking the larger, or by the mini
17
+ * app `fontScale.userScaleRate`; a provider rate caps both at `size × rate`.
13
18
  */
14
19
  const useScaleSize = (size: number, scaleRate?: number) => {
15
20
  const { width } = useWindowDimensions();
16
- const context = useContext<any>(MiniAppContext);
21
+ const context = useContext<MiniAppScale | undefined>(MiniAppContext);
22
+ const scaleSizeMaxRate = context?.scaleSizeMaxRate;
23
+ const maxSize = scaleSizeMaxRate ? size * scaleSizeMaxRate : Infinity;
17
24
  if (context?.fontScale?.useOSScaleRate === false) {
18
- return size * (context.fontScale.userScaleRate ?? 1);
25
+ return Math.min(size * (context.fontScale.userScaleRate ?? 1), maxSize);
19
26
  }
20
27
  const fontScale = PixelRatio.getFontScale();
21
28
  const deviceScale = width / DEFAULT_SCREEN_SIZE;
22
29
 
23
- const maxScaleRate = scaleRate ?? MAX_FONT_SCALE;
30
+ const maxScaleRate = scaleRate ?? scaleSizeMaxRate ?? MAX_FONT_SCALE;
24
31
 
25
32
  let fontSizeDeviceScale = size;
26
33
  let fontSizeOSScale = size;
@@ -39,7 +46,7 @@ const useScaleSize = (size: number, scaleRate?: number) => {
39
46
  );
40
47
  }
41
48
 
42
- return Math.max(fontSizeDeviceScale, fontSizeOSScale);
49
+ return Math.min(Math.max(fontSizeDeviceScale, fontSizeOSScale), maxSize);
43
50
  };
44
51
 
45
52
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.169.5",
3
+ "version": "0.169.6",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},