@momo-kits/foundation 0.165.1-sp.4 → 0.165.2-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.
@@ -233,18 +233,17 @@ const BottomTab: React.FC<BottomTabProps> = ({
233
233
  });
234
234
  }, [itemWidth, indicatorAnimated]);
235
235
 
236
- // TEMP: commented out to test whether the blur reset is needed on a real host.
237
- // Without it, pushing a screen from a tab other than the first leaves the flag true, so that
238
- // pushed screen gets the host floating-arrow instead of its own card swipe-back.
239
- // useFocusEffect(
240
- // React.useCallback(() => {
241
- // NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(
242
- // activeIndex.current !== 0,
243
- // );
244
- // return () =>
245
- // NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(false);
246
- // }, []),
247
- // );
236
+ // Focus, not mount: pushing a screen from a tab leaves BottomTab mounted but blurred, and
237
+ // onFocus only fires on tab changes.
238
+ useFocusEffect(
239
+ React.useCallback(() => {
240
+ NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(
241
+ activeIndex.current !== 0,
242
+ );
243
+ return () =>
244
+ NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(false);
245
+ }, []),
246
+ );
248
247
 
249
248
  const onFocus = (e: any) => {
250
249
  activeIndex.current = tabs.findIndex(i => e.target.includes(i.name));
@@ -10,10 +10,20 @@ import { Image } from '../../Image';
10
10
  import { Colors } from '../../Consts';
11
11
  import { useNormalizedSharedValue } from '../utils';
12
12
 
13
+ const ASPECT_RATIOS: Record<
14
+ NonNullable<HeaderAnimatedProps['aspectRatio']>,
15
+ number
16
+ > = {
17
+ '16/9': 16 / 9,
18
+ '3/2': 3 / 2,
19
+ '1/1': 1 / 1,
20
+ };
21
+
13
22
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
14
23
  animatedValue,
15
24
  image,
16
25
  useScale = true,
26
+ aspectRatio = '16/9',
17
27
  children,
18
28
  }) => {
19
29
  const sharedValue = useNormalizedSharedValue(animatedValue);
@@ -40,7 +50,13 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
40
50
  });
41
51
 
