@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.
- package/Application/BottomSheet.hooks.ts +8 -8
- package/Application/BottomSheet.parts.tsx +44 -34
- package/Application/BottomSheet.tsx +35 -26
- package/Application/BottomTab/ActiveIndicator.tsx +15 -14
- package/Application/BottomTab/BottomTabBar.styles.ts +1 -18
- package/Application/BottomTab/BottomTabBar.tsx +58 -57
- package/Application/BottomTab/BottomTabInset.tsx +8 -4
- package/Application/BottomTab/CurvedContainer.tsx +16 -20
- package/Application/BottomTab/CustomBottomTabItem.tsx +14 -15
- package/Application/BottomTab/DuoBarCompression.tsx +13 -19
- package/Application/BottomTab/DuoRailTabBar.tsx +20 -20
- package/Application/BottomTab/DuoTabBar.tsx +11 -24
- package/Application/BottomTab/TabBarIcon.tsx +15 -29
- package/Application/BottomTab/TabScreen.tsx +1 -5
- package/Application/BottomTab/index.tsx +33 -30
- package/Application/BottomTab/metrics.ts +25 -48
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +4 -0
- package/Application/Components/DuoGlassBackground.tsx +5 -4
- package/Application/Components/HeaderAnimated.tsx +4 -0
- package/Application/Components/HeaderBackground.tsx +4 -0
- package/Application/Components/HeaderExtendHeader.tsx +11 -10
- package/Application/Components/HeaderLeft.tsx +2 -1
- package/Application/Components/HeaderRight.tsx +2 -1
- package/Application/Components/HeaderTitle.shared.ts +11 -9
- package/Application/Components/HeaderTitle.tsx +4 -3
- package/Application/Components/HeaderToolkitAction.tsx +17 -20
- package/Application/Components/NavigationButton.tsx +2 -1
- package/Application/Components/SearchHeader.tsx +17 -10
- package/Application/Components/TitleJourney.tsx +2 -2
- package/Application/Components/TitleLocation.tsx +2 -2
- package/Application/Components/TitleUser.tsx +2 -2
- package/Application/Components/useToolkitActions.tsx +17 -32
- package/Application/Localize.ts +7 -0
- package/Application/ModalScreen.tsx +9 -3
- package/Application/Navigation.ts +14 -16
- package/Application/NavigationContainer.tsx +19 -16
- package/Application/Navigator.ts +20 -48
- package/Application/ScaleSizeProvider.tsx +13 -6
- package/Application/StackScreen.tracking.ts +11 -26
- package/Application/StackScreen.tsx +8 -35
- package/Application/TooltipPortal.tsx +12 -5
- package/Application/WidgetContainer.tsx +10 -4
- package/Application/modalLayout.ts +13 -14
- package/Application/navigationOptions.tsx +21 -17
- package/Application/types.tabs.ts +1 -0
- package/Badge/Badge.tsx +5 -0
- package/Badge/BadgeDot.tsx +1 -0
- package/Badge/BadgeDotAnimation.tsx +15 -7
- package/Badge/BadgeRibbon.tsx +46 -38
- package/Badge/Shape.tsx +2 -0
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +5 -16
- package/Button/utils.ts +9 -15
- package/CheckBox/index.tsx +4 -0
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +5 -1
- package/Divider/DashDivider.tsx +1 -0
- package/Divider/index.tsx +4 -0
- package/FoundationList/index.tsx +4 -0
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +4 -12
- package/Image/index.tsx +9 -7
- package/Input/Input.tsx +8 -9
- package/Input/InputDropDown.tsx +10 -12
- package/Input/InputMoney.tsx +12 -8
- package/Input/InputMoneyView.tsx +14 -7
- package/Input/InputOTP.tsx +35 -33
- package/Input/InputPhoneNumber.tsx +8 -9
- package/Input/InputSearch.tsx +4 -6
- package/Input/InputTextArea.tsx +4 -0
- package/Input/SearchTextTyping.tsx +4 -0
- package/Input/SystemTextInput.tsx +7 -2
- package/Input/TextTyping.tsx +4 -0
- package/Input/common.tsx +26 -12
- package/Input/index.tsx +17 -20
- package/Input/moneyUtils.ts +10 -4
- package/Input/styles.ts +0 -5
- package/Input/utils.ts +14 -12
- package/Layout/Card.tsx +7 -4
- package/Layout/FloatingButton.tsx +4 -0
- package/Layout/Item.tsx +6 -2
- package/Layout/ItemList.tsx +9 -7
- package/Layout/ItemSectionList.tsx +2 -1
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +56 -58
- package/Layout/Section.tsx +8 -5
- package/Layout/TrackingScope.tsx +1 -0
- package/Layout/screenContent.tsx +4 -2
- package/Layout/screenFooter.tsx +28 -18
- package/Layout/screenHeader.tsx +34 -31
- package/Layout/screenQuickReturn.tsx +17 -20
- package/Layout/screenScroll.ts +4 -7
- package/Layout/screenTypes.ts +3 -2
- package/Layout/types.ts +2 -4
- package/Layout/utils.ts +31 -37
- package/Loader/DotLoader.tsx +4 -0
- package/Loader/Loader.tsx +4 -0
- package/Loader/ProgressBar.tsx +1 -0
- package/Loader/Spinner.tsx +4 -0
- package/Loader/utils.ts +11 -7
- package/Pagination/Dot.tsx +4 -0
- package/Pagination/PaginationDot.tsx +1 -0
- package/Pagination/PaginationNumber.tsx +5 -3
- package/Pagination/PaginationScroll.tsx +5 -0
- package/Pagination/PaginationWhiteDot.tsx +1 -0
- package/Pagination/index.tsx +4 -0
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +44 -48
- package/Popup/PopupPromotion.tsx +4 -7
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +4 -0
- package/Radio/index.tsx +4 -0
- package/Skeleton/index.tsx +8 -0
- package/Switch/index.tsx +1 -0
- package/Tag/index.tsx +5 -1
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +14 -3
- package/Text/styles.ts +1 -1
- package/Text/types.ts +1 -6
- package/Text/utils.ts +20 -4
- package/Title/index.tsx +41 -32
- package/ViewBoundary/index.tsx +4 -2
- package/index.ts +0 -6
- package/package.json +1 -1
- package/Application/__tests__/modalLayout.test.ts +0 -128
package/Pagination/types.ts
CHANGED
|
@@ -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
|
|
8
|
+
* - 'black_white': Represents dots on a translucent dark background.
|
|
9
9
|
* - 'number': Represents a style where pages are represented by numbers.
|
|
10
|
-
* - 'scroll':
|
|
10
|
+
* - 'scroll': Rendered as 'default'; use `PaginationScroll` instead.
|
|
11
11
|
*/
|
|
12
12
|
type?: 'default' | 'black_white' | 'number' | 'scroll';
|
|
13
13
|
|
package/Popup/PopupNotify.tsx
CHANGED
|
@@ -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
|
-
|
|
130
|
-
|
|
131
|
-
(
|
|
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={
|
|
211
|
+
numberOfLines={2}
|
|
198
212
|
accessibilityLabel={'title_popup_notify'}
|
|
199
|
-
|
|
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
|
-
{
|
|
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 },
|
package/Popup/PopupPromotion.tsx
CHANGED
|
@@ -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
|
|
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
|
-
* `
|
|
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
|
|
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
|
|
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
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,
|
package/Skeleton/index.tsx
CHANGED
|
@@ -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
|
-
//
|
|
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 '
|
|
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.
|
|
33
|
-
*
|
|
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
|
|
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
|
-
|
|
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<
|
|
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
|
-
{
|
|
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
|
};
|
package/ViewBoundary/index.tsx
CHANGED
|
@@ -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';
|