@momo-kits/foundation 0.169.6-beta.1 → 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.
Files changed (128) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +44 -34
  3. package/Application/BottomSheet.tsx +35 -26
  4. package/Application/BottomTab/ActiveIndicator.tsx +15 -14
  5. package/Application/BottomTab/BottomTabBar.styles.ts +1 -18
  6. package/Application/BottomTab/BottomTabBar.tsx +58 -57
  7. package/Application/BottomTab/BottomTabInset.tsx +8 -4
  8. package/Application/BottomTab/CurvedContainer.tsx +16 -20
  9. package/Application/BottomTab/CustomBottomTabItem.tsx +14 -15
  10. package/Application/BottomTab/DuoBarCompression.tsx +13 -19
  11. package/Application/BottomTab/DuoRailTabBar.tsx +20 -20
  12. package/Application/BottomTab/DuoTabBar.tsx +11 -24
  13. package/Application/BottomTab/TabBarIcon.tsx +15 -29
  14. package/Application/BottomTab/TabScreen.tsx +1 -5
  15. package/Application/BottomTab/index.tsx +33 -30
  16. package/Application/BottomTab/metrics.ts +25 -48
  17. package/Application/BottomTab/tabItems.ts +1 -1
  18. package/Application/BottomTab/usePressLift.ts +2 -2
  19. package/Application/Components/BackgroundImageView.tsx +4 -0
  20. package/Application/Components/DuoGlassBackground.tsx +5 -4
  21. package/Application/Components/HeaderAnimated.tsx +4 -0
  22. package/Application/Components/HeaderBackground.tsx +4 -0
  23. package/Application/Components/HeaderExtendHeader.tsx +11 -10
  24. package/Application/Components/HeaderLeft.tsx +2 -1
  25. package/Application/Components/HeaderRight.tsx +2 -1
  26. package/Application/Components/HeaderTitle.shared.ts +11 -9
  27. package/Application/Components/HeaderTitle.tsx +4 -3
  28. package/Application/Components/HeaderToolkitAction.tsx +17 -20
  29. package/Application/Components/NavigationButton.tsx +2 -1
  30. package/Application/Components/SearchHeader.tsx +17 -10
  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 +17 -32
  35. package/Application/Localize.ts +7 -0
  36. package/Application/ModalScreen.tsx +9 -3
  37. package/Application/Navigation.ts +14 -16
  38. package/Application/NavigationContainer.tsx +19 -16
  39. package/Application/Navigator.ts +20 -48
  40. package/Application/ScaleSizeProvider.tsx +13 -6
  41. package/Application/StackScreen.tracking.ts +11 -26
  42. package/Application/StackScreen.tsx +8 -35
  43. package/Application/TooltipPortal.tsx +12 -5
  44. package/Application/WidgetContainer.tsx +10 -4
  45. package/Application/modalLayout.ts +13 -14
  46. package/Application/navigationOptions.tsx +21 -17
  47. package/Application/types.tabs.ts +1 -0
  48. package/Badge/Badge.tsx +5 -0
  49. package/Badge/BadgeDot.tsx +1 -0
  50. package/Badge/BadgeDotAnimation.tsx +15 -7
  51. package/Badge/BadgeRibbon.tsx +46 -38
  52. package/Badge/Shape.tsx +2 -0
  53. package/Badge/types.ts +2 -2
  54. package/Button/index.tsx +5 -16
  55. package/Button/utils.ts +9 -15
  56. package/CheckBox/index.tsx +4 -0
  57. package/Consts/theme.ts +10 -10
  58. package/Context/index.ts +5 -1
  59. package/Divider/DashDivider.tsx +1 -0
  60. package/Divider/index.tsx +4 -0
  61. package/FoundationList/index.tsx +4 -0
  62. package/Icon/index.tsx +6 -6
  63. package/Icon/types.ts +1 -1
  64. package/IconButton/index.tsx +4 -12
  65. package/Image/index.tsx +9 -7
  66. package/Input/Input.tsx +8 -9
  67. package/Input/InputDropDown.tsx +10 -12
  68. package/Input/InputMoney.tsx +12 -8
  69. package/Input/InputMoneyView.tsx +14 -7
  70. package/Input/InputOTP.tsx +35 -33
  71. package/Input/InputPhoneNumber.tsx +8 -9
  72. package/Input/InputSearch.tsx +4 -6
  73. package/Input/InputTextArea.tsx +4 -0
  74. package/Input/SearchTextTyping.tsx +4 -0
  75. package/Input/SystemTextInput.tsx +7 -2
  76. package/Input/TextTyping.tsx +4 -0
  77. package/Input/common.tsx +26 -12
  78. package/Input/index.tsx +17 -20
  79. package/Input/moneyUtils.ts +10 -4
  80. package/Input/styles.ts +0 -5
  81. package/Input/utils.ts +14 -12
  82. package/Layout/Card.tsx +7 -4
  83. package/Layout/FloatingButton.tsx +4 -0
  84. package/Layout/Item.tsx +6 -2
  85. package/Layout/ItemList.tsx +9 -7
  86. package/Layout/ItemSectionList.tsx +2 -1
  87. package/Layout/ReservedRegions.tsx +25 -25
  88. package/Layout/Screen.tsx +56 -58
  89. package/Layout/Section.tsx +8 -5
  90. package/Layout/TrackingScope.tsx +1 -0
  91. package/Layout/screenContent.tsx +4 -2
  92. package/Layout/screenFooter.tsx +28 -18
  93. package/Layout/screenHeader.tsx +34 -31
  94. package/Layout/screenQuickReturn.tsx +17 -20
  95. package/Layout/screenScroll.ts +4 -7
  96. package/Layout/screenTypes.ts +3 -2
  97. package/Layout/types.ts +2 -4
  98. package/Layout/utils.ts +31 -37
  99. package/Loader/DotLoader.tsx +4 -0
  100. package/Loader/Loader.tsx +4 -0
  101. package/Loader/ProgressBar.tsx +1 -0
  102. package/Loader/Spinner.tsx +4 -0
  103. package/Loader/utils.ts +11 -7
  104. package/Pagination/Dot.tsx +4 -0
  105. package/Pagination/PaginationDot.tsx +1 -0
  106. package/Pagination/PaginationNumber.tsx +5 -3
  107. package/Pagination/PaginationScroll.tsx +5 -0
  108. package/Pagination/PaginationWhiteDot.tsx +1 -0
  109. package/Pagination/index.tsx +4 -0
  110. package/Pagination/types.ts +2 -2
  111. package/Popup/PopupNotify.tsx +44 -48
  112. package/Popup/PopupPromotion.tsx +4 -7
  113. package/Popup/types.ts +4 -4
  114. package/Popup/utils.ts +4 -0
  115. package/Radio/index.tsx +4 -0
  116. package/Skeleton/index.tsx +8 -0
  117. package/Switch/index.tsx +1 -0
  118. package/Tag/index.tsx +5 -1
  119. package/Tag/types.ts +3 -3
  120. package/Text/index.tsx +14 -3
  121. package/Text/styles.ts +1 -1
  122. package/Text/types.ts +1 -6
  123. package/Text/utils.ts +20 -4
  124. package/Title/index.tsx +41 -32
  125. package/ViewBoundary/index.tsx +4 -2
  126. package/index.ts +0 -6
  127. package/package.json +1 -1
  128. package/Application/__tests__/modalLayout.test.ts +0 -128