42
52
  return (
43
- <Animated.View style={[styles.container, animatedStyle]}>
53
+ <Animated.View
54
+ style={[
55
+ styles.container,
56
+ { aspectRatio: ASPECT_RATIOS[aspectRatio] },
57
+ animatedStyle,
58
+ ]}
59
+ >
44
60
  {children ?? (
45
61
  <Image
46
62
  source={{
@@ -54,7 +70,7 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
54
70
  };
55
71
 
56
72
  const styles = StyleSheet.create({
57
- container: { width: '100%', aspectRatio: 16 / 9 },
73
+ container: { width: '100%' },
58
74
  verifiedDot: {
59
75
  width: 4,
60
76
  height: 4,
@@ -1,5 +1,5 @@
1
1
  import { BackHandler, StyleSheet, View } from 'react-native';
2
- import { HeaderBackProps } from '../types';
2
+ import type { HeaderBackProps } from '../types';
3
3
  import React, { useCallback, useContext, useEffect } from 'react';
4
4
  import { ApplicationContext, MiniAppContext } from '../../Context';
5
5
  import { PopupNotify } from '../../Popup';
@@ -1,10 +1,5 @@
1
1
  import React, { useContext } from 'react';
2
- import {
3
- Dimensions,
4
- StyleSheet,
5
- TouchableOpacity,
6
- View,
7
- } from 'react-native';
2
+ import { Dimensions, StyleSheet, TouchableOpacity, View } from 'react-native';
8
3
  import Animated, {
9
4
  Extrapolation,
10
5
  interpolate,
@@ -21,10 +16,7 @@ import {
21
16
  import { Image } from '../../Image';
22
17
  import { Icon } from '../../Icon';
23
18
  import { Skeleton } from '../../Skeleton';
24
- import {
25
- AnimatedCompatValue,
26
- useNormalizedSharedValue,
27
- } from '../utils';
19
+ import { AnimatedCompatValue, useNormalizedSharedValue } from '../utils';
28
20
 
29
21
  type HeaderTitleInterpolate = {
30
22
  inputRange: number[];
@@ -41,7 +33,9 @@ type HeaderTitleExtraProps = {
41
33
  /**
42
34
  * default header title used for nav
43
35
  */
44
- const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = props => {
36
+ const HeaderTitle: React.FC<
37
+ HeaderTitleExtraProps & { [key: string]: any }
38
+ > = props => {
45
39
  const context = useContext<any>(MiniAppContext);
46
40
 
47
41
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
@@ -86,10 +80,13 @@ const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = pr
86
80
  {
87
81
  fontSize: useScaleSize(15),
88
82
  lineHeight: useScaleSize(22),
89
- fontWeight: 'bold',
90
83
  },
91
84
  {
92
- fontFamily: exportFontFamily('bold', 'action_xs_bold'),
85
+ fontFamily: exportFontFamily(
86
+ 'bold',
87
+ 'action_xs_bold',
88
+ 'MoMoTrustDisplay',
89
+ ),
93
90
  color: props.tintColor,
94
91
  },
95
92
  animatedStyle,
@@ -381,6 +378,7 @@ const TitleUser: React.FC<TitleUserProps> = ({
381
378
  <View style={Styles.row}>
382
379
  <Text
383
380
  typography="action_xs_bold"
381
+ fontFamily='MoMoTrustDisplay'
384
382
  color={tintColor}
385
383
  numberOfLines={1}
386
384
  >
@@ -450,7 +448,9 @@ const TitleLocation: React.FC<TitleLocationProps> = ({
450
448
  </Text>
451
449
  )}
452
450
  <View style={Styles.row}>
453
- <Text typography="action_xs_bold" color={tintColor} numberOfLines={1}>
451
+ <Text typography="action_xs_bold"
452
+ fontFamily='MoMoTrustDisplay'
453
+ color={tintColor} numberOfLines={1}>
454
454
  {location}
455
455
  </Text>
456
456
  <Icon
@@ -512,6 +512,8 @@ const TitleJourney: React.FC<TitleJourneyProps> = ({
512
512
  style={{ maxWidth: startWidth }}
513
513
  color={tintColor}
514
514
  numberOfLines={1}
515
+ fontFamily='MoMoTrustDisplay'
516
+
515
517
  >
516
518
  {start}
517
519
  </Text>
@@ -527,6 +529,8 @@ const TitleJourney: React.FC<TitleJourneyProps> = ({
527
529
  typography="action_xs_bold"
528
530
  color={tintColor}
529
531
  numberOfLines={1}
532
+ fontFamily='MoMoTrustDisplay'
533
+
530
534
  >
531
535
  {end}
532
536
  </Text>
@@ -1,5 +1,6 @@
1
1
  import React, { useContext, useEffect, useRef, useState } from 'react';
2
2
  import {
3
+ SafeAreaInsetsContext,
3
4
  SafeAreaProvider,
4
5
  useSafeAreaInsets,
5
6
  } from 'react-native-safe-area-context';
@@ -19,12 +20,14 @@ import Navigator from './Navigator';
19
20
  import { getModalOptions, getStackOptions, useNativeCanGoBack } from './utils';
20
21
  import { NavigationContainerProps } from './types';
21
22
  import { ApplicationContext, MiniAppContext } from '../Context';
23
+ import { useDefaultRegularFontFamily } from '../Consts/designSystemFont';
22
24
  import Localize from './Localize';
23
25
  import { Colors, defaultTheme } from '../Consts';
24
26
  import { HeaderLeft } from './Components/HeaderLeft';
25
27
  import { HeaderRight } from './Components/HeaderRight';
26
28
  import { HeaderTitle } from './Components/HeaderTitle';
27
29
  import { HeaderBackground } from './Components/HeaderBackground';
30
+ import { type EdgeInsets } from 'react-native-safe-area-context';
28
31
 
29
32
  const Stack = createStackNavigator();
30
33
 
@@ -61,8 +64,15 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
61
64
  },
62
65
  };
63
66
 
67
+ const parentInsets = useContext(SafeAreaInsetsContext);
68
+
64
69
  return (
65
- <SafeAreaProvider>
70
+ <SafeAreaProvider
71
+ initialMetrics={{
72
+ insets: parentInsets as EdgeInsets,
73
+ frame: { x: 0, y: 0, width: 0, height: 0 },
74
+ }}
75
+ >
66
76
  <MiniAppContext.Provider value={mergedContext}>
67
77
  <Navigation
68
78
  screen={screen}
@@ -93,8 +103,8 @@ const Navigation: React.FC<any> = ({
93
103
  const navigator = useRef(new Navigator(navigationRef, isReady));
94
104
  const [showGrid, setShowGrid] = useState(false);
95
105
  const insets = useSafeAreaInsets();
106
+ const defaultRegularFontFamily = useDefaultRegularFontFamily();
96
107
  const syncCanGoBack = useNativeCanGoBack();
97
-
98
108
  let headerBackground = context?.designSystemConfig?.config?.headerBar;
99
109
  let headerGradient = theme.colors?.gradient;
100
110
 
@@ -173,19 +183,19 @@ const Navigation: React.FC<any> = ({
173
183
  },
174
184
  fonts: {
175
185
  regular: {
176
- fontFamily: 'SFProText',
186
+ fontFamily: defaultRegularFontFamily,
177
187
  fontWeight: '400',
178
188
  },
179
189
  medium: {
180
- fontFamily: 'SFProText',
190
+ fontFamily: defaultRegularFontFamily,
181
191
  fontWeight: '400',
182
192
  },
183
193
  bold: {
184
- fontFamily: 'SFProText',
194
+ fontFamily: defaultRegularFontFamily,
185
195
  fontWeight: '400',
186
196
  },
187
197
  heavy: {
188
- fontFamily: 'SFProText',
198
+ fontFamily: defaultRegularFontFamily,
189
199
  fontWeight: '400',
190
200
  },
191
201
  },
@@ -3,6 +3,7 @@ import { SafeAreaProvider } from 'react-native-safe-area-context';
3
3
  import Navigator from './Navigator';
4
4
  import { WidgetContainerProps } from './types';
5
5
  import { ApplicationContext, MiniAppContext } from '../Context';
6
+ import { useDefaultRegularFontFamily } from '../Consts/designSystemFont';
6
7
  import Localize from './Localize';
7
8
  import { defaultTheme } from '../Consts';
8
9
  import {
@@ -42,6 +43,7 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
42
43
  const isReady = useRef(false);
43
44
  const navigator = useRef(new Navigator(navigationRef, isReady, true));
44
45
  const [currentContext, setCurrentContext] = useState({});
46
+ const defaultRegularFontFamily = useDefaultRegularFontFamily();
45
47
 
46
48
  useEffect(() => {
47
49
  const sub = maxApi?.observer?.('font_scale_config', (data: any) => {
@@ -114,19 +116,19 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
114
116
  },
115
117
  fonts: {
116
118
  regular: {
117
- fontFamily: 'SFProText',
119
+ fontFamily: defaultRegularFontFamily,
118
120
  fontWeight: '400',
119
121
  },
120
122
  medium: {
121
- fontFamily: 'SFProText',
123
+ fontFamily: defaultRegularFontFamily,
122
124
  fontWeight: '400',
123
125
  },
124
126
  bold: {
125
- fontFamily: 'SFProText',
127
+ fontFamily: defaultRegularFontFamily,
126
128
  fontWeight: '400',
127
129
  },
128
130
  heavy: {
129
- fontFamily: 'SFProText',
131
+ fontFamily: defaultRegularFontFamily,
130
132
  fontWeight: '400',
131
133
  },
132
134
  },
@@ -229,6 +229,8 @@ export type HeaderTitleProps = {
229
229
  data: TitleUserProps | TitleLocationProps | TitleJourneyProps;
230
230
  };
231
231
 
232
+ export type HeaderType = 'default' | 'extended' | 'none';
233
+
232
234
  export interface NavigationOptions
233
235
  extends Omit<StackNavigationOptions, 'headerRight' | 'headerTitle'> {
234
236
  preventBack?: PopupNotifyProps;
@@ -302,6 +304,7 @@ export interface HeaderAnimatedProps extends ViewProps {
302
304
  animatedValue: Animated.Value | SharedValue<number>;
303
305
  image: string;
304
306
  useScale?: boolean;
307
+ aspectRatio?: '16/9' | '3/2' | '1/1';
305
308
  }
306
309
 
307
310
  export type BottomTabItemProps = {
package/Button/index.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import React, { FC, useContext, useRef } from 'react';
2
2
  import {
3
+ Platform,
3
4
  StyleSheet,
4
5
  TouchableOpacity,
5
6
  TouchableOpacityProps,
@@ -283,6 +284,17 @@ const Button: FC<ButtonProps> = ({
283
284
  { keypath: 'base pink 2.Ellipse 1.Stroke 1', color: textColor },
284
285
  ]}
285
286
  style={[style, { borderRadius: Spacing.M }]}
287
+ webStyle={
288
+ Platform.OS === 'web'
289
+ ? {
290
+ width: iconSize,
291
+ height: iconSize,
292
+ marginRight: isLeft ? space : 0,
293
+ marginLeft: isLeft ? 0 : space,
294
+ borderRadius: Spacing.M,
295
+ }
296
+ : undefined
297
+ }
286
298
  />
287
299
  );
288
300
  }
@@ -0,0 +1,38 @@
1
+ import { useContext } from 'react';
2
+ import { MiniAppContext } from '../Context';
3
+
4
+ export const NEW_DEFAULT_FONT_FAMILY = 'MomoTrustSans';
5
+ export const LEGACY_DEFAULT_FONT_FAMILY = 'SFProText';
6
+
7
+ /**
8
+ * Font thuoc bo design system moi. Ngoai whitelist thi ha het ve font cu:
9
+ * MoMoTrustDisplay chi duoc dung tu commit header title 5bfde6251 (DS-791),
10
+ * truoc do header lay font theo theme.
11
+ */
12
+ const NEW_FONT_FAMILIES = [NEW_DEFAULT_FONT_FAMILY, 'MoMoTrustDisplay'];
13
+
14
+ /**
15
+ * Cờ whitelist design system, host đẩy xuống qua `decorateProps`
16
+ * (`designSystemConfig.isWhiteListDesignSystem`). Ngoài whitelist thì kit giữ
17
+ * nguyên font cũ.
18
+ */
19
+ export const useIsDesignSystemWhiteList = (): boolean => {
20
+ const context = useContext<any>(MiniAppContext);
21
+ return context?.designSystemConfig?.isWhiteListDesignSystem === true;
22
+ };
23
+
24
+ /**
25
+ * Hạ font mặc định mới về font cũ khi user không nằm trong whitelist.
26
+ * Font do caller chỉ định tường minh (MoMoSignature, AlegreyaSans...) giữ nguyên.
27
+ */
28
+ export const useResolvedFontFamily = (font: string): string => {
29
+ const isWhiteList = useIsDesignSystemWhiteList();
30
+ if (NEW_FONT_FAMILIES.includes(font) && !isWhiteList) {
31
+ return LEGACY_DEFAULT_FONT_FAMILY;
32
+ }
33
+ return font;
34
+ };
35
+
36
+ /** Tên file font Regular tương ứng với nhánh whitelist đang áp dụng. */
37
+ export const useDefaultRegularFontFamily = (): string =>
38
+ `${useResolvedFontFamily(NEW_DEFAULT_FONT_FAMILY)}-Regular`;
package/Consts/index.ts CHANGED
@@ -2,3 +2,4 @@ export * from './colors+spacing+radius';
2
2
  export * from './theme';
3
3
  export * from './styles';
4
4
  export const GRIDVIEW = false;
5
+ export * from './designSystemFont';
package/Consts/theme.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {Context, Theme} from '../Application/types';
1
+ import type {Context, Theme} from '../Application/types';
2
2
  import {Colors} from './colors+spacing+radius';
3
3
 
4
4
  const defaultTheme: Theme = {
@@ -51,7 +51,7 @@ const defaultTheme: Theme = {
51
51
  container: Colors.blue_08,
52
52
  },
53
53
  },
54
- font: 'SFProText',
54
+ font: 'MomoTrustSans',
55
55
  assets: {},
56
56
  };
57
57
 
@@ -105,7 +105,7 @@ const defaultDarkTheme: Theme = {
105
105
  container: Colors.blue_08,
106
106
  },
107
107
  },
108
- font: 'SFProText',
108
+ font: 'MomoTrustSans',
109
109
  assets: {},
110
110
  };
111
111
 
@@ -21,7 +21,7 @@ import {
21
21
  MiniAppContext,
22
22
  } from '../Context';
23
23
  import { Icon } from '../Icon';
24
- import { useScaleSize } from '../Text';
24
+ import { useScaleSize, exportFontFamily } from '../Text';
25
25
  import { Spacing } from '../Consts';
26
26
  import {
27
27
  ErrorView,
@@ -69,6 +69,7 @@ const InputMoney = forwardRef(
69
69
  const context = useContext<any>(MiniAppContext);
70
70
  const scaleHeight = useScaleSize(size === 'small' ? 48 : 56);
71
71
  const scaledFontSize = useScaleSize(20);
72
+ const inputFontFamily = exportFontFamily('bold');
72
73
  const [focused, setFocused] = useState(false);
73
74
  const [displayValue, setDisplayValue] = useState('');
74
75
  const [numericValue, setNumericValue] = useState('');
@@ -236,7 +237,7 @@ const InputMoney = forwardRef(
236
237
  height: '100%',
237
238
  fontSize: scaledFontSize,
238
239
  paddingVertical: Spacing.S,
239
- fontWeight: 'bold',
240
+ fontFamily: inputFontFamily,
240
241
  },
241
242
  {
242
243
  color: textColor,
@@ -21,7 +21,7 @@ import {
21
21
  } from '../Context';
22
22
  import { Colors, Spacing, Styles } from '../Consts';
23
23
  import { Icon } from '../Icon';
24
- import { useScaleSize, Text } from '../Text';
24
+ import { useScaleSize, Text, exportFontFamily } from '../Text';
25
25
  import {
26
26
  ErrorView,
27
27
  getBorderColor,
@@ -128,13 +128,21 @@ const InputPhoneNumber = forwardRef(
128
128
  let dividerHeight = 24;
129
129
  let size14 = useScaleSize(14);
130
130
  let size18 = useScaleSize(18);
131
+ const semiboldFontFamily = exportFontFamily('semibold');
132
+ const boldFontFamily = exportFontFamily('bold');
131
133
  let typography: Typography;
132
134
  if (size === 'small') {
133
- inputTextStyles = { fontSize: size14, fontWeight: '600' };
135
+ inputTextStyles = {
136
+ fontSize: size14,
137
+ fontFamily: semiboldFontFamily,
138
+ };
134
139
  dividerHeight = 24;
135
140
  typography = 'header_s_semibold';
136
141
  } else {
137
- inputTextStyles = { fontSize: size18, fontWeight: '700' };
142
+ inputTextStyles = {
143
+ fontSize: size18,
144
+ fontFamily: boldFontFamily,
145
+ };
138
146
  dividerHeight = 32;
139
147
  typography = 'header_m_bold';
140
148
  }
@@ -23,7 +23,7 @@ import {
23
23
  MiniAppContext,
24
24
  } from '../Context';
25
25
  import { Icon } from '../Icon';
26
- import { Text, useScaleSize } from '../Text';
26
+ import { Text, useScaleSize, exportFontFamily } from '../Text';
27
27
  import { InputRef, InputSearchProps } from './index';
28
28
  import styles from './styles';
29
29
  import { Styles } from '../Consts';
@@ -36,6 +36,7 @@ const TextTyping: FC<any> = ({
36
36
  }) => {
37
37
  const textRef = useRef<TextInput>(null);
38
38
  const scaledFontSize = useScaleSize(12);
39
+ const typingFontFamily = exportFontFamily('regular');
39
40
 
40
41
  const currentIndex = useRef<number>(0);
41
42
  const currentCharIndex = useRef<number>(0);
@@ -108,7 +109,10 @@ const TextTyping: FC<any> = ({
108
109
 
109
110
  return (
110
111
  <TextInput
111
- style={[styles.inputStyle, { fontSize: scaledFontSize }]}
112
+ style={[
113
+ styles.inputStyle,
114
+ { fontSize: scaledFontSize, fontFamily: typingFontFamily },
115
+ ]}
112
116
  ref={textRef}
113
117
  editable={false}
114
118
  autoCorrect={false}
@@ -145,6 +149,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
145
149
  const { theme } = useContext(ApplicationContext);
146
150
  const context = useContext<any>(MiniAppContext);
147
151
  const scaledFontSize = useScaleSize(14);
152
+ const searchFontFamily = exportFontFamily('regular');
148
153
 
149
154
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
150
155
 
@@ -231,6 +236,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
231
236
  {
232
237
  fontSize: scaledFontSize,
233
238
  color: theme.colors.text.default,
239
+ fontFamily: searchFontFamily,
234
240
  },
235
241
  ]}
236
242
  value={value}
@@ -15,7 +15,7 @@ import {
15
15
  } from 'react-native';
16
16
  import { ApplicationContext, MiniAppContext } from '../Context';
17
17
  import { Icon } from '../Icon';
18
- import { Text, useScaleSize } from '../Text';
18
+ import { Text, useScaleSize, exportFontFamily } from '../Text';
19
19
  import { Spacing } from '../Consts';
20
20
  import { ErrorView, FloatingView, getBorderColor, MAX_LENGTH } from './common';
21
21
  import { InputTextAreaProps } from './index';
@@ -59,6 +59,7 @@ const InputTextArea = forwardRef(
59
59
 
60
60
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
61
61
  const scaledFontSize = useScaleSize(14);
62
+ const inputFontFamily = exportFontFamily('regular');
62
63
  const [focused, setFocused] = useState(false);
63
64
  const [inputValue, setInputValue] = useState(defaultValue ?? '');
64
65
  const inputRef = useRef<TextInput | null>(null);
@@ -164,6 +165,7 @@ const InputTextArea = forwardRef(
164
165
  paddingRight: Spacing.XS,
165
166
  paddingVertical: Spacing.XS,
166
167
  fontSize: scaledFontSize,
168
+ fontFamily: inputFontFamily,
167
169
  },
168
170
  {
169
171
  color: textColor,
@@ -2,7 +2,7 @@
2
2
  import React, { FC, memo, useEffect, useRef } from 'react';
3
3
  import { Platform, TextInput, TextStyle } from 'react-native';
4
4
  import { Colors } from '../Consts';
5
- import { useScaleSize } from '../Text';
5
+ import { useScaleSize, exportFontFamily } from '../Text';
6
6
 
7
7
  export interface TextTypingProps {
8
8
  data: string[];
@@ -19,6 +19,7 @@ const TextTyping: FC<TextTypingProps> = ({
19
19
  }) => {
20
20
  const textRef = useRef<TextInput>(null);
21
21
  const scaledFontSize = useScaleSize(12);
22
+ const inputFontFamily = exportFontFamily('regular');
22
23
 
23
24
  const currentIndex = useRef<number>(0);
24
25
  const currentCharIndex = useRef<number>(0);
@@ -95,6 +96,7 @@ const TextTyping: FC<TextTypingProps> = ({
95
96
  {
96
97
  color: Colors.black_12,
97
98
  fontSize: scaledFontSize,
99
+ fontFamily: inputFontFamily,
98
100
  height: Platform.select({
99
101
  android: 60,
100
102
  ios: 36,
@@ -14,7 +14,7 @@ import Animated, {
14
14
  } from 'react-native-reanimated';
15
15
  import { ApplicationContext } from '../Context';
16
16
  import { Icon } from '../Icon';
17
- import { useScaleSize } from '../Text';
17
+ import { useScaleSize, exportFontFamily } from '../Text';
18
18
  import {
19
19
  AnimatedCompatValue,
20
20
  useNormalizedSharedValue,
@@ -48,6 +48,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
48
48
  const sharedValue = useNormalizedSharedValue(animatedValue);
49
49
  const scaledFontSize = useScaleSize(16);
50
50
  const scaledLineHeight = useScaleSize(22);
51
+ const labelFontFamily = exportFontFamily('bold');
51
52
  const maxWidth = useSharedValue(0);
52
53
  const minWidth = size === 'small' ? 36 : 48;
53
54
  const opacityAnimated = useSharedValue(0);
@@ -63,7 +64,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
63
64
 
64
65
  useAnimatedReaction(
65
66
  () => sharedValue.value,
66
- (value) => {
67
+ value => {
67
68
  'worklet';
68
69
  if (!label || !enabled) return;
69
70
  if (value !== lastOffset.value && value > 0) {
@@ -76,7 +77,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
76
77
  widthAnimated.value = withTiming(
77
78
  minWidth,
78
79
  { duration: 100 },
79
- (finished) => {
80
+ finished => {
80
81
  if (finished) runOnJS(setShowText)(false);
81
82
  },
82
83
  );
@@ -85,7 +86,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
85
86
  widthAnimated.value = withTiming(
86
87
  maxWidth.value,
87
88
  { duration: 100 },
88
- (finished) => {
89
+ finished => {
89
90
  if (finished) runOnJS(setShowText)(true);
90
91
  },
91
92
  );
@@ -162,6 +163,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
162
163
  fontSize: scaledFontSize,
163
164
  lineHeight: scaledLineHeight,
164
165
  color: 'white',
166
+ fontFamily: labelFontFamily,
165
167
  },
166
168
  labelStyle,
167
169
  ]}
@@ -188,6 +190,5 @@ const styles = StyleSheet.create({
188
190
  },
189
191
  label: {
190
192
  marginLeft: 8,
191
- fontWeight: '700',
192
193
  },
193
194
  });
package/Layout/Screen.tsx CHANGED
@@ -26,7 +26,10 @@ import {
26
26
  ViewProps,
27
27
  } from 'react-native';
28
28
  import { useSharedValue, withTiming } from 'react-native-reanimated';
29
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
29
+ import {
30
+ useSafeAreaFrame,
31
+ useSafeAreaInsets,
32
+ } from 'react-native-safe-area-context';
30
33
  import { ApplicationContext, ScreenContext } from '../Context';
31
34
  import Navigation from '../Application/Navigation';
32
35
  import {
@@ -96,6 +99,14 @@ export interface ScreenProps extends ViewProps {
96
99
  */
97
100
  footerComponent?: ReactNode;
98
101
 
102
+ /**
103
+ * Optional. If `true`, the footer is inset by the standard horizontal margin
104
+ * (12pt each side). Set `false` for a footer that should span the full width —
105
+ * e.g. one that already carries its own horizontal padding.
106
+ * Defaults to `true`, on every platform.
107
+ */
108
+ marginFooter?: boolean;
109
+
99
110
  /**
100
111
  * Optional. Background color of the screen.
101
112
  */
@@ -104,7 +115,7 @@ export interface ScreenProps extends ViewProps {
104
115
  /**
105
116
  * Optional. layout card offset overlap header banner.
106
117
  */
107
- layoutOffset?: -8 | -24 | -56;
118
+ layoutOffset?: -8 | -24 | -56 | number;
108
119
 
109
120
  /**
110
121
  * Optional. Width of the right header.
@@ -174,6 +185,7 @@ const Screen = forwardRef(
174
185
  scrollViewProps,
175
186
  headerComponent: Header,
176
187
  footerComponent: Footer,
188
+ marginFooter = true,
177
189
  layoutOffset = -56,
178
190
  headerRightWidth = 73,
179
191
  backgroundColor,
@@ -196,6 +208,7 @@ const Screen = forwardRef(
196
208
  const { theme, navigator } = useContext(ApplicationContext);
197
209
  const screen: any = useContext(ScreenContext);
198
210
  const insets = useSafeAreaInsets();
211
+ const frame = useSafeAreaFrame();
199
212
  const heightHeader = useHeaderHeight();
200
213
 
201
214
  /**
@@ -608,12 +621,31 @@ const Screen = forwardRef(
608
621
  }
609
622
  };
610
623
 
624
+ // Cap the screen container height so a scrollable body with a sticky footer
625
+ // can't push the footer past the visible frame (web previews measure the
626
+ // safe-area frame from the seeded SafeAreaProvider metrics). Subtract the
627
+ // bottom tab bar when present and the header unless it's an animated/extended
628
+ // header (those overlay rather than offset the body).
629
+ const getHeight = () => {
630
+ let newHeight = frame.height;
631
+ if (isTab) {
632
+ newHeight -= 64 + Math.min(insets.bottom, 21);
633
+ }
634
+ if (!animatedHeader && headerType !== 'extended') {
635
+ newHeight -= heightHeader;
636
+ }
637
+ return newHeight;
638
+ };
639
+
611
640
  return (
612
641
  <View
613
642
  style={[
614
643
  Styles.flex,
615
644
  {
616
645
  backgroundColor: backgroundColor ?? theme.colors.background.default,
646
+ // Web-only cap so a scrollable body with a sticky footer can't push
647
+ // the footer past the frame. Native handles this without a cap.
648
+ maxHeight: Platform.OS === 'web' ? getHeight() : undefined,
617
649
  },
618
650
  ]}
619
651
  >
@@ -679,7 +711,9 @@ const Screen = forwardRef(
679
711
  },
680
712
  ]}
681
713
  >
682
- <Section>{Footer}</Section>
714
+ {/* The footer inset is a caller decision, not a platform one — a footer
715
+ that carries its own horizontal padding passes `marginFooter={false}`. */}
716
+ <Section useMargin={marginFooter}>{Footer}</Section>
683
717
  </View>
684
718
  )}
685
719
  </KeyboardAvoidingView>
@@ -1,4 +1,5 @@
1
1
  import React, { FC, useContext } from 'react';
2
+ import { Platform } from 'react-native';
2
3
  import AnimatedLottieView from 'lottie-react-native';
3
4
  import animation from '../Assets/DotAnimation.json';
4
5
  import { ApplicationContext } from '../Context';
@@ -29,6 +30,7 @@ const DotLoader: FC<LoaderProps> = ({ color, style }) => {
29
30
  <AnimatedLottieView
30
31
  source={source}
31
32
  style={[{ width: 52, height: 18 }, style]}
33
+ webStyle={Platform.OS === 'web' ? { width: 52, height: 18 } : undefined}
32
34
  autoPlay
33
35
  />
34
36
  );
@@ -1,4 +1,5 @@
1
1
  import React, { FC, useContext } from 'react';
2
+ import { Platform } from 'react-native';
2
3
  import AnimatedLottieView from 'lottie-react-native';
3
4
  import animation from '../Assets/SpinnerAnimation.json';
4
5
  import { hexToRGBA } from './utils';
@@ -21,6 +22,7 @@ const Spinner: FC<LoaderProps> = ({ color, style }) => {
21
22
  <AnimatedLottieView
22
23
  source={source}
23
24
  style={[{ width: 24, height: 24 }, style]}
25
+ webStyle={Platform.OS === 'web' ? { width: 24, height: 24 } : undefined}
24
26
  autoPlay
25
27
  />
26
28
  );
package/Text/index.tsx CHANGED
@@ -7,6 +7,10 @@ import {
7
7
  MiniAppContext,
8
8
  SkeletonContext,
9
9
  } from '../Context';
10
+ import {
11
+ NEW_DEFAULT_FONT_FAMILY,
12
+ useResolvedFontFamily,
13
+ } from '../Consts/designSystemFont';
10
14
  import { scaleSize, useScaleSize } from './utils';
11
15
  import { Skeleton } from '../Skeleton';
12
16
 
@@ -52,6 +56,20 @@ const BarlowCondensed: TypographyWeight = {
52
56
  bold: 'Bold',
53
57
  };
54
58
 
59
+ const MomoTrustSans: TypographyWeight = {
60
+ 100: 'ExtraLight',
61
+ 200: 'ExtraLight',
62
+ 300: 'Light',
63
+ 400: 'Regular',
64
+ 500: 'Medium',
65
+ 600: 'SemiBold',
66
+ 700: 'Bold',
67
+ 800: 'ExtraBold',
68
+ 900: 'ExtraBold',
69
+ normal: 'Regular',
70
+ bold: 'Bold',
71
+ };
72
+
55
73
  const FontStyle: { [key: string]: string } = {
56
74
  italic: 'Italic',
57
75
  normal: '',
@@ -100,7 +118,8 @@ const TypoStyles = (typo: Typography, newFontFamily?: string) => {
100
118
  const typoStyle = styleSheet[typo] ?? styleSheet.paragraph_default;
101
119
  const { fontWeight, fontSize, fontStyle, lineHeight } = typoStyle;
102
120
  const style = FontStyle[fontStyle ?? 'normal'];
103
- const font = newFontFamily ?? theme.font;
121
+ const font = useResolvedFontFamily(newFontFamily ?? theme.font);
122
+ const resolvedDefaultFamily = useResolvedFontFamily(NEW_DEFAULT_FONT_FAMILY);
104
123
  let fontFamily: string;
105
124
 
106
125
  switch (font) {
@@ -108,6 +127,10 @@ const TypoStyles = (typo: Typography, newFontFamily?: string) => {
108
127
  fontFamily = `${font}-${SFProText[fontWeight]}${style}`;
109
128
  break;
110
129
  }
130
+ case 'MomoTrustSans': {
131
+ fontFamily = `${font}-${MomoTrustSans[fontWeight]}`;
132
+ break;
133
+ }
111
134
  case 'AlegreyaSans': {
112
135
  fontFamily = `${font}-${AlegreyaSans[fontWeight]}${style}`;
113
136
  break;
@@ -125,7 +148,11 @@ const TypoStyles = (typo: Typography, newFontFamily?: string) => {
125
148
  break;
126
149
  }
127
150
  default: {
128
- fontFamily = `SFProText-${SFProText[fontWeight]}${style}`;
151
+ fontFamily = `${resolvedDefaultFamily}-${
152
+ resolvedDefaultFamily === NEW_DEFAULT_FONT_FAMILY
153
+ ? MomoTrustSans[fontWeight]
154
+ : SFProText[fontWeight]
155
+ }`;
129
156
  }
130
157
  }
131
158
 
@@ -135,7 +162,10 @@ const TypoStyles = (typo: Typography, newFontFamily?: string) => {
135
162
  fontSize: useScaleSize(fontSize),
136
163
  lineHeight: useScaleSize(lineHeight),
137
164
  fontStyle,
138
- fontWeight,
165
+ // fontFamily da mang san weight. Tren Android, ReactFontManager doi
166
+ // `fonts/<family>_bold.ttf` khi fontWeight >= 700; file do khong ton tai nen
167
+ // font se roi ve font he thong. Bo fontWeight de Android dung dung file.
168
+ fontWeight: undefined,
139
169
  };
140
170
  };
141
171
 
@@ -216,6 +246,12 @@ const exportFontFamily = (
216
246
  typography = 'body_default_regular';
217
247
  break;
218
248
 
249
+ case 'semibold':
250
+ case 'Semibold':
251
+ case 'SemiBold':
252
+ typography = 'header_s_semibold';
253
+ break;
254
+
219
255
  default: {
220
256
  typography = 'body_default_regular';
221
257
  break;
package/Title/index.tsx CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  ViewStyle,
7
7
  } from 'react-native';
8
8
  import { Icon } from '../Icon';
9
- import { useScaleSize, Text } from '../Text';
9
+ import { useScaleSize, Text, exportFontFamily } from '../Text';
10
10
  import { useComponentId } from '../Application';
11
11
  import { ApplicationContext, MiniAppContext } from '../Context';
12
12
  import { Badge } from '../Badge';
@@ -75,6 +75,7 @@ const Title: FC<TitleProps> = ({
75
75
  [key: string]: any;
76
76
  } = { ...styles, ...dynamicStyles };
77
77
  const typography = `${type}_${size}`;
78
+ const titleFontFamily = exportFontFamily('bold');
78
79
  const isSection = type === 'section';
79
80
  const numberOfLines = showTrailingAction || !!badgeLabel ? 1 : 2;
80
81
  const buttonTypo: Typography =
@@ -129,6 +130,7 @@ const Title: FC<TitleProps> = ({
129
130
  numberOfLines={numberOfLines}
130
131
  style={[
131
132
  styleSheet[typography],
133
+ { fontFamily: titleFontFamily },
132
134
  styles.title,
133
135
  {
134
136
  maxWidth:
@@ -256,7 +258,11 @@ const Title: FC<TitleProps> = ({
256
258
  allowFontScaling={false}
257
259
  accessibilityLabel={componentId + '|title-text'}
258
260
  numberOfLines={numberOfLines}
259
- style={[styleSheet[typography], styles.title]}
261
+ style={[
262
+ styleSheet[typography],
263
+ { fontFamily: titleFontFamily },
264
+ styles.title,
265
+ ]}
260
266
  >
261
267
  {title}
262
268
  </RNText>
package/Title/styles.ts CHANGED
@@ -42,7 +42,6 @@ export default StyleSheet.create({
42
42
  },
43
43
  contentView: { marginRight: Spacing.S, flex: 1 },
44
44
  title: {
45
- fontWeight: 'bold',
46
45
  color: Colors.black_17,
47
46
  },
48
47
  badge: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.165.1-sp.4",
3
+ "version": "0.165.2-beta.1",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},