@momo-kits/foundation 0.167.1-beta.7 → 0.167.1-duo.12
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.tsx +115 -14
- package/Application/BottomTab/BottomTabBar.tsx +6 -3
- package/Application/BottomTab/DuoBarCompression.tsx +135 -0
- package/Application/BottomTab/DuoTabBar.tsx +462 -0
- package/Application/BottomTab/index.tsx +155 -134
- package/Application/Components/DuoGlassBackground.tsx +179 -0
- package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
- package/Application/Components/HeaderExtendHeader.tsx +65 -28
- package/Application/Components/HeaderLeft.tsx +23 -0
- package/Application/Components/HeaderRight.tsx +104 -9
- package/Application/Components/HeaderTitle.tsx +47 -21
- package/Application/Components/OptionalGlassView.ts +11 -0
- package/Application/Components/SearchHeader.tsx +15 -14
- package/Application/Components/SideBarButton.tsx +128 -0
- package/Application/ModalScreen.tsx +16 -2
- package/Application/NavigationContainer.tsx +36 -48
- package/Application/Navigator.ts +1 -3
- package/Application/StackScreen.tsx +2 -5
- package/Application/WidgetContainer.tsx +9 -15
- package/Application/device.ts +29 -0
- package/Application/types.ts +10 -1
- package/Application/utils.tsx +10 -3
- package/Consts/index.ts +0 -1
- package/Consts/theme.ts +3 -4
- package/Input/Input.tsx +3 -4
- package/Input/InputMoney.tsx +31 -51
- package/Input/InputPhoneNumber.tsx +3 -4
- package/Input/InputSearch.tsx +3 -4
- package/Input/InputTextArea.tsx +3 -4
- package/Layout/Card.tsx +8 -26
- package/Layout/FloatingButton.tsx +3 -3
- package/Layout/Item.tsx +1 -10
- package/Layout/ReservedRegions.tsx +47 -0
- package/Layout/Screen.tsx +240 -48
- package/Layout/Section.tsx +5 -18
- package/Layout/index.ts +4 -4
- package/Layout/styles.ts +5 -0
- package/Layout/utils.ts +375 -5
- package/Popup/PopupNotify.tsx +33 -22
- package/Popup/PopupPromotion.tsx +20 -8
- package/Popup/utils.ts +32 -0
- package/Text/index.tsx +5 -15
- package/Text/utils.ts +1 -2
- package/package.json +3 -2
- package/Consts/designSystemFont.ts +0 -25
- package/Layout/GridSystem.tsx +0 -137
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import React, { ReactNode, useContext } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleProp,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
View,
|
|
7
|
+
ViewStyle,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import { NavigationButtonProps } from '../types';
|
|
10
|
+
import { ApplicationContext } from '../../Context';
|
|
11
|
+
import { Colors, Radius, Shadow, Spacing } from '../../Consts';
|
|
12
|
+
import { Icon } from '../../Icon';
|
|
13
|
+
import { BadgeDot } from '../../Badge';
|
|
14
|
+
import { DuoMetrics } from '../../Layout/utils';
|
|
15
|
+
|
|
16
|
+
const SIDE_BAR_ICON_SIZE = 24;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* glass material for side bar controls: dark glass under light icons (banner headers),
|
|
20
|
+
* light glass otherwise
|
|
21
|
+
*/
|
|
22
|
+
const getGlassStyle = (tintColor?: string) =>
|
|
23
|
+
tintColor === Colors.black_01 || tintColor === 'white'
|
|
24
|
+
? styles.glassDark
|
|
25
|
+
: styles.glassLight;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 48pt glass circle used for a single control in the iPhone Duo side bar
|
|
29
|
+
*/
|
|
30
|
+
const SideBarButton: React.FC<NavigationButtonProps> = ({
|
|
31
|
+
icon,
|
|
32
|
+
tintColor,
|
|
33
|
+
showBadge = false,
|
|
34
|
+
style,
|
|
35
|
+
...props
|
|
36
|
+
}) => {
|
|
37
|
+
const { theme } = useContext(ApplicationContext);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View>
|
|
41
|
+
<TouchableOpacity
|
|
42
|
+
{...props}
|
|
43
|
+
style={[styles.control, getGlassStyle(tintColor), style]}
|
|
44
|
+
>
|
|
45
|
+
<Icon
|
|
46
|
+
source={icon}
|
|
47
|
+
color={tintColor ?? theme.colors.text.default}
|
|
48
|
+
size={SIDE_BAR_ICON_SIZE}
|
|
49
|
+
/>
|
|
50
|
+
</TouchableOpacity>
|
|
51
|
+
{showBadge && <BadgeDot size={'small'} style={styles.badgeDot} />}
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 48pt wide glass capsule grouping related controls, one item per 50pt
|
|
58
|
+
*/
|
|
59
|
+
const SideBarGroup: React.FC<{
|
|
60
|
+
tintColor?: string;
|
|
61
|
+
style?: StyleProp<ViewStyle>;
|
|
62
|
+
children: ReactNode;
|
|
63
|
+
}> = ({ tintColor, style, children }) => {
|
|
64
|
+
const count = React.Children.toArray(children).length;
|
|
65
|
+
const height =
|
|
66
|
+
count > 1 ? count * DuoMetrics.itemPitch : DuoMetrics.controlSize;
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<View style={[styles.control, getGlassStyle(tintColor), { height }, style]}>
|
|
70
|
+
{children}
|
|
71
|
+
</View>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* icon item inside a SideBarGroup
|
|
77
|
+
*/
|
|
78
|
+
const SideBarGroupItem: React.FC<NavigationButtonProps> = ({
|
|
79
|
+
icon,
|
|
80
|
+
tintColor,
|
|
81
|
+
...props
|
|
82
|
+
}) => {
|
|
83
|
+
const { theme } = useContext(ApplicationContext);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<TouchableOpacity {...props} style={styles.groupItem}>
|
|
87
|
+
<Icon
|
|
88
|
+
source={icon}
|
|
89
|
+
color={tintColor ?? theme.colors.text.default}
|
|
90
|
+
size={SIDE_BAR_ICON_SIZE}
|
|
91
|
+
/>
|
|
92
|
+
</TouchableOpacity>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const styles = StyleSheet.create({
|
|
97
|
+
control: {
|
|
98
|
+
width: DuoMetrics.controlSize,
|
|
99
|
+
height: DuoMetrics.controlSize,
|
|
100
|
+
borderRadius: Radius.XL,
|
|
101
|
+
justifyContent: 'center',
|
|
102
|
+
alignItems: 'center',
|
|
103
|
+
},
|
|
104
|
+
glassLight: {
|
|
105
|
+
backgroundColor: Colors.black_01 + 'D9',
|
|
106
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
107
|
+
borderColor: Colors.black_20 + '14',
|
|
108
|
+
...Shadow.Light,
|
|
109
|
+
},
|
|
110
|
+
glassDark: {
|
|
111
|
+
backgroundColor: Colors.black_20 + '80',
|
|
112
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
113
|
+
borderColor: Colors.black_01 + '33',
|
|
114
|
+
},
|
|
115
|
+
groupItem: {
|
|
116
|
+
flex: 1,
|
|
117
|
+
alignSelf: 'stretch',
|
|
118
|
+
justifyContent: 'center',
|
|
119
|
+
alignItems: 'center',
|
|
120
|
+
},
|
|
121
|
+
badgeDot: {
|
|
122
|
+
position: 'absolute',
|
|
123
|
+
top: Spacing.XXS,
|
|
124
|
+
right: Spacing.XXS,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export { SideBarButton, SideBarGroup, SideBarGroupItem };
|
|
@@ -8,15 +8,18 @@ import {
|
|
|
8
8
|
Platform,
|
|
9
9
|
Pressable,
|
|
10
10
|
StyleSheet,
|
|
11
|
+
useWindowDimensions,
|
|
11
12
|
View,
|
|
12
13
|
} from 'react-native';
|
|
14
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
13
15
|
|
|
14
16
|
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
15
17
|
import { Styles } from '../Consts';
|
|
16
18
|
import Navigation from './Navigation';
|
|
17
19
|
import { ModalParams } from './types';
|
|
18
20
|
import BottomSheet from './BottomSheet';
|
|
19
|
-
import {
|
|
21
|
+
import { getDuoTopInset, isIPhoneDuo } from './device';
|
|
22
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
20
23
|
|
|
21
24
|
const ModalScreen: React.FC<any> = props => {
|
|
22
25
|
if (props.route?.params?.isBottomSheet) {
|
|
@@ -37,6 +40,8 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
37
40
|
title,
|
|
38
41
|
} = props.route.params;
|
|
39
42
|
const Component = useRef(screen).current;
|
|
43
|
+
const insets = useSafeAreaInsets();
|
|
44
|
+
const windowSize = useWindowDimensions();
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* Fire auto_popup_displayed tracking event on mount
|
|
@@ -106,7 +111,7 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
106
111
|
}),
|
|
107
112
|
]).start(() => {
|
|
108
113
|
navigator?.pop();
|
|
109
|
-
|
|
114
|
+
scheduleOnRN(callback);
|
|
110
115
|
});
|
|
111
116
|
},
|
|
112
117
|
[navigator, opacity, scale],
|
|
@@ -147,6 +152,15 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
147
152
|
opacity,
|
|
148
153
|
transform: [{ scale }],
|
|
149
154
|
},
|
|
155
|
+
// center below the side status bar and above the home indicator
|
|
156
|
+
isIPhoneDuo() && {
|
|
157
|
+
marginTop: getDuoTopInset(
|
|
158
|
+
insets,
|
|
159
|
+
windowSize.width,
|
|
160
|
+
windowSize.height,
|
|
161
|
+
),
|
|
162
|
+
marginBottom: insets.bottom,
|
|
163
|
+
},
|
|
150
164
|
modalStyle,
|
|
151
165
|
]}
|
|
152
166
|
>
|
|
@@ -2,7 +2,6 @@ import React, { useContext, useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
SafeAreaInsetsContext,
|
|
4
4
|
SafeAreaProvider,
|
|
5
|
-
useSafeAreaInsets,
|
|
6
5
|
} from 'react-native-safe-area-context';
|
|
7
6
|
import {
|
|
8
7
|
NavigationContainer as ReactNavigationContainer,
|
|
@@ -13,17 +12,17 @@ import {
|
|
|
13
12
|
StackNavigationOptions,
|
|
14
13
|
TransitionPresets,
|
|
15
14
|
} from '@react-navigation/stack';
|
|
16
|
-
import { DeviceEventEmitter } from 'react-native';
|
|
17
15
|
import StackScreen from './StackScreen';
|
|
18
16
|
import ModalScreen from './ModalScreen';
|
|
19
17
|
import Navigator from './Navigator';
|
|
20
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
getModalOptions,
|
|
20
|
+
getStackOptions,
|
|
21
|
+
isWhiteListDesignSystem,
|
|
22
|
+
useNativeCanGoBack,
|
|
23
|
+
} from './utils';
|
|
21
24
|
import { NavigationContainerProps } from './types';
|
|
22
25
|
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
23
|
-
import {
|
|
24
|
-
useDefaultRegularFontFamily,
|
|
25
|
-
useResolvedFontFamily,
|
|
26
|
-
} from '../Consts/designSystemFont';
|
|
27
26
|
import Localize from './Localize';
|
|
28
27
|
import { Colors, defaultTheme } from '../Consts';
|
|
29
28
|
import { HeaderLeft } from './Components/HeaderLeft';
|
|
@@ -31,6 +30,8 @@ import { HeaderRight } from './Components/HeaderRight';
|
|
|
31
30
|
import { HeaderTitle } from './Components/HeaderTitle';
|
|
32
31
|
import { HeaderBackground } from './Components/HeaderBackground';
|
|
33
32
|
import { type EdgeInsets } from 'react-native-safe-area-context';
|
|
33
|
+
import { useScreenLayout } from '../Layout';
|
|
34
|
+
import { ReservedRegionsProvider } from '../Layout/ReservedRegions';
|
|
34
35
|
|
|
35
36
|
const Stack = createStackNavigator();
|
|
36
37
|
|
|
@@ -76,17 +77,19 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
76
77
|
frame: { x: 0, y: 0, width: 0, height: 0 },
|
|
77
78
|
}}
|
|
78
79
|
>
|
|
79
|
-
<
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
<ReservedRegionsProvider>
|
|
81
|
+
<MiniAppContext.Provider value={mergedContext}>
|
|
82
|
+
<Navigation
|
|
83
|
+
screen={screen}
|
|
84
|
+
theme={theme}
|
|
85
|
+
options={options}
|
|
86
|
+
maxApi={maxApi}
|
|
87
|
+
setCurrentContext={setCurrentContext}
|
|
88
|
+
initialParams={initialParams}
|
|
89
|
+
localize={localize}
|
|
90
|
+
/>
|
|
91
|
+
</MiniAppContext.Provider>
|
|
92
|
+
</ReservedRegionsProvider>
|
|
90
93
|
</SafeAreaProvider>
|
|
91
94
|
);
|
|
92
95
|
};
|
|
@@ -104,11 +107,11 @@ const Navigation: React.FC<any> = ({
|
|
|
104
107
|
const navigationRef = useRef<any>(null);
|
|
105
108
|
const isReady = useRef(false);
|
|
106
109
|
const navigator = useRef(new Navigator(navigationRef, isReady));
|
|
107
|
-
const
|
|
108
|
-
const insets = useSafeAreaInsets();
|
|
109
|
-
const defaultRegularFontFamily = useDefaultRegularFontFamily();
|
|
110
|
-
const themeFont = useResolvedFontFamily(theme.font);
|
|
110
|
+
const { headerHeight, headerStatusBarHeight } = useScreenLayout();
|
|
111
111
|
const syncCanGoBack = useNativeCanGoBack();
|
|
112
|
+
const isWhiteList = isWhiteListDesignSystem(context);
|
|
113
|
+
const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
|
|
114
|
+
const navigationFontFamily = isWhiteList ? 'MomoTrustSans-Regular' : 'SFProText';
|
|
112
115
|
let headerBackground = context?.designSystemConfig?.config?.headerBar;
|
|
113
116
|
let headerGradient = theme.colors?.gradient;
|
|
114
117
|
|
|
@@ -124,23 +127,6 @@ const Navigation: React.FC<any> = ({
|
|
|
124
127
|
*/
|
|
125
128
|
navigator.current.maxApi = maxApi;
|
|
126
129
|
|
|
127
|
-
/**
|
|
128
|
-
* handle mini language & listen change
|
|
129
|
-
* engine only shake to enable grid view
|
|
130
|
-
*/
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
const subscription = DeviceEventEmitter.addListener(
|
|
133
|
-
'onChangeGrid',
|
|
134
|
-
enable => {
|
|
135
|
-
setShowGrid(!!enable);
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
return () => {
|
|
140
|
-
subscription?.remove?.();
|
|
141
|
-
};
|
|
142
|
-
}, []);
|
|
143
|
-
|
|
144
130
|
/**
|
|
145
131
|
* translate context method
|
|
146
132
|
* @param data
|
|
@@ -160,7 +146,7 @@ const Navigation: React.FC<any> = ({
|
|
|
160
146
|
navigator: navigator.current,
|
|
161
147
|
theme: {
|
|
162
148
|
...theme,
|
|
163
|
-
font:
|
|
149
|
+
font: fontFamily,
|
|
164
150
|
colors: {
|
|
165
151
|
...theme.colors,
|
|
166
152
|
gradient: headerGradient,
|
|
@@ -170,7 +156,6 @@ const Navigation: React.FC<any> = ({
|
|
|
170
156
|
headerBackground,
|
|
171
157
|
},
|
|
172
158
|
},
|
|
173
|
-
showGrid,
|
|
174
159
|
translate,
|
|
175
160
|
}}
|
|
176
161
|
>
|
|
@@ -188,19 +173,19 @@ const Navigation: React.FC<any> = ({
|
|
|
188
173
|
},
|
|
189
174
|
fonts: {
|
|
190
175
|
regular: {
|
|
191
|
-
fontFamily:
|
|
176
|
+
fontFamily: navigationFontFamily,
|
|
192
177
|
fontWeight: '400',
|
|
193
178
|
},
|
|
194
179
|
medium: {
|
|
195
|
-
fontFamily:
|
|
180
|
+
fontFamily: navigationFontFamily,
|
|
196
181
|
fontWeight: '400',
|
|
197
182
|
},
|
|
198
183
|
bold: {
|
|
199
|
-
fontFamily:
|
|
184
|
+
fontFamily: navigationFontFamily,
|
|
200
185
|
fontWeight: '400',
|
|
201
186
|
},
|
|
202
187
|
heavy: {
|
|
203
|
-
fontFamily:
|
|
188
|
+
fontFamily: navigationFontFamily,
|
|
204
189
|
fontWeight: '400',
|
|
205
190
|
},
|
|
206
191
|
},
|
|
@@ -220,8 +205,9 @@ const Navigation: React.FC<any> = ({
|
|
|
220
205
|
headerTitleAlign: 'center',
|
|
221
206
|
headerTintColor: theme.colors.text.default,
|
|
222
207
|
headerStyle: {
|
|
223
|
-
height:
|
|
208
|
+
height: headerHeight,
|
|
224
209
|
},
|
|
210
|
+
headerStatusBarHeight,
|
|
225
211
|
}}
|
|
226
212
|
>
|
|
227
213
|
<Stack.Screen
|
|
@@ -244,11 +230,13 @@ const Navigation: React.FC<any> = ({
|
|
|
244
230
|
cardStyle: {
|
|
245
231
|
backgroundColor: theme.colors.background.default,
|
|
246
232
|
},
|
|
247
|
-
headerLeft: (props: any) => <HeaderLeft {...props} />,
|
|
233
|
+
headerLeft: (props: any) => <HeaderLeft {...props} floating />,
|
|
248
234
|
headerBackground: (props: any) => (
|
|
249
235
|
<HeaderBackground {...props} />
|
|
250
236
|
),
|
|
251
|
-
headerRight: (props: any) =>
|
|
237
|
+
headerRight: (props: any) => (
|
|
238
|
+
<HeaderRight {...props} floating />
|
|
239
|
+
),
|
|
252
240
|
headerTintColor: Colors.black_17,
|
|
253
241
|
...TransitionPresets.ModalTransition,
|
|
254
242
|
}}
|
package/Application/Navigator.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { useHeaderHeight } from '@react-navigation/elements';
|
|
|
4
4
|
import { ScreenTrackingParams } from './types';
|
|
5
5
|
import Navigation from './Navigation';
|
|
6
6
|
import { ApplicationContext, MiniAppContext, ScreenContext } from '../Context';
|
|
7
|
-
import { GridSystem } from '../Layout';
|
|
8
7
|
import { version } from '../package.json';
|
|
9
8
|
import { SwipeBackHaptic, useAppState } from './utils';
|
|
10
9
|
import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
|
|
@@ -17,7 +16,7 @@ const runAfterInteractions = InteractionManager.runAfterInteractions;
|
|
|
17
16
|
* @constructor
|
|
18
17
|
*/
|
|
19
18
|
const StackScreen: React.FC<any> = props => {
|
|
20
|
-
const {
|
|
19
|
+
const { navigator } = useContext(ApplicationContext);
|
|
21
20
|
const tracking = useRef<any>({
|
|
22
21
|
mounted: false,
|
|
23
22
|
timeoutLoad: undefined,
|
|
@@ -42,6 +41,7 @@ const StackScreen: React.FC<any> = props => {
|
|
|
42
41
|
});
|
|
43
42
|
const widgets = useRef<any>([]);
|
|
44
43
|
const context = useContext<any>(MiniAppContext);
|
|
44
|
+
|
|
45
45
|
const {
|
|
46
46
|
screen: Component,
|
|
47
47
|
options,
|
|
@@ -411,10 +411,7 @@ const StackScreen: React.FC<any> = props => {
|
|
|
411
411
|
>
|
|
412
412
|
<TooltipPortalProvider>
|
|
413
413
|
<Component heightHeader={heightHeader} {...data} />
|
|
414
|
-
{showGrid && <GridSystem />}
|
|
415
414
|
<TooltipPortalHost />
|
|
416
|
-
{/* a leaf on purpose: it subscribes to CardAnimationContext, which changes on every
|
|
417
|
-
Card render, and must not pull this screen into those renders */}
|
|
418
415
|
<SwipeBackHaptic />
|
|
419
416
|
</TooltipPortalProvider>
|
|
420
417
|
</ScreenContext.Provider>
|
|
@@ -3,10 +3,6 @@ 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 {
|
|
7
|
-
resolveFontFamily,
|
|
8
|
-
useDefaultRegularFontFamily,
|
|
9
|
-
} from '../Consts/designSystemFont';
|
|
10
6
|
import Localize from './Localize';
|
|
11
7
|
import { defaultTheme } from '../Consts';
|
|
12
8
|
import {
|
|
@@ -14,7 +10,7 @@ import {
|
|
|
14
10
|
NavigationContainerRef,
|
|
15
11
|
NavigationIndependentTree,
|
|
16
12
|
} from '@react-navigation/native';
|
|
17
|
-
import { getModalOptions } from './utils';
|
|
13
|
+
import { getModalOptions, isWhiteListDesignSystem } from './utils';
|
|
18
14
|
import { createStackNavigator } from '@react-navigation/stack';
|
|
19
15
|
import ModalScreen from './ModalScreen';
|
|
20
16
|
import { View } from 'react-native';
|
|
@@ -46,7 +42,6 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
46
42
|
const isReady = useRef(false);
|
|
47
43
|
const navigator = useRef(new Navigator(navigationRef, isReady, true));
|
|
48
44
|
const [currentContext, setCurrentContext] = useState({});
|
|
49
|
-
const defaultRegularFontFamily = useDefaultRegularFontFamily();
|
|
50
45
|
|
|
51
46
|
useEffect(() => {
|
|
52
47
|
const sub = maxApi?.observer?.('font_scale_config', (data: any) => {
|
|
@@ -83,6 +78,9 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
83
78
|
...context,
|
|
84
79
|
...currentContext,
|
|
85
80
|
};
|
|
81
|
+
const isWhiteList = isWhiteListDesignSystem(mergedContext);
|
|
82
|
+
const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
|
|
83
|
+
const navigationFontFamily = isWhiteList ? 'MomoTrustSans-Regular' : 'SFProText';
|
|
86
84
|
|
|
87
85
|
return (
|
|
88
86
|
<View style={{ height: height ?? '100%', minHeight: 1 }}>
|
|
@@ -93,11 +91,7 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
93
91
|
navigator: navigator.current,
|
|
94
92
|
theme: {
|
|
95
93
|
...theme,
|
|
96
|
-
font:
|
|
97
|
-
theme.font,
|
|
98
|
-
mergedContext?.designSystemConfig?.isWhiteListDesignSystem ===
|
|
99
|
-
true,
|
|
100
|
-
),
|
|
94
|
+
font: fontFamily,
|
|
101
95
|
colors: {
|
|
102
96
|
...theme.colors,
|
|
103
97
|
gradient: headerGradient,
|
|
@@ -124,19 +118,19 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
124
118
|
},
|
|
125
119
|
fonts: {
|
|
126
120
|
regular: {
|
|
127
|
-
fontFamily:
|
|
121
|
+
fontFamily: navigationFontFamily,
|
|
128
122
|
fontWeight: '400',
|
|
129
123
|
},
|
|
130
124
|
medium: {
|
|
131
|
-
fontFamily:
|
|
125
|
+
fontFamily: navigationFontFamily,
|
|
132
126
|
fontWeight: '400',
|
|
133
127
|
},
|
|
134
128
|
bold: {
|
|
135
|
-
fontFamily:
|
|
129
|
+
fontFamily: navigationFontFamily,
|
|
136
130
|
fontWeight: '400',
|
|
137
131
|
},
|
|
138
132
|
heavy: {
|
|
139
|
-
fontFamily:
|
|
133
|
+
fontFamily: navigationFontFamily,
|
|
140
134
|
fontWeight: '400',
|
|
141
135
|
},
|
|
142
136
|
},
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Dimensions, Platform, ScaledSize } from 'react-native';
|
|
2
|
+
import type { EdgeInsets } from 'react-native-safe-area-context';
|
|
3
|
+
|
|
4
|
+
// widest short side of any single-display iPhone
|
|
5
|
+
const IPHONE_MAX_SHORT_SIDE = 440;
|
|
6
|
+
|
|
7
|
+
// iPhone Duo reports idiom "phone"; 'screen' stays on the outer display, so it
|
|
8
|
+
// still matches when Split View narrows the window
|
|
9
|
+
const isIPhoneDuo = () => {
|
|
10
|
+
if (Platform.OS !== 'ios' || Platform.isPad) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const shortSide = (size: ScaledSize) => Math.min(size.width, size.height);
|
|
14
|
+
return (
|
|
15
|
+
Math.max(
|
|
16
|
+
shortSide(Dimensions.get('screen')),
|
|
17
|
+
shortSide(Dimensions.get('window')),
|
|
18
|
+
) > IPHONE_MAX_SHORT_SIDE
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// portrait side status bar is about twice as tall as its inset is wide
|
|
23
|
+
const getDuoTopInset = (insets: EdgeInsets, width: number, height: number) =>
|
|
24
|
+
Math.max(
|
|
25
|
+
insets.top,
|
|
26
|
+
height > width ? Math.max(insets.left, insets.right) * 2 : 0,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export { isIPhoneDuo, getDuoTopInset };
|
package/Application/types.ts
CHANGED
|
@@ -117,7 +117,6 @@ export type Context = {
|
|
|
117
117
|
[key: string]: any;
|
|
118
118
|
theme: Theme;
|
|
119
119
|
navigator?: NavigatorProps;
|
|
120
|
-
showGrid?: boolean;
|
|
121
120
|
translate?: (data: string | { vi: string; en: string }) => string;
|
|
122
121
|
};
|
|
123
122
|
|
|
@@ -258,6 +257,11 @@ export interface HeaderBackProps extends NavigationButtonProps {
|
|
|
258
257
|
preventBack?: PopupNotifyProps;
|
|
259
258
|
onPressLeftHeader?: () => void;
|
|
260
259
|
onBackHandler?: (goBack: () => void) => void;
|
|
260
|
+
/**
|
|
261
|
+
* Optional. If `true`, the button floats into the iPhone Duo side bar while the
|
|
262
|
+
* device shows one. Set when the button is rendered by the navigation header.
|
|
263
|
+
*/
|
|
264
|
+
floating?: boolean;
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
export type HeaderBackgroundProps = {
|
|
@@ -329,6 +333,11 @@ export type BottomTabProps = {
|
|
|
329
333
|
blur?: (e: EventArg<'blur', false, unknown>) => void;
|
|
330
334
|
};
|
|
331
335
|
floatingButton?: FloatingButtonProps;
|
|
336
|
+
/**
|
|
337
|
+
* iPhone Duo: when the side bar can't hold both bars, which one starts compressed;
|
|
338
|
+
* tapping the compressed one swaps them. Defaults to 'toolbar'
|
|
339
|
+
*/
|
|
340
|
+
compressionBehavior?: 'toolbar' | 'tabBar';
|
|
332
341
|
};
|
|
333
342
|
|
|
334
343
|
export type FloatingButtonProps = {
|
package/Application/utils.tsx
CHANGED
|
@@ -73,8 +73,8 @@ const getStackOptions = (): StackNavigationOptions => {
|
|
|
73
73
|
left: Spacing.S,
|
|
74
74
|
},
|
|
75
75
|
headerBackground: (props: any) => <HeaderBackground {...props} />,
|
|
76
|
-
headerLeft: (props: any) => <HeaderLeft {...props} />,
|
|
77
|
-
headerRight: (props: any) => <HeaderRight {...props} />,
|
|
76
|
+
headerLeft: (props: any) => <HeaderLeft {...props} floating />,
|
|
77
|
+
headerRight: (props: any) => <HeaderRight {...props} floating />,
|
|
78
78
|
headerTintColor: Colors.black_17,
|
|
79
79
|
gestureEnabled: Platform.OS === 'ios',
|
|
80
80
|
...TransitionPresets.SlideFromRightIOS,
|
|
@@ -129,6 +129,7 @@ const getOptions = (
|
|
|
129
129
|
preventBack={params.preventBack}
|
|
130
130
|
onPressLeftHeader={params.onPressLeftHeader}
|
|
131
131
|
onBackHandler={params.onBackHandler}
|
|
132
|
+
floating
|
|
132
133
|
/>
|
|
133
134
|
);
|
|
134
135
|
if (params.hiddenBack) {
|
|
@@ -167,7 +168,9 @@ const getOptions = (
|
|
|
167
168
|
...options,
|
|
168
169
|
...{
|
|
169
170
|
headerRight: (props: any) => {
|
|
170
|
-
return
|
|
171
|
+
return (
|
|
172
|
+
<HeaderRight {...props} headerRight={params.headerRight} floating />
|
|
173
|
+
);
|
|
171
174
|
},
|
|
172
175
|
},
|
|
173
176
|
};
|
|
@@ -475,6 +478,9 @@ const SwipeBackHaptic = () => {
|
|
|
475
478
|
return null;
|
|
476
479
|
};
|
|
477
480
|
|
|
481
|
+
const isWhiteListDesignSystem = (context: any): boolean =>
|
|
482
|
+
context?.designSystemConfig?.isWhiteListDesignSystem === true;
|
|
483
|
+
|
|
478
484
|
export {
|
|
479
485
|
getStackOptions,
|
|
480
486
|
getModalOptions,
|
|
@@ -487,4 +493,5 @@ export {
|
|
|
487
493
|
useNativeCanGoBack,
|
|
488
494
|
useNormalizedSharedValue,
|
|
489
495
|
SwipeBackHaptic,
|
|
496
|
+
isWhiteListDesignSystem,
|
|
490
497
|
};
|
package/Consts/index.ts
CHANGED
package/Consts/theme.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {Context, Theme} from '../Application/types';
|
|
2
|
-
import {Colors} from './colors+spacing+radius';
|
|
1
|
+
import type { Context, Theme } from '../Application/types';
|
|
2
|
+
import { Colors } from './colors+spacing+radius';
|
|
3
3
|
|
|
4
4
|
const defaultTheme: Theme = {
|
|
5
5
|
dark: false,
|
|
@@ -112,10 +112,9 @@ const defaultDarkTheme: Theme = {
|
|
|
112
112
|
const defaultContext: Context = {
|
|
113
113
|
theme: defaultTheme,
|
|
114
114
|
navigator: undefined,
|
|
115
|
-
showGrid: false,
|
|
116
115
|
translate: () => {
|
|
117
116
|
return '';
|
|
118
117
|
},
|
|
119
118
|
};
|
|
120
119
|
|
|
121
|
-
export {defaultContext, defaultTheme, defaultDarkTheme};
|
|
120
|
+
export { defaultContext, defaultTheme, defaultDarkTheme };
|
package/Input/Input.tsx
CHANGED
|
@@ -6,9 +6,8 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import {
|
|
9
|
-
NativeSyntheticEvent,
|
|
10
9
|
TextInput,
|
|
11
|
-
|
|
10
|
+
TextInputFocusEvent,
|
|
12
11
|
TouchableOpacity,
|
|
13
12
|
View,
|
|
14
13
|
} from 'react-native';
|
|
@@ -102,12 +101,12 @@ const Input = forwardRef(
|
|
|
102
101
|
onChangeText?.(text);
|
|
103
102
|
};
|
|
104
103
|
|
|
105
|
-
const _onFocus = (e:
|
|
104
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
106
105
|
setFocused(true);
|
|
107
106
|
onFocus?.(e);
|
|
108
107
|
};
|
|
109
108
|
|
|
110
|
-
const _onBlur = (e:
|
|
109
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
111
110
|
setFocused(false);
|
|
112
111
|
onBlur?.(e);
|
|
113
112
|
};
|