@momo-kits/foundation 0.169.6-beta.1 → 0.169.7-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.
- 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.tsx +48 -49
- package/Application/BottomTab/BottomTabInset.tsx +8 -4
- package/Application/BottomTab/CurvedContainer.tsx +14 -12
- package/Application/BottomTab/CustomBottomTabItem.tsx +12 -13
- package/Application/BottomTab/DuoBarCompression.tsx +13 -19
- package/Application/BottomTab/DuoRailTabBar.tsx +20 -20
- package/Application/BottomTab/DuoTabBar.tsx +8 -9
- 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 +22 -25
- 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/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
|
@@ -4,6 +4,10 @@ import { Spacing } from '../../Consts';
|
|
|
4
4
|
import { isWhiteListDesignSystem } from '../utils';
|
|
5
5
|
import { useScreenLayout } from '../../Layout';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Header title font: MoMoTrustDisplay for design-system whitelisted mini apps,
|
|
9
|
+
* SFProText otherwise.
|
|
10
|
+
*/
|
|
7
11
|
const useHeaderFontFamily = (): string => {
|
|
8
12
|
const context = useContext<any>(MiniAppContext);
|
|
9
13
|
return isWhiteListDesignSystem(context) ? 'MoMoTrustDisplay' : 'SFProText';
|
|
@@ -20,24 +24,22 @@ const HEADER_ACTIONS_WIDTH = 172;
|
|
|
20
24
|
const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
|
-
*
|
|
27
|
+
* Max header title width. The iPhone Duo side bar takes back and toolkit out of
|
|
28
|
+
* the bar, so the title gets the content width; otherwise it shares the bar with them.
|
|
24
29
|
*/
|
|
25
30
|
const useTitleMaxWidth = () => {
|
|
26
|
-
const
|
|
27
|
-
|
|
31
|
+
const layout = useScreenLayout();
|
|
32
|
+
const { width, contentWidth, sideBar, statusBarClearance } = layout;
|
|
28
33
|
let clearance = 0;
|
|
34
|
+
let rowActionsExtra = 0;
|
|
29
35
|
if (statusBarClearance) {
|
|
30
36
|
clearance = statusBarClearance.left + statusBarClearance.right;
|
|
37
|
+
rowActionsExtra = STATUS_BAR_ROW_ACTIONS_EXTRA;
|
|
31
38
|
}
|
|
32
39
|
if (sideBar) {
|
|
33
40
|
return contentWidth - Spacing.M * 2 - clearance;
|
|
34
41
|
}
|
|
35
|
-
return
|
|
36
|
-
width -
|
|
37
|
-
HEADER_ACTIONS_WIDTH -
|
|
38
|
-
(statusBarClearance ? STATUS_BAR_ROW_ACTIONS_EXTRA : 0) -
|
|
39
|
-
clearance
|
|
40
|
-
);
|
|
42
|
+
return width - HEADER_ACTIONS_WIDTH - rowActionsExtra - clearance;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
export { useHeaderFontFamily, HEADER_ACTIONS_WIDTH, useTitleMaxWidth };
|
|
@@ -33,7 +33,8 @@ type HeaderTitleExtraProps = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Default navigation header title. With `animatedValue` its opacity follows the
|
|
37
|
+
* scroll through `interpolate` (0 to 1 over the first 200pt by default).
|
|
37
38
|
*/
|
|
38
39
|
const HeaderTitle: React.FC<
|
|
39
40
|
HeaderTitleExtraProps & { [key: string]: any }
|
|
@@ -100,8 +101,8 @@ const HeaderTitle: React.FC<
|
|
|
100
101
|
};
|
|
101
102
|
|
|
102
103
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
104
|
+
* Header title picked by `type` (user, location or journey) with `data` as its
|
|
105
|
+
* props; an empty view for an unknown type.
|
|
105
106
|
*/
|
|
106
107
|
const TitleCustom: React.FC<any> = ({ type, data, ...props }) => {
|
|
107
108
|
switch (type) {
|
|
@@ -14,7 +14,8 @@ const CLOSE_ICON = '16_navigation_close_circle';
|
|
|
14
14
|
const HOME_ICON = '16_basic_home';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Header toolkit
|
|
17
|
+
* Header toolkit: optional shortcut plus the help center / close capsule. When
|
|
18
|
+
* `vertical` (iPhone Duo side bar) it reports its height and may fold into one button.
|
|
18
19
|
*/
|
|
19
20
|
const HeaderToolkitAction: React.FC<any> = ({
|
|
20
21
|
tintColor,
|
|
@@ -27,8 +28,9 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
27
28
|
vertical = false,
|
|
28
29
|
}) => {
|
|
29
30
|
const context = useContext<any>(MiniAppContext);
|
|
30
|
-
const
|
|
31
|
-
|
|
31
|
+
const compression = useDuoBarCompression();
|
|
32
|
+
const { toolbarCompressed, toolbarHidden } = compression;
|
|
33
|
+
const { expandToolbar, setToolbarHeight } = compression;
|
|
32
34
|
const { statusBarClearance } = useScreenLayout();
|
|
33
35
|
const {
|
|
34
36
|
isLoading,
|
|
@@ -56,18 +58,22 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
56
58
|
|
|
57
59
|
const useHelpCenter = context.appId !== 'vn.momo.helpcenter';
|
|
58
60
|
const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
|
|
61
|
+
let toolkitWidthStyle: ViewStyle = styles.toolkitNarrow;
|
|
62
|
+
if (useHelpCenter) {
|
|
63
|
+
toolkitWidthStyle = styles.toolkitWide;
|
|
64
|
+
}
|
|
59
65
|
|
|
60
66
|
const groupCount = useHelpCenter ? 2 : 1;
|
|
61
67
|
let capsuleHeight = DuoMetrics.controlSize;
|
|
62
68
|
if (groupCount > 1) {
|
|
63
69
|
capsuleHeight = groupCount * DuoMetrics.itemPitch;
|
|
64
70
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Spacing.M
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
const backSlotHeight = DuoMetrics.controlSize + Spacing.M;
|
|
72
|
+
let shortcutHeight = 0;
|
|
73
|
+
if (useShortcut) {
|
|
74
|
+
shortcutHeight = DuoMetrics.controlSize + Spacing.M;
|
|
75
|
+
}
|
|
76
|
+
const fullToolbarHeight = backSlotHeight + shortcutHeight + capsuleHeight;
|
|
71
77
|
|
|
72
78
|
useEffect(() => {
|
|
73
79
|
if (vertical) {
|
|
@@ -80,8 +86,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
80
86
|
return null;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
// HIG "Toolbar compressed":
|
|
84
|
-
// toolbar and compresses the tab bar instead. Not the useMore icon, to tell them apart
|
|
89
|
+
// HIG "Toolbar compressed": a menu icon (not the "more" one) that expands the toolbar
|
|
85
90
|
if (vertical && toolbarCompressed) {
|
|
86
91
|
return (
|
|
87
92
|
<SideBarButton
|
|
@@ -170,15 +175,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
170
175
|
showBadge={showBadge}
|
|
171
176
|
/>
|
|
172
177
|
)}
|
|
173
|
-
<View
|
|
174
|
-
style={[
|
|
175
|
-
styles.toolkitContainer,
|
|
176
|
-
buttonStyle,
|
|
177
|
-
context.appId !== 'vn.momo.helpcenter'
|
|
178
|
-
? styles.toolkitWide
|
|
179
|
-
: styles.toolkitNarrow,
|
|
180
|
-
]}
|
|
181
|
-
>
|
|
178
|
+
<View style={[styles.toolkitContainer, buttonStyle, toolkitWidthStyle]}>
|
|
182
179
|
{useHelpCenter && (
|
|
183
180
|
<>
|
|
184
181
|
<TouchableOpacity
|
|
@@ -7,7 +7,8 @@ import { Icon } from '../../Icon';
|
|
|
7
7
|
import { BadgeDot } from '../../Badge';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Round 28pt icon button of the header (back, shortcut) with an optional badge
|
|
11
|
+
* dot; switches to a dark fill under a white tint so the icon stays readable.
|
|
11
12
|
*/
|
|
12
13
|
const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
13
14
|
icon,
|
|
@@ -10,6 +10,10 @@ import { useNormalizedSharedValue } from '../hooks';
|
|
|
10
10
|
import { useScreenLayout } from '../../Layout';
|
|
11
11
|
import { DuoMetrics } from '../../Layout/utils';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Header search bar whose fill turns from surface to default once scrolled, with
|
|
15
|
+
* a cancel button that goes back (or dismisses the mini app) unless `renderButtons`.
|
|
16
|
+
*/
|
|
13
17
|
const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
14
18
|
(
|
|
15
19
|
{
|
|
@@ -36,9 +40,9 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
36
40
|
if (layout.sideBar) {
|
|
37
41
|
searchMaxWidth = layout.contentWidth - Spacing.M * 2 - headerRightWidth;
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const clearance = layout.statusBarClearance;
|
|
44
|
+
if (clearance) {
|
|
45
|
+
searchMaxWidth -= clearance.left + clearance.right;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
const backgroundStyle = useAnimatedStyle(() => {
|
|
@@ -76,6 +80,15 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
76
80
|
|
|
77
81
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
78
82
|
|
|
83
|
+
let buttons: React.ReactNode = (
|
|
84
|
+
<TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
|
|
85
|
+
<Text typography={'action_default_bold'}>{buttonText}</Text>
|
|
86
|
+
</TouchableOpacity>
|
|
87
|
+
);
|
|
88
|
+
if (typeof renderButtons === 'function') {
|
|
89
|
+
buttons = renderButtons();
|
|
90
|
+
}
|
|
91
|
+
|
|
79
92
|
return (
|
|
80
93
|
<View
|
|
81
94
|
style={[styles.container, showBaseLineDebug && styles.debugBaseLine]}
|
|
@@ -97,13 +110,7 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
97
110
|
/>
|
|
98
111
|
</Animated.View>
|
|
99
112
|
</View>
|
|
100
|
-
{
|
|
101
|
-
renderButtons()
|
|
102
|
-
) : (
|
|
103
|
-
<TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
|
|
104
|
-
<Text typography={'action_default_bold'}>{buttonText}</Text>
|
|
105
|
-
</TouchableOpacity>
|
|
106
|
-
)}
|
|
113
|
+
{buttons}
|
|
107
114
|
</View>
|
|
108
115
|
);
|
|
109
116
|
},
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
} from './HeaderTitle.shared';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Header
|
|
18
|
-
*
|
|
17
|
+
* Header title for a journey: `start` and `end` around an icon, with an optional
|
|
18
|
+
* description below; a shimmer while `isLoading`.
|
|
19
19
|
*/
|
|
20
20
|
const TitleJourney: React.FC<TitleJourneyProps> = ({
|
|
21
21
|
start,
|
|
@@ -9,8 +9,8 @@ import { styles } from './HeaderTitle.styles';
|
|
|
9
9
|
import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Header
|
|
13
|
-
*
|
|
12
|
+
* Header title showing a location with a dropdown chevron and an optional
|
|
13
|
+
* description above it; a shimmer while `isLoading`.
|
|
14
14
|
*/
|
|
15
15
|
const TitleLocation: React.FC<TitleLocationProps> = ({
|
|
16
16
|
tintColor,
|
|
@@ -10,8 +10,8 @@ import { styles } from './HeaderTitle.styles';
|
|
|
10
10
|
import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Header user
|
|
14
|
-
*
|
|
13
|
+
* Header title for a user or group: avatar (1 to 4 images, a `+N` stack, or name
|
|
14
|
+
* initials), title with verified icons and subtitle; a shimmer while `isLoading`.
|
|
15
15
|
*/
|
|
16
16
|
const TitleUser: React.FC<TitleUserProps> = ({
|
|
17
17
|
title,
|
|
@@ -15,7 +15,8 @@ type ToolkitActionsParams = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* State and handlers behind the header toolkit: the favorite shortcut (kept in sync
|
|
19
|
+
* with the home), single tool or "more" shortcut, help center and close.
|
|
19
20
|
*/
|
|
20
21
|
const useToolkitActions = ({
|
|
21
22
|
preventClose,
|
|
@@ -31,20 +32,15 @@ const useToolkitActions = ({
|
|
|
31
32
|
const [isFavorite, setIsFavorite] = useState<boolean>(false);
|
|
32
33
|
const [isLoading, setIsLoading] = useState(false);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
setIsFavorite(result);
|
|
44
|
-
},
|
|
45
|
-
),
|
|
46
|
-
[context?.code, navigator?.maxApi],
|
|
47
|
-
);
|
|
35
|
+
const checkAppIsFavorite = useCallback(() => {
|
|
36
|
+
return navigator?.maxApi?.dispatchFunction?.(
|
|
37
|
+
'isFavoriteApp',
|
|
38
|
+
{ code: context?.code },
|
|
39
|
+
(result: boolean) => {
|
|
40
|
+
setIsFavorite(result);
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
}, [context?.code, navigator?.maxApi]);
|
|
48
44
|
|
|
49
45
|
useEffect(() => {
|
|
50
46
|
if (useShortcut) {
|
|
@@ -75,9 +71,6 @@ const useToolkitActions = ({
|
|
|
75
71
|
}
|
|
76
72
|
};
|
|
77
73
|
|
|
78
|
-
/**
|
|
79
|
-
* close navigation container
|
|
80
|
-
*/
|
|
81
74
|
const onClose = () => {
|
|
82
75
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
83
76
|
context?.autoTracking?.({
|
|
@@ -107,9 +100,6 @@ const useToolkitActions = ({
|
|
|
107
100
|
}
|
|
108
101
|
};
|
|
109
102
|
|
|
110
|
-
/**
|
|
111
|
-
* on press shortcut
|
|
112
|
-
*/
|
|
113
103
|
const onPressShortcut = () => {
|
|
114
104
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
115
105
|
const expected = !isFavorite;
|
|
@@ -139,9 +129,6 @@ const useToolkitActions = ({
|
|
|
139
129
|
}, 1000);
|
|
140
130
|
};
|
|
141
131
|
|
|
142
|
-
/**
|
|
143
|
-
* on press help center
|
|
144
|
-
*/
|
|
145
132
|
const onPressHelpCenter = () => {
|
|
146
133
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
147
134
|
context?.autoTracking?.({
|
|
@@ -154,9 +141,6 @@ const useToolkitActions = ({
|
|
|
154
141
|
navigator?.maxApi?.dispatchFunction?.('showHelpCenter', context);
|
|
155
142
|
};
|
|
156
143
|
|
|
157
|
-
/**
|
|
158
|
-
* on press icon more
|
|
159
|
-
*/
|
|
160
144
|
const onPressIconMore = () => {
|
|
161
145
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
162
146
|
context?.autoTracking?.({
|
|
@@ -200,9 +184,10 @@ const useToolkitActions = ({
|
|
|
200
184
|
iconShortcut = 'navigation_more_icon';
|
|
201
185
|
shortcutOnPress = onPressIconMore;
|
|
202
186
|
} else if (isHaveOneTool) {
|
|
203
|
-
const
|
|
187
|
+
const singleToolGroup = tools.find(
|
|
204
188
|
(group: ToolGroup) => group?.items?.length === 1,
|
|
205
|
-
)
|
|
189
|
+
);
|
|
190
|
+
const singleTool = singleToolGroup?.items[0];
|
|
206
191
|
iconShortcut = singleTool?.icon;
|
|
207
192
|
shortcutOnPress = () => {
|
|
208
193
|
singleTool?.onPress?.();
|
|
@@ -216,9 +201,9 @@ const useToolkitActions = ({
|
|
|
216
201
|
};
|
|
217
202
|
}
|
|
218
203
|
|
|
219
|
-
const showBadge = tools.some((group: ToolGroup) =>
|
|
220
|
-
group?.items?.some?.(tool => tool?.showBadge)
|
|
221
|
-
);
|
|
204
|
+
const showBadge = tools.some((group: ToolGroup) => {
|
|
205
|
+
return group?.items?.some?.(tool => tool?.showBadge);
|
|
206
|
+
});
|
|
222
207
|
|
|
223
208
|
return {
|
|
224
209
|
isLoading,
|
package/Application/Localize.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { NativeModules } from 'react-native';
|
|
|
2
2
|
import { LocalizationObject } from './types';
|
|
3
3
|
import defaultLanguage from '../Assets/language.json';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* String table for kit and feature copy. The language is read once from the
|
|
7
|
+
* native `RNResource` module at construction; Vietnamese is the default.
|
|
8
|
+
*/
|
|
5
9
|
class Localize {
|
|
6
10
|
private assets: LocalizationObject;
|
|
7
11
|
private readonly currentLanguage: 'en' | 'vi';
|
|
@@ -25,14 +29,17 @@ class Localize {
|
|
|
25
29
|
return this.assets;
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
/** Text for `key` in the current language, or the key itself if missing. */
|
|
28
33
|
translate(key: string) {
|
|
29
34
|
return this.assets[this.currentLanguage]?.[key] || key;
|
|
30
35
|
}
|
|
31
36
|
|
|
37
|
+
/** Picks the current-language text of a `{ vi, en }` pair, else its JSON. */
|
|
32
38
|
translateData(data: { vi: string; en: string }) {
|
|
33
39
|
return data[this.currentLanguage] ?? JSON.stringify(data);
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
/** Merges extra translations; keys that already exist keep their text. */
|
|
36
43
|
addTranslations(translations: LocalizationObject) {
|
|
37
44
|
this.assets = {
|
|
38
45
|
vi: { ...translations?.vi, ...this.assets.vi },
|
|
@@ -27,6 +27,10 @@ import { isIPhoneDuo } from './utils';
|
|
|
27
27
|
import { useModalLayout } from './modalLayout';
|
|
28
28
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Screen of the `Modal` route: a bottom sheet when pushed by
|
|
32
|
+
* `showBottomSheet`, otherwise a centered popup.
|
|
33
|
+
*/
|
|
30
34
|
const ModalScreen: React.FC<any> = props => {
|
|
31
35
|
if (props.route?.params?.isBottomSheet) {
|
|
32
36
|
return <BottomSheet {...props} />;
|
|
@@ -34,6 +38,10 @@ const ModalScreen: React.FC<any> = props => {
|
|
|
34
38
|
return <Modal {...props} />;
|
|
35
39
|
};
|
|
36
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Centered popup that fades and scales in over a scrim. On foldables it is
|
|
43
|
+
* centered in the modal placement area, at most a popup wide.
|
|
44
|
+
*/
|
|
37
45
|
const Modal: React.FC<ModalParams> = props => {
|
|
38
46
|
const { navigator } = useContext(ApplicationContext);
|
|
39
47
|
const context = useContext<any>(MiniAppContext);
|
|
@@ -50,9 +58,7 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
50
58
|
const isDuo = isIPhoneDuo();
|
|
51
59
|
const { isFoldable, placement, popup, scrimOpacity } = useModalLayout();
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
* Fire auto_popup_displayed tracking event on mount
|
|
55
|
-
*/
|
|
61
|
+
// Auto-tracks the popup as displayed on the current screen.
|
|
56
62
|
useEffect(() => {
|
|
57
63
|
navigator?.maxApi?.getDataObserver?.('current_screen', (curScreen: any) => {
|
|
58
64
|
context.autoTracking({
|
|
@@ -11,6 +11,10 @@ export const WHITE_LIST = [
|
|
|
11
11
|
'vn.momo.ekyc',
|
|
12
12
|
];
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Screen-level navigation handle given to features; `setOptions` enforces the
|
|
16
|
+
* design system's header rules before applying React Navigation options.
|
|
17
|
+
*/
|
|
14
18
|
class Navigation {
|
|
15
19
|
instance: NavigationProp<any>;
|
|
16
20
|
context: any;
|
|
@@ -21,39 +25,33 @@ class Navigation {
|
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
28
|
+
* Drops header overrides the design system forbids: any `headerLeft`, and
|
|
29
|
+
* render-function `headerRight`/`headerTitle` outside `WHITE_LIST` apps.
|
|
26
30
|
*/
|
|
27
31
|
private filterParams = (params: NavigationOptions) => {
|
|
32
|
+
const isWhiteListed = WHITE_LIST.includes(this.context?.appId);
|
|
33
|
+
const rightType = typeof params.headerRight;
|
|
34
|
+
const titleType = typeof params.headerTitle;
|
|
35
|
+
const isTitleRenderer = titleType !== 'string' && titleType !== 'object';
|
|
36
|
+
|
|
28
37
|
if (params.headerLeft) {
|
|
29
38
|
console.warn('headerLeft not allow override render by design system!');
|
|
30
39
|
delete params.headerLeft;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
if (
|
|
34
|
-
params.headerRight &&
|
|
35
|
-
typeof params.headerRight !== 'object' &&
|
|
36
|
-
!WHITE_LIST.includes(this.context?.appId)
|
|
37
|
-
) {
|
|
42
|
+
if (params.headerRight && rightType !== 'object' && !isWhiteListed) {
|
|
38
43
|
console.warn('headerRight not allow type func render by design system!');
|
|
39
44
|
delete params.headerRight;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
if (
|
|
43
|
-
params.headerTitle &&
|
|
44
|
-
!['string', 'object'].includes(typeof params.headerTitle) &&
|
|
45
|
-
!WHITE_LIST.includes(this.context?.appId)
|
|
46
|
-
) {
|
|
47
|
+
if (params.headerTitle && isTitleRenderer && !isWhiteListed) {
|
|
47
48
|
console.warn('headerTitle not allow type func render by design system!');
|
|
48
49
|
delete params.headerTitle;
|
|
49
50
|
}
|
|
50
51
|
return params;
|
|
51
52
|
};
|
|
52
53
|
|
|
53
|
-
/**
|
|
54
|
-
* navigation setOptions support props NavigationOptions base on react-navigation options
|
|
55
|
-
* @param params
|
|
56
|
-
*/
|
|
54
|
+
/** Applies kit `NavigationOptions` to the screen after `filterParams`. */
|
|
57
55
|
setOptions = (params: NavigationOptions) => {
|
|
58
56
|
const options = getOptions(this.filterParams(params));
|
|
59
57
|
this.instance.setOptions(options as StackNavigationOptions);
|
|
@@ -33,6 +33,10 @@ import { ReservedRegionsProvider } from '../Layout/ReservedRegions';
|
|
|
33
33
|
|
|
34
34
|
const Stack = createStackNavigator();
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Root of a mini app: safe area, reserved regions and the merged mini app
|
|
38
|
+
* context around the kit's navigator.
|
|
39
|
+
*/
|
|
36
40
|
const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
37
41
|
screen,
|
|
38
42
|
theme = defaultTheme,
|
|
@@ -92,6 +96,10 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
92
96
|
);
|
|
93
97
|
};
|
|
94
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Stack navigator with its `Stack`, `Dialog` and `Modal` routes, under an
|
|
101
|
+
* `ApplicationContext` carrying the navigator, themed fonts and `translate`.
|
|
102
|
+
*/
|
|
95
103
|
const Navigation: React.FC<any> = ({
|
|
96
104
|
screen,
|
|
97
105
|
theme = defaultTheme,
|
|
@@ -109,9 +117,10 @@ const Navigation: React.FC<any> = ({
|
|
|
109
117
|
const syncCanGoBack = useNativeCanGoBack();
|
|
110
118
|
const isWhiteList = isWhiteListDesignSystem(context);
|
|
111
119
|
const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
120
|
+
let navigationFontFamily = 'SFProText';
|
|
121
|
+
if (isWhiteList) {
|
|
122
|
+
navigationFontFamily = 'MomoTrustSans-Regular';
|
|
123
|
+
}
|
|
115
124
|
let headerBackground = context?.designSystemConfig?.config?.headerBar;
|
|
116
125
|
let headerGradient = theme.colors?.gradient;
|
|
117
126
|
|
|
@@ -122,15 +131,9 @@ const Navigation: React.FC<any> = ({
|
|
|
122
131
|
headerGradient = context?.designSystemConfig?.config?.headerGradient;
|
|
123
132
|
}
|
|
124
133
|
|
|
125
|
-
/**
|
|
126
|
-
* inject data for navigator
|
|
127
|
-
*/
|
|
128
134
|
navigator.current.maxApi = maxApi;
|
|
129
135
|
|
|
130
|
-
/**
|
|
131
|
-
* translate context method
|
|
132
|
-
* @param data
|
|
133
|
-
*/
|
|
136
|
+
/** Text for a localization key, or the current-language side of `{ vi, en }`. */
|
|
134
137
|
const translate = (data?: string | { vi: string; en: string }) => {
|
|
135
138
|
if (data && typeof data !== 'string') {
|
|
136
139
|
return localize?.translateData(data);
|
|
@@ -228,12 +231,12 @@ const Navigation: React.FC<any> = ({
|
|
|
228
231
|
backgroundColor: theme.colors.background.default,
|
|
229
232
|
},
|
|
230
233
|
headerLeft: (props: any) => <HeaderLeft {...props} floating />,
|
|
231
|
-
headerBackground: (props: any) =>
|
|
232
|
-
<HeaderBackground {...props}
|
|
233
|
-
|
|
234
|
-
headerRight: (props: any) =>
|
|
235
|
-
<HeaderRight {...props} floating
|
|
236
|
-
|
|
234
|
+
headerBackground: (props: any) => {
|
|
235
|
+
return <HeaderBackground {...props} />;
|
|
236
|
+
},
|
|
237
|
+
headerRight: (props: any) => {
|
|
238
|
+
return <HeaderRight {...props} floating />;
|
|
239
|
+
},
|
|
237
240
|
headerTintColor: Colors.black_17,
|
|
238
241
|
...TransitionPresets.ModalTransition,
|
|
239
242
|
}}
|