@@ -5,9 +5,9 @@ export type PaginationProps = {
5
5
  /**
6
6
  * Optional. Defines the visual style of the Pagination component.
7
7
  * - 'default': Represents the default style.
8
- * - 'black_white': Represents a pagination that have black background.
8
+ * - 'black_white': Represents dots on a translucent dark background.
9
9
  * - 'number': Represents a style where pages are represented by numbers.
10
- * - 'scroll': Represents a scrollable style.
10
+ * - 'scroll': Rendered as 'default'; use `PaginationScroll` instead.
11
11
  */
12
12
  type?: 'default' | 'black_white' | 'number' | 'scroll';
13
13
 
@@ -11,6 +11,13 @@ import { useModalLayout } from '../Application/modalLayout';
11
11
  import { ModalIconButton } from '../Application/ModalIconButton';
12
12
  import { splitHighlightText } from './utils';
13
13
 
14
+ const MAX_ROW_TITLE_LENGTH = 12;
15
+
16
+ /**
17
+ * Notify popup body: optional image, title, description, error code and one or
18
+ * two actions. Actions close via `onRequestClose` if given, else pop the modal
19
+ * and then run the callback.
20
+ */
14
21
  const PopupNotify: React.FC<PopupNotifyProps> = ({
15
22
  image,
16
23
  title = 'Title',
@@ -43,10 +50,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
43
50
  descriptionStyle = { maxHeight: scaledMaxHeight };
44
51
  }
45
52
 
46
- /**
47
- * on action popup
48
- * @param callback
49
- */
50
53
  const onAction = (callback?: () => void) => {
51
54
  if (typeof onRequestClose === 'function') {
52
55
  onRequestClose?.(callback);
@@ -56,9 +59,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
56
59
  }
57
60
  };
58
61
 
59
- /**
60
- * build main action of popup
61
- */
62
62
  const buildAction = () => {
63
63
  const renderRow = () => {
64
64
  return (
@@ -126,10 +126,13 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
126
126
  };
127
127
 
128
128
  if (buttonDirection === 'auto') {
129
- if (
130
- secondary?.title &&
131
- (secondary.title.length > 12 || primary.title.length > 12)
132
- ) {
129
+ const secondaryTitle = secondary?.title;
130
+ if (!secondaryTitle) {
131
+ return renderRow();
132
+ }
133
+ const isLongSecondary = secondaryTitle.length > MAX_ROW_TITLE_LENGTH;
134
+ const isLongPrimary = primary.title.length > MAX_ROW_TITLE_LENGTH;
135
+ if (isLongSecondary || isLongPrimary) {
133
136
  return renderColumn();
134
137
  }
135
138
  return renderRow();
@@ -140,9 +143,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
140
143
  return renderColumn();
141
144
  };
142
145
 
143
- /**
144
- * build description text, highlighted keywords keep size/lineHeight and use weight 600
145
- */
146
+ /** Highlighted keywords use weight 600 but keep the body size and line height. */
146
147
  const renderDescription = (text: string) => {
147
148
  const segments = splitHighlightText(text, highlightText);
148
149
  if (!segments) {
@@ -160,9 +161,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
160
161
  });
161
162
  };
162
163
 
163
- /**
164
- * build close action
165
- */
166
164
  const buildCloseIcon = () => {
167
165
  if (typeof onIconClose !== 'function') {
168
166
  return null;
@@ -180,48 +178,44 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
180
178
  );
181
179
  };
182
180
 
181
+ const hasCloseIcon = typeof onIconClose === 'function';
182
+ // keeps the title clear of the close button on a text only popup
183
+ const titleStyle = !image && hasCloseIcon && styles.titleClearClose;
184
+
185
+ let descriptionContent: React.ReactNode = description;
186
+ if (typeof description === 'string') {
187
+ descriptionContent = (
188
+ <Text
189
+ accessibilityLabel={'description_popup_notify'}
190
+ typography={'body_default_regular'}
191
+ onTextLayout={({ nativeEvent: { lines } }) => {
192
+ const enableScroll = lines.length > 8.5;
193
+ if (enableScroll !== scrollContent) {
194
+ setScrollContent(enableScroll);
195
+ }
196
+ }}
197
+ >
198
+ {renderDescription(description)}
199
+ </Text>
200
+ );
201
+ }
202
+
183
203
  const body = (
184
204
  <>
185
205
  {!!image && (
186
- <Image
187
- source={{ uri: image }}
188
- style={[
189
- styles.image,
190
- { borderTopLeftRadius: radius, borderTopRightRadius: radius },
191
- ]}
192
- />
206
+ <Image source={{ uri: image }} style={styles.image} />
193
207
  )}
194
208
  <View style={[styles.content, isFoldable && styles.shrink]}>
195
209
  <Text
196
210
  typography={'header_default_bold'}
197
- numberOfLines={3}
211
+ numberOfLines={2}
198
212
  accessibilityLabel={'title_popup_notify'}
199
- // keeps the title clear of the close button on a text only popup
200
- style={
201
- !image &&
202
- typeof onIconClose === 'function' &&
203
- styles.titleClearClose
204
- }
213
+ style={titleStyle}
205
214
  >
206
215
  {title}
207
216
  </Text>
208
217
  <Description style={[styles.description, descriptionStyle]}>
209
- {typeof description === 'string' ? (
210
- <Text
211
- accessibilityLabel={'description_popup_notify'}
212
- typography={'body_default_regular'}
213
- onTextLayout={({ nativeEvent: { lines } }) => {
214
- const enableScroll = lines.length > 8.5;
215
- if (enableScroll !== scrollContent) {
216
- setScrollContent(enableScroll);
217
- }
218
- }}
219
- >
220
- {renderDescription(description)}
221
- </Text>
222
- ) : (
223
- description
224
- )}
218
+ {descriptionContent}
225
219
  </Description>
226
220
  {!!error && (
227
221
  <View style={styles.information}>
@@ -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 },
@@ -12,6 +12,10 @@ const ICON_CLOSE_SIZE = 20;
12
12
  // close button row under the image: marginTop + vertical padding + icon
13
13
  const CLOSE_ROW_HEIGHT = Spacing.S * 3 + ICON_CLOSE_SIZE;
14
14
 
15
+ /**
16
+ * Promotion popup body: a tappable portrait image with a close button under it.
17
+ * Both close the popup first, then call `onActionClick` / `onIconClose`.
18
+ */
15
19
  const PopupPromotion: React.FC<PopupPromotionProps> = ({
16
20
  image,
17
21
  onIconClose,
@@ -33,10 +37,6 @@ const PopupPromotion: React.FC<PopupPromotionProps> = ({
33
37
 
34
38
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
35
39
 
36
- /**
37
- * on action popup
38
- * @param callback
39
- */
40
40
  const onAction = (callback?: () => void) => {
41
41
  if (typeof onRequestClose === 'function') {
42
42
  onRequestClose?.(callback);
@@ -46,9 +46,6 @@ const PopupPromotion: React.FC<PopupPromotionProps> = ({
46
46
  }
47
47
  };
48
48
 
49
- /**
50
- * build close action
51
- */
52
49
  const buildCloseIcon = () => {
53
50
  return (
54
51
  <View style={styles.iconCloseContainer}>
package/Popup/types.ts CHANGED
@@ -56,7 +56,7 @@ export type PopupNotifyProps = {
56
56
  onIconClose?: () => void;
57
57
 
58
58
  /**
59
- * onRequestClose on request close for modal
59
+ * `onRequestClose`: Optional. Closes the modal, then runs `callback`. ModalScreen injects it; without it the popup uses `navigator.pop()`.
60
60
  */
61
61
  onRequestClose?: (callback?: () => void) => void;
62
62
  };
@@ -73,17 +73,17 @@ export type PopupPromotionProps = {
73
73
  image: string;
74
74
 
75
75
  /**
76
- * `onClose`: Optional. A callback function that is called when the PopupPromotion component is closed.
76
+ * `onIconClose`: Optional. A callback function that is called after the popup closes from the close icon.
77
77
  */
78
78
  onIconClose?: () => void;
79
79
 
80
80
  /**
81
- * `onActionClick`: Optional. A callback function that is called when the PopupPromotion component is closed.
81
+ * `onActionClick`: Optional. A callback function that is called after the popup closes from an image tap.
82
82
  */
83
83
  onActionClick?: () => void;
84
84
 
85
85
  /**
86
- * onRequestClose on request close for modal
86
+ * `onRequestClose`: Optional. Closes the modal, then runs `callback`. ModalScreen injects it; without it the popup uses `navigator.pop()`.
87
87
  */
88
88
  onRequestClose?: (callback?: () => void) => void;
89
89
  };
package/Popup/utils.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  type HighlightSegment = { text: string; highlight: boolean };
2
2
 
3
+ /**
4
+ * True for cased-script letters, digits and `_`; caseless scripts (CJK, Thai)
5
+ * count as word breaks.
6
+ */
3
7
  const isWordChar = (char?: string) => {
4
8
  if (!char) {
5
9
  return false;
package/Radio/index.tsx CHANGED
@@ -11,6 +11,10 @@ import {
11
11
  MiniAppContext,
12
12
  } from '../Context';
13
13
 
14
+ /**
15
+ * Radio button, selected when `value === groupValue`. `onChange` is called on
16
+ * press without arguments; the parent updates `groupValue`.
17
+ */
14
18
  const Radio: FC<RadioProps> = ({
15
19
  value,
16
20
  groupValue,
@@ -21,6 +21,10 @@ import { Colors, Styles } from '../Consts';
21
21
  import styles from './styles';
22
22
  import { ScreenContext, SkeletonContext } from '../Context';
23
23
 
24
+ /**
25
+ * Shimmering placeholder that fills its parent. Reports loading to the screen
26
+ * context while mounted.
27
+ */
24
28
  const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
25
29
  const screen = useContext<any>(ScreenContext);
26
30
  const [width, setWidth] = useState(0);
@@ -84,6 +88,10 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
84
88
  );
85
89
  };
86
90
 
91
+ /**
92
+ * Shares `loading` with descendants (e.g. `Image` shows a skeleton) and animates
93
+ * the layout change when it flips.
94
+ */
87
95
  const SkeletonProvider: React.FC<{ loading: boolean; children: any }> = ({
88
96
  loading = true,
89
97
  children,
package/Switch/index.tsx CHANGED
@@ -6,6 +6,7 @@ import { Colors } from '../Consts';
6
6
  import { useComponentId } from '../Application';
7
7
  import { ComponentContext, MiniAppContext } from '../Context';
8
8
 
9
+ /** On/off toggle; `onChange` receives the negated `value`. */
9
10
  const Switch: FC<SwitchProps> = ({
10
11
  value = false,
11
12
  onChange,
package/Tag/index.tsx CHANGED
@@ -25,6 +25,10 @@ const textColor = {
25
25
  grey: Colors.black_12,
26
26
  };
27
27
 
28
+ /**
29
+ * Small status label with an optional leading icon. `customColor` replaces the
30
+ * palette color and turns the text white.
31
+ */
28
32
  const Tag: FC<TagProps> = ({
29
33
  label = 'Label',
30
34
  icon,
@@ -72,7 +76,7 @@ const Tag: FC<TagProps> = ({
72
76
  `${componentName}/${label}`,
73
77
  accessibilityLabel,
74
78
  );
75
- //Check if custom color is [color]_03 (only check in dev mode)
79
+ // Dev builds only accept `Colors.*_03` as customColor; release accepts any.
76
80
  const validateCustomColor = (hex: string) => {
77
81
  if (__DEV__) {
78
82
  const colorCore: { [key: string]: string } = Colors;
package/Tag/types.ts CHANGED
@@ -8,7 +8,7 @@ export type TagProps = {
8
8
 
9
9
  /**
10
10
  * `size`: Optional. Defines the size of the Tag component.
11
- * Defaults to 'medium' if not provided.
11
+ * Defaults to 'large' if not provided.
12
12
  */
13
13
  size?: 'medium' | 'large';
14
14
 
@@ -29,8 +29,8 @@ export type TagProps = {
29
29
  style?: ViewStyle;
30
30
 
31
31
  /**
32
- * `customColor`: Optional. Represents a custom color string (e.g., hex, rgb, rgba, etc.)
33
- * that can be applied to the Tag component.
32
+ * `customColor`: Optional. Custom background color; the label turns white.
33
+ * Dev builds ignore values other than the `Colors.*_03` tokens.
34
34
  */
35
35
  customColor?: string;
36
36
 
package/Text/index.tsx CHANGED
@@ -111,6 +111,10 @@ const deprecatedValues: Typography[] = [
111
111
  'action_s',
112
112
  ];
113
113
 
114
+ /**
115
+ * Text style of a typography token with sizes scaled by `useScaleSize` and the
116
+ * font file name for its weight. Calls hooks, so it only runs during render.
117
+ */
114
118
  const TypoStyles = (typo: Typography, newFontFamily?: string) => {
115
119
  const { theme } = useContext(ApplicationContext);
116
120
  const styleSheet: {
@@ -158,9 +162,7 @@ const TypoStyles = (typo: Typography, newFontFamily?: string) => {
158
162
  fontSize: useScaleSize(fontSize),
159
163
  lineHeight: useScaleSize(lineHeight),
160
164
  fontStyle,
161
- // fontFamily da mang san weight. Tren Android, ReactFontManager doi
162
- // `fonts/<family>_bold.ttf` khi fontWeight >= 700; file do khong ton tai nen
163
- // font se roi ve font he thong. Bo fontWeight de Android dung dung file.
165
+ // Weight is in fontFamily; fontWeight >= 700 makes Android seek a missing <family>_bold.ttf.
164
166
  fontWeight: undefined,
165
167
  };
166
168
  };
@@ -169,6 +171,11 @@ const skeletonStyles = StyleSheet.create({
169
171
  overlay: { position: 'absolute' },
170
172
  });
171
173
 
174
+ /**
175
+ * Design-system text: renders a `typography` token in the theme font with
176
+ * scaled sizes and OS font scaling off. Warns on deprecated tokens and shows a
177
+ * skeleton overlay while a SkeletonContext is loading.
178
+ */
172
179
  const Text: React.FC<TextProps> = ({
173
180
  typography = 'body_default_regular',
174
181
  color,
@@ -228,6 +235,10 @@ const Text: React.FC<TextProps> = ({
228
235
  );
229
236
  };
230
237
 
238
+ /**
239
+ * Theme font family for a loose weight name ('bold', 'semibold', 'regular').
240
+ * The `typography` argument is ignored; it calls hooks, so use it during render.
241
+ */
231
242
  const exportFontFamily = (
232
243
  fontWeight: string,
233
244
  typography?: Typography,
package/Text/styles.ts CHANGED
@@ -39,7 +39,7 @@ export default {
39
39
  action_s_bold: { fontSize: 14, lineHeight: 20, fontWeight: '700' },
40
40
  action_xs_bold: { fontSize: 12, lineHeight: 18, fontWeight: '700' },
41
41
  action_xxs_bold: { fontSize: 10, lineHeight: 14, fontWeight: '700' },
42
- /**non-support**/
42
+ // Legacy tokens kept for old callers; not in the current type scale.
43
43
  headline_default: {
44
44
  fontSize: 28,
45
45
  lineHeight: 34,
package/Text/types.ts CHANGED
@@ -14,9 +14,7 @@ export type TypographyWeight = {
14
14
  };
15
15
 
16
16
  export type Typography =
17
- /**
18
- * @deprecated
19
- **/
17
+ // Deprecated up to 'action_s': Text warns at runtime when one is used.
20
18
  | 'headline_default'
21
19
  | 'headline_l'
22
20
  | 'headline_xl'
@@ -29,9 +27,6 @@ export type Typography =
29
27
  | 'paragraph_italic'
30
28
  | 'label_xxs'
31
29
  | 'action_s'
32
- /**
33
- * end
34
- **/
35
30
  | 'headline_l_bold'
36
31
  | 'headline_default_bold'
37
32
  | 'header_m_bold'
package/Text/utils.ts CHANGED
@@ -6,16 +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
+
14
+ /**
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`.
18
+ */
9
19
  const useScaleSize = (size: number, scaleRate?: number) => {
10
20
  const { width } = useWindowDimensions();
11
- const context = useContext<any>(MiniAppContext);
21
+ const context = useContext<MiniAppScale | undefined>(MiniAppContext);
22
+ const scaleSizeMaxRate = context?.scaleSizeMaxRate;
23
+ const maxSize = scaleSizeMaxRate ? size * scaleSizeMaxRate : Infinity;
12
24
  if (context?.fontScale?.useOSScaleRate === false) {
13
- return size * (context.fontScale.userScaleRate ?? 1);
25
+ return Math.min(size * (context.fontScale.userScaleRate ?? 1), maxSize);
14
26
  }
15
27
  const fontScale = PixelRatio.getFontScale();
16
28
  const deviceScale = width / DEFAULT_SCREEN_SIZE;
17
29
 
18
- const maxScaleRate = scaleRate ?? MAX_FONT_SCALE;
30
+ const maxScaleRate = scaleRate ?? scaleSizeMaxRate ?? MAX_FONT_SCALE;
19
31
 
20
32
  let fontSizeDeviceScale = size;
21
33
  let fontSizeOSScale = size;
@@ -34,9 +46,13 @@ const useScaleSize = (size: number, scaleRate?: number) => {
34
46
  );
35
47
  }
36
48
 
37
- return Math.max(fontSizeDeviceScale, fontSizeOSScale);
49
+ return Math.min(Math.max(fontSizeDeviceScale, fontSizeOSScale), maxSize);
38
50
  };
39
51
 
52
+ /**
53
+ * Non-hook `useScaleSize` with the default cap and no mini app rate, for use
54
+ * outside render. Reads the window width at call time, so it misses resizes.
55
+ */
40
56
  const scaleSize = (size: number) => {
41
57
  const fontScale = PixelRatio.getFontScale();
42
58
  const deviceScale = Dimensions.get('window').width / DEFAULT_SCREEN_SIZE;
package/Title/index.tsx CHANGED
@@ -15,6 +15,11 @@ import styles from './styles';
15
15
  import { TitleProps } from './types';
16
16
  import { Typography } from '../Text/types';
17
17
 
18
+ /**
19
+ * Section or card heading with optional icon, badge, description, trailing
20
+ * chevron and right action (chevron, or a text button when `buttonTitle` is
21
+ * set). `textOnly` renders the title text alone.
22
+ */
18
23
  const Title: FC<TitleProps> = ({
19
24
  accessibilityLabel,
20
25
  title = 'Title',
@@ -43,7 +48,6 @@ const Title: FC<TitleProps> = ({
43
48
 
44
49
  const size18 = useScaleSize(18);
45
50
  const size22 = useScaleSize(22);
46
- // Dynamic styles using useScaleSize hook
47
51
  const dynamicStyles = {
48
52
  card_small: {
49
53
  fontSize: useScaleSize(14),
@@ -204,39 +208,44 @@ const Title: FC<TitleProps> = ({
204
208
 
205
209
  const lineHeight = styleSheet[typography].lineHeight;
206
210
 
211
+ let action = (
212
+ <TouchableOpacity
213
+ onPress={onPressRightAction}
214
+ style={[
215
+ styles.iconRight,
216
+ {
217
+ backgroundColor: theme.colors.primary + '0F',
218
+ },
219
+ ]}
220
+ accessibilityLabel={componentId + '|label-right-action-touch'}
221
+ >
222
+ <Icon
223
+ source={'arrow_chevron_right_small'}
224
+ size={size22}
225
+ color={theme.colors.primary}
226
+ />
227
+ </TouchableOpacity>
228
+ );
229
+ if (buttonTitle) {
230
+ action = (
231
+ <TouchableOpacity
232
+ onPress={onPressRightAction}
233
+ accessibilityLabel={componentId + '|label-right-action-touch'}
234
+ >
235
+ <Text
236
+ color={theme.colors.primary}
237
+ typography={buttonTypo}
238
+ accessibilityLabel={componentId + '|label-right-action-text'}
239
+ >
240
+ {buttonTitle}
241
+ </Text>
242
+ </TouchableOpacity>
243
+ );
244
+ }
245
+
207
246
  return (
208
247
  <View style={[styles.actionRightView, { height: lineHeight }]}>
209
- {!buttonTitle ? (
210
- <TouchableOpacity
211
- onPress={onPressRightAction}
212
- style={[
213
- styles.iconRight,
214
- {
215
- backgroundColor: theme.colors.primary + '0F',
216
- },
217
- ]}
218
- accessibilityLabel={componentId + '|label-right-action-touch'}
219
- >
220
- <Icon
221
- source={'arrow_chevron_right_small'}
222
- size={size22}
223
- color={theme.colors.primary}
224
- />
225
- </TouchableOpacity>
226
- ) : (
227
- <TouchableOpacity
228
- onPress={onPressRightAction}
229
- accessibilityLabel={componentId + '|label-right-action-touch'}
230
- >
231
- <Text
232
- color={theme.colors.primary}
233
- typography={buttonTypo}
234
- accessibilityLabel={componentId + '|label-right-action-text'}
235
- >
236
- {buttonTitle}
237
- </Text>
238
- </TouchableOpacity>
239
- )}
248
+ {action}
240
249
  </View>
241
250
  );
242
251
  };
@@ -14,6 +14,10 @@ interface ViewBoundaryState {
14
14
  error: Error | null;
15
15
  }
16
16
 
17
+ /**
18
+ * Error boundary: renders `fallback` (or nothing) after a render error and
19
+ * reports it to native `MiniAppModule.onJSBoundaryCrash` when available.
20
+ */
17
21
  class ViewBoundary extends React.Component<
18
22
  ViewBoundaryProps,
19
23
  ViewBoundaryState
@@ -33,7 +37,6 @@ class ViewBoundary extends React.Component<
33
37
  this.props.onError?.(error, errorInfo);
34
38
 
35
39
  const context = (this as any).context ?? {};
36
- // check exist native module method onJSBoundaryCrash
37
40
  const nativeMethod = NativeModules?.MiniAppModule?.onJSBoundaryCrash;
38
41
  if (typeof nativeMethod === 'function') {
39
42
  try {
@@ -49,7 +52,6 @@ class ViewBoundary extends React.Component<
49
52
  componentStack: errorInfo?.componentStack?.substring(0, 500) ?? '',
50
53
  },
51
54
  });
52
- console.info('[ViewBoundary] onJSBoundaryCrash success!');
53
55
  } catch (_e) {
54
56
  console.warn('[ViewBoundary] onJSBoundaryCrash failed:', _e);
55
57
  }
package/index.ts CHANGED
@@ -1,14 +1,8 @@
1
- /**
2
- * native export
3
- */
4
1
  import { TouchableOpacity } from 'react-native';
5
2
 
6
3
  export * from 'react-native-linear-gradient';
7
4
  export * from 'react-native-safe-area-context';
8
5
 
9
- /**
10
- * foundation component
11
- */
12
6
  export * from './Consts';
13
7
  export * from './Context';
14
8
  export * from './Layout';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.169.6-beta.1",
3
+ "version": "0.169.6",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},