@momo-kits/foundation 0.169.9-beta.1 → 0.169.9
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 +27 -17
- package/Application/BottomSheet.tsx +35 -27
- package/Application/BottomTab/ActiveIndicator.tsx +5 -1
- package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
- package/Application/BottomTab/BottomTabBar.tsx +14 -16
- package/Application/BottomTab/CurvedContainer.tsx +8 -2
- package/Application/BottomTab/CustomBottomTabItem.tsx +2 -2
- package/Application/BottomTab/DuoRailTabBar.tsx +5 -5
- package/Application/BottomTab/DuoTabBar.tsx +15 -3
- package/Application/BottomTab/TabScreen.tsx +11 -10
- package/Application/BottomTab/index.tsx +5 -5
- package/Application/BottomTab/metrics.ts +23 -3
- package/Application/BottomTab/usePressLift.ts +3 -3
- package/Application/Components/HeaderAnimated.tsx +13 -38
- package/Application/Components/HeaderBackground.tsx +3 -16
- package/Application/Components/HeaderExtendHeader.tsx +28 -41
- package/Application/Components/HeaderLeft.tsx +18 -4
- package/Application/Components/HeaderRight.styles.ts +13 -15
- package/Application/Components/HeaderTitle.shared.ts +11 -10
- package/Application/Components/HeaderToolkitAction.tsx +54 -21
- package/Application/Components/NavigationButton.tsx +6 -9
- package/Application/Components/SearchHeader.tsx +7 -20
- package/Application/Components/useToolkitActions.tsx +22 -21
- package/Application/ModalScreen.tsx +19 -15
- package/Application/StackScreen.tsx +13 -18
- package/Application/index.ts +1 -11
- package/Application/modalLayout.ts +1 -0
- package/Application/navigationOptions.tsx +15 -6
- package/Application/types.header.ts +2 -7
- package/Application/types.tabs.ts +0 -1
- package/Application/types.ts +0 -7
- package/Context/index.ts +3 -1
- package/Image/index.tsx +6 -6
- package/Input/ErrorView.tsx +59 -0
- package/Input/FloatingView.tsx +101 -0
- package/Input/Input.tsx +40 -37
- package/Input/InputDropDown.tsx +13 -10
- package/Input/InputMoney.tsx +17 -17
- package/Input/InputMoneyView.tsx +12 -12
- package/Input/InputOTP.tsx +46 -34
- package/Input/InputPhoneNumber.tsx +48 -43
- package/Input/InputSearch.tsx +26 -39
- package/Input/InputTextArea.tsx +29 -23
- package/Input/RenderTrailing.tsx +79 -0
- package/Input/constants.ts +8 -0
- package/Input/index.tsx +1 -7
- package/Input/utils.ts +126 -7
- package/Layout/FloatingButton.tsx +38 -24
- package/Layout/Screen.tsx +0 -3
- package/Layout/layoutMetrics.ts +43 -34
- package/Layout/screenHeader.tsx +52 -55
- package/Layout/screenTypes.ts +4 -13
- package/Pagination/PaginationScroll.tsx +5 -3
- package/Pagination/types.ts +3 -5
- package/Popup/PopupNotify.tsx +17 -14
- package/Popup/PopupPromotion.tsx +5 -4
- package/Popup/types.ts +2 -4
- package/Popup/utils.ts +7 -4
- package/Skeleton/index.tsx +15 -12
- package/package.json +1 -1
- package/Application/headerLayout.ts +0 -165
- package/Input/common.tsx +0 -257
- package/Input/moneyUtils.ts +0 -23
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { StyleSheet
|
|
2
|
-
import LinearGradient from 'react-native-linear-gradient';
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
3
2
|
import Animated, {
|
|
4
3
|
Extrapolation,
|
|
5
4
|
interpolate,
|
|
@@ -10,7 +9,6 @@ import React from 'react';
|
|
|
10
9
|
import { Image } from '../../Image';
|
|
11
10
|
import { Colors } from '../../Consts';
|
|
12
11
|
import { useNormalizedSharedValue } from '../hooks';
|
|
13
|
-
import { HeaderBarMetrics, useHeaderLayout } from '../headerLayout';
|
|
14
12
|
|
|
15
13
|
const ASPECT_RATIOS: Record<
|
|
16
14
|
NonNullable<HeaderAnimatedProps['aspectRatio']>,
|
|
@@ -23,20 +21,16 @@ const ASPECT_RATIOS: Record<
|
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* Parallax header image (or `children`): stretches when pulled down and fades out
|
|
26
|
-
* as the content scrolls up.
|
|
27
|
-
* it at phone size, centered.
|
|
24
|
+
* as the content scrolls up.
|
|
28
25
|
*/
|
|
29
26
|
const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
|
|
30
27
|
animatedValue,
|
|
31
28
|
image,
|
|
32
29
|
useScale = true,
|
|
33
30
|
aspectRatio = '16/9',
|
|
34
|
-
bannerBackgroundColor,
|
|
35
31
|
children,
|
|
36
32
|
}) => {
|
|
37
33
|
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
38
|
-
const { isBannerCentered, bannerWidth } = useHeaderLayout();
|
|
39
|
-
const ratio = ASPECT_RATIOS[aspectRatio];
|
|
40
34
|
const animatedStyle = useAnimatedStyle(() => {
|
|
41
35
|
let scale = 1;
|
|
42
36
|
if (useScale) {
|
|
@@ -65,36 +59,22 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
|
|
|
65
59
|
};
|
|
66
60
|
});
|
|
67
61
|
|
|
68
|
-
const content = children ?? (
|
|
69
|
-
<Image
|
|
70
|
-
source={{
|
|
71
|
-
uri: image,
|
|
72
|
-
}}
|
|
73
|
-
style={styles.full}
|
|
74
|
-
/>
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
if (!isBannerCentered) {
|
|
78
|
-
return (
|
|
79
|
-
<Animated.View
|
|
80
|
-
style={[styles.container, { aspectRatio: ratio }, animatedStyle]}
|
|
81
|
-
>
|
|
82
|
-
{content}
|
|
83
|
-
</Animated.View>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
62
|
return (
|
|
88
63
|
<Animated.View
|
|
89
|
-
style={[
|
|
64
|
+
style={[
|
|
65
|
+
styles.container,
|
|
66
|
+
{ aspectRatio: ASPECT_RATIOS[aspectRatio] },
|
|
67
|
+
animatedStyle,
|
|
68
|
+
]}
|
|
90
69
|
>
|
|
91
|
-
{
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
70
|
+
{children ?? (
|
|
71
|
+
<Image
|
|
72
|
+
source={{
|
|
73
|
+
uri: image,
|
|
74
|
+
}}
|
|
75
|
+
style={styles.full}
|
|
95
76
|
/>
|
|
96
77
|
)}
|
|
97
|
-
<View style={styles.phoneBanner}>{content}</View>
|
|
98
78
|
</Animated.View>
|
|
99
79
|
);
|
|
100
80
|
};
|
|
@@ -110,11 +90,6 @@ const styles = StyleSheet.create({
|
|
|
110
90
|
alignSelf: 'center',
|
|
111
91
|
},
|
|
112
92
|
full: { width: '100%', height: '100%' },
|
|
113
|
-
phoneBanner: {
|
|
114
|
-
width: HeaderBarMetrics.bannerWidth,
|
|
115
|
-
height: '100%',
|
|
116
|
-
alignSelf: 'center',
|
|
117
|
-
},
|
|
118
93
|
});
|
|
119
94
|
|
|
120
95
|
export { HeaderAnimated };
|
|
@@ -8,12 +8,10 @@ import { Colors, Styles } from '../../Consts';
|
|
|
8
8
|
import { Image } from '../../Image';
|
|
9
9
|
import BackgroundImageView from './BackgroundImageView';
|
|
10
10
|
import { useNormalizedSharedValue } from '../hooks';
|
|
11
|
-
import { HeaderBarMetrics, useHeaderLayout } from '../headerLayout';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Header background: surface and image that fade in on scroll, with an optional
|
|
15
|
-
* gradient (custom or theme) on top
|
|
16
|
-
* when `showBaseLineDebug` is on.
|
|
14
|
+
* gradient (custom or theme) on top. Outlined when `showBaseLineDebug` is on.
|
|
17
15
|
*/
|
|
18
16
|
const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
19
17
|
animatedValue,
|
|
@@ -27,7 +25,6 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
|
27
25
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
28
26
|
const headerImage = headerBackground ?? theme.assets?.headerBackground;
|
|
29
27
|
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
30
|
-
const { isWide } = useHeaderLayout();
|
|
31
28
|
|
|
32
29
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
33
30
|
|
|
@@ -41,19 +38,14 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
|
41
38
|
/>
|
|
42
39
|
<View style={styles.gradientContainer}>
|
|
43
40
|
{useGradient && !!gradientColor && (
|
|
44
|
-
<Animated.View
|
|
45
|
-
style={[styles.extendedHeader, isWide && styles.phoneHeight]}
|
|
46
|
-
>
|
|
41
|
+
<Animated.View style={styles.extendedHeader}>
|
|
47
42
|
<LinearGradient
|
|
48
43
|
colors={[gradientColor, gradientColor + '00']}
|
|
49
44
|
style={StyleSheet.absoluteFill}
|
|
50
45
|
>
|
|
51
46
|
{!!headerImage && (
|
|
52
47
|
<Image
|
|
53
|
-
style={
|
|
54
|
-
styles.headerBackground,
|
|
55
|
-
isWide && styles.phoneHeight,
|
|
56
|
-
]}
|
|
48
|
+
style={styles.headerBackground}
|
|
57
49
|
source={{ uri: headerImage }}
|
|
58
50
|
loading={false}
|
|
59
51
|
/>
|
|
@@ -83,11 +75,6 @@ const styles = StyleSheet.create({
|
|
|
83
75
|
position: 'absolute',
|
|
84
76
|
aspectRatio: 375 / 154,
|
|
85
77
|
},
|
|
86
|
-
// wide windows keep the phone gradient height instead of scaling with the width
|
|
87
|
-
phoneHeight: {
|
|
88
|
-
aspectRatio: undefined,
|
|
89
|
-
height: HeaderBarMetrics.gradientHeight,
|
|
90
|
-
},
|
|
91
78
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
92
79
|
});
|
|
93
80
|
|
|
@@ -17,16 +17,11 @@ import BackgroundImageView from './BackgroundImageView';
|
|
|
17
17
|
import { type AnimatedCompatValue } from '../utils';
|
|
18
18
|
import { useNormalizedSharedValue } from '../hooks';
|
|
19
19
|
import { useScreenLayout } from '../../Layout';
|
|
20
|
-
import { HeaderMetrics } from '../../Layout/utils';
|
|
21
|
-
import {
|
|
22
|
-
HeaderBarMetrics,
|
|
23
|
-
type HeaderContentLayout,
|
|
24
|
-
useHeaderLayout,
|
|
25
|
-
} from '../headerLayout';
|
|
20
|
+
import { DuoMetrics, HeaderMetrics } from '../../Layout/utils';
|
|
26
21
|
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const TOOLKIT_WIDTH =
|
|
22
|
+
const SCREEN_PADDING = 12;
|
|
23
|
+
const BACK_WIDTH = 28;
|
|
24
|
+
const TOOLKIT_WIDTH = 73;
|
|
30
25
|
|
|
31
26
|
/**
|
|
32
27
|
* Extended header background: a gradient that collapses on scroll and, with
|
|
@@ -43,7 +38,6 @@ const HeaderExtendHeader: React.FC<{
|
|
|
43
38
|
useShadowHeader?: boolean;
|
|
44
39
|
gradientColor?: string;
|
|
45
40
|
headerBackground?: string;
|
|
46
|
-
contentLayout?: HeaderContentLayout;
|
|
47
41
|
}> = ({
|
|
48
42
|
headerType = 'default',
|
|
49
43
|
animatedValue,
|
|
@@ -54,7 +48,6 @@ const HeaderExtendHeader: React.FC<{
|
|
|
54
48
|
useShadowHeader: useShadowHeaderProp = true,
|
|
55
49
|
gradientColor: customGradientColor,
|
|
56
50
|
headerBackground: customBackground,
|
|
57
|
-
contentLayout,
|
|
58
51
|
}) => {
|
|
59
52
|
const { theme } = useContext(ApplicationContext);
|
|
60
53
|
const context = useContext<any>(MiniAppContext);
|
|
@@ -62,32 +55,29 @@ const HeaderExtendHeader: React.FC<{
|
|
|
62
55
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
63
56
|
const headerBackground = customBackground ?? theme.assets?.headerBackground;
|
|
64
57
|
const layout = useScreenLayout();
|
|
65
|
-
const headerLayout = useHeaderLayout(contentLayout);
|
|
66
|
-
// the stretch frame pulls the search in from both bar edges
|
|
67
|
-
const frameShift = headerLayout.frameInset - sidePadding;
|
|
68
58
|
|
|
69
|
-
|
|
70
|
-
const
|
|
59
|
+
// the iPhone Duo status bar row keeps side bar sized back and toolkit
|
|
60
|
+
const isStatusBarRow = !!layout.statusBarClearance;
|
|
61
|
+
const duoToolkitWidth = DuoMetrics.itemPitch * 2 + SCREEN_PADDING;
|
|
62
|
+
const backWidth = isStatusBarRow ? DuoMetrics.controlSize : BACK_WIDTH;
|
|
63
|
+
const toolkitWidth = isStatusBarRow ? duoToolkitWidth : TOOLKIT_WIDTH;
|
|
64
|
+
const defaultLeft = layout.sideBar ? SCREEN_PADDING : backWidth + 20;
|
|
71
65
|
const leftPosition = inputSearchProps?.leftPosition || defaultLeft;
|
|
72
|
-
const defaultRightWidth = layout.sideBar ? 0 :
|
|
66
|
+
const defaultRightWidth = layout.sideBar ? 0 : toolkitWidth;
|
|
73
67
|
const headerRightWidth = customHeaderRightWidth ?? defaultRightWidth;
|
|
74
|
-
const contentLeft = layout.insetLeft + SCREEN_PADDING
|
|
75
|
-
const contentRight = layout.insetLeft + layout.contentWidth
|
|
68
|
+
const contentLeft = layout.insetLeft + SCREEN_PADDING;
|
|
69
|
+
const contentRight = layout.insetLeft + layout.contentWidth;
|
|
76
70
|
// collapsed on the status bar row, the search stays between its clusters
|
|
77
71
|
const clearanceLeft = layout.statusBarClearance?.left ?? 0;
|
|
78
72
|
const clearanceRight = layout.statusBarClearance?.right ?? 0;
|
|
79
|
-
const
|
|
80
|
-
const collapsedLeft = layout.insetLeft + searchLeft;
|
|
73
|
+
const collapsedLeft = layout.insetLeft + leftPosition + clearanceLeft;
|
|
81
74
|
const collapsedRight = contentRight - clearanceRight - headerRightWidth;
|
|
82
|
-
const searchWidth =
|
|
75
|
+
const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
|
|
83
76
|
const collapsedSearchWidth = collapsedRight - collapsedLeft - SCREEN_PADDING;
|
|
84
77
|
// a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
|
|
85
78
|
const titleRowHeight = layout.headerHeight - layout.headerStatusBarHeight;
|
|
86
79
|
const collapsedRaise = (titleRowHeight - HeaderMetrics.barHeight) / 2;
|
|
87
80
|
|
|
88
|
-
// the iPhone Duo side bar keeps the content search size
|
|
89
|
-
const searchSize = layout.sideBar ? 'default' : 'header';
|
|
90
|
-
|
|
91
81
|
let useShadowHeader = useShadowHeaderProp;
|
|
92
82
|
if (inputSearchProps && Platform.OS === 'android') {
|
|
93
83
|
useShadowHeader = false;
|
|
@@ -179,7 +169,6 @@ const HeaderExtendHeader: React.FC<{
|
|
|
179
169
|
<Animated.View
|
|
180
170
|
style={[
|
|
181
171
|
styles.extendedHeader,
|
|
182
|
-
headerLayout.isWide && styles.phoneHeight,
|
|
183
172
|
// fade out within the header box, as DuoScrollGradient does
|
|
184
173
|
!!layout.sideBar && styles.fillHeaderBox,
|
|
185
174
|
gradientOpacityStyle,
|
|
@@ -191,10 +180,7 @@ const HeaderExtendHeader: React.FC<{
|
|
|
191
180
|
>
|
|
192
181
|
{!!theme.assets?.headerBackground && (
|
|
193
182
|
<Image
|
|
194
|
-
style={
|
|
195
|
-
styles.headerBackground,
|
|
196
|
-
headerLayout.isWide && styles.phoneHeight,
|
|
197
|
-
]}
|
|
183
|
+
style={styles.headerBackground}
|
|
198
184
|
source={{ uri: theme.assets?.headerBackground }}
|
|
199
185
|
loading={false}
|
|
200
186
|
/>
|
|
@@ -203,7 +189,9 @@ const HeaderExtendHeader: React.FC<{
|
|
|
203
189
|
</Animated.View>
|
|
204
190
|
)}
|
|
205
191
|
</Animated.View>
|
|
206
|
-
<Animated.View
|
|
192
|
+
<Animated.View
|
|
193
|
+
style={[styles.searchContainer, heightStyle]}
|
|
194
|
+
>
|
|
207
195
|
<Animated.View
|
|
208
196
|
style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
|
|
209
197
|
>
|
|
@@ -213,7 +201,6 @@ const HeaderExtendHeader: React.FC<{
|
|
|
213
201
|
<InputSearch
|
|
214
202
|
{...inputSearchProps}
|
|
215
203
|
ref={inputSearchRef}
|
|
216
|
-
size={searchSize}
|
|
217
204
|
backgroundColor={'transparent'}
|
|
218
205
|
showButtonText={false}
|
|
219
206
|
/>
|
|
@@ -249,10 +236,7 @@ const HeaderExtendHeader: React.FC<{
|
|
|
249
236
|
>
|
|
250
237
|
{!!headerBackground && (
|
|
251
238
|
<Image
|
|
252
|
-
style={
|
|
253
|
-
styles.headerBackground,
|
|
254
|
-
headerLayout.isWide && styles.phoneHeight,
|
|
255
|
-
]}
|
|
239
|
+
style={styles.headerBackground}
|
|
256
240
|
source={{ uri: headerBackground }}
|
|
257
241
|
loading={false}
|
|
258
242
|
/>
|
|
@@ -280,11 +264,6 @@ const styles = StyleSheet.create({
|
|
|
280
264
|
position: 'absolute',
|
|
281
265
|
width: '100%',
|
|
282
266
|
},
|
|
283
|
-
// wide windows keep the phone gradient height instead of scaling with the width
|
|
284
|
-
phoneHeight: {
|
|
285
|
-
aspectRatio: undefined,
|
|
286
|
-
height: HeaderBarMetrics.gradientHeight,
|
|
287
|
-
},
|
|
288
267
|
fillHeaderBox: {
|
|
289
268
|
aspectRatio: undefined,
|
|
290
269
|
height: '100%',
|
|
@@ -294,6 +273,14 @@ const styles = StyleSheet.create({
|
|
|
294
273
|
position: 'absolute',
|
|
295
274
|
overflow: 'hidden',
|
|
296
275
|
},
|
|
276
|
+
verifiedDot: {
|
|
277
|
+
width: 4,
|
|
278
|
+
height: 4,
|
|
279
|
+
borderRadius: 2,
|
|
280
|
+
backgroundColor: Colors.green_03,
|
|
281
|
+
position: 'absolute',
|
|
282
|
+
alignSelf: 'center',
|
|
283
|
+
},
|
|
297
284
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
298
285
|
container: {
|
|
299
286
|
zIndex: 0,
|
|
@@ -6,13 +6,12 @@ import { PopupNotify } from '../../Popup';
|
|
|
6
6
|
import { NavigationButton } from './NavigationButton';
|
|
7
7
|
import { SideBarButton } from './SideBarButton';
|
|
8
8
|
import Animated from 'react-native-reanimated';
|
|
9
|
-
import { useSideBarItemStyle } from '../../Layout/utils';
|
|
9
|
+
import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
|
|
10
10
|
import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
|
|
11
|
-
import { HeaderBarMetrics } from '../headerLayout';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Header back button, also bound to the Android hardware back. `preventBack`
|
|
15
|
-
* confirms in a popup first; the iPhone Duo
|
|
14
|
+
* confirms in a popup first; the iPhone Duo layouts use the 48pt side bar button.
|
|
16
15
|
*/
|
|
17
16
|
const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
18
17
|
tintColor,
|
|
@@ -25,6 +24,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
25
24
|
const { navigator } = useContext(ApplicationContext);
|
|
26
25
|
const sideBarStyle = useSideBarItemStyle('left', 0);
|
|
27
26
|
const counterStyle = useSideBarCounterStyle();
|
|
27
|
+
const { statusBarClearance } = useScreenLayout();
|
|
28
28
|
|
|
29
29
|
const goBack = useCallback(() => {
|
|
30
30
|
const canGoBack = navigator?.ref?.current?.canGoBack?.();
|
|
@@ -97,6 +97,20 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
97
97
|
);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
// on the status bar row the controls keep the side bar size
|
|
101
|
+
if (statusBarClearance) {
|
|
102
|
+
return (
|
|
103
|
+
<View style={styles.headerLeft}>
|
|
104
|
+
<SideBarButton
|
|
105
|
+
accessibilityLabel="btn_navigation_back"
|
|
106
|
+
icon="arrow-back"
|
|
107
|
+
tintColor={tintColor}
|
|
108
|
+
onPress={goBackSafe}
|
|
109
|
+
/>
|
|
110
|
+
</View>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
100
114
|
return (
|
|
101
115
|
<View style={styles.headerLeft}>
|
|
102
116
|
<NavigationButton
|
|
@@ -111,7 +125,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
111
125
|
|
|
112
126
|
const styles = StyleSheet.create({
|
|
113
127
|
headerLeft: {
|
|
114
|
-
marginLeft:
|
|
128
|
+
marginLeft: 12,
|
|
115
129
|
},
|
|
116
130
|
});
|
|
117
131
|
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { Platform, StyleSheet } from 'react-native';
|
|
2
2
|
import { Colors, Spacing } from '../../Consts';
|
|
3
|
-
import { HeaderBarMetrics } from '../headerLayout';
|
|
4
|
-
|
|
5
|
-
const { buttonSize, pillWidth, pillHeight, sidePadding } = HeaderBarMetrics;
|
|
6
3
|
|
|
7
4
|
export const styles = StyleSheet.create({
|
|
8
5
|
navigationButton: {
|
|
9
|
-
height:
|
|
10
|
-
width:
|
|
11
|
-
borderRadius:
|
|
6
|
+
height: 28,
|
|
7
|
+
width: 28,
|
|
8
|
+
borderRadius: 14,
|
|
12
9
|
justifyContent: 'center',
|
|
13
10
|
alignItems: 'center',
|
|
14
11
|
backgroundColor: Colors.black_01 + '99',
|
|
@@ -48,11 +45,11 @@ export const styles = StyleSheet.create({
|
|
|
48
45
|
flexDirection: 'row',
|
|
49
46
|
justifyContent: 'center',
|
|
50
47
|
alignItems: 'center',
|
|
51
|
-
paddingRight:
|
|
48
|
+
paddingRight: Spacing.M,
|
|
52
49
|
},
|
|
53
50
|
toolkitContainer: {
|
|
54
|
-
height:
|
|
55
|
-
borderRadius:
|
|
51
|
+
height: 28,
|
|
52
|
+
borderRadius: 14,
|
|
56
53
|
justifyContent: 'center',
|
|
57
54
|
alignItems: 'center',
|
|
58
55
|
flexDirection: 'row',
|
|
@@ -62,10 +59,7 @@ export const styles = StyleSheet.create({
|
|
|
62
59
|
marginLeft: Spacing.S,
|
|
63
60
|
},
|
|
64
61
|
toolkitButton: {
|
|
65
|
-
|
|
66
|
-
height: '100%',
|
|
67
|
-
justifyContent: 'center',
|
|
68
|
-
alignItems: 'center',
|
|
62
|
+
padding: 4,
|
|
69
63
|
},
|
|
70
64
|
divider: {
|
|
71
65
|
width: 0.5,
|
|
@@ -74,6 +68,10 @@ export const styles = StyleSheet.create({
|
|
|
74
68
|
sideBarColumn: {
|
|
75
69
|
gap: Spacing.M,
|
|
76
70
|
},
|
|
71
|
+
sideBarRow: {
|
|
72
|
+
flexDirection: 'row',
|
|
73
|
+
gap: Spacing.M,
|
|
74
|
+
},
|
|
77
75
|
headerLeft: {
|
|
78
76
|
marginLeft: 12,
|
|
79
77
|
},
|
|
@@ -138,9 +136,9 @@ export const styles = StyleSheet.create({
|
|
|
138
136
|
marginRight: Spacing.M,
|
|
139
137
|
},
|
|
140
138
|
toolkitWide: {
|
|
141
|
-
width:
|
|
139
|
+
width: 65,
|
|
142
140
|
},
|
|
143
141
|
toolkitNarrow: {
|
|
144
|
-
width:
|
|
142
|
+
width: 28,
|
|
145
143
|
},
|
|
146
144
|
});
|
|
@@ -3,9 +3,6 @@ import { MiniAppContext } from '../../Context';
|
|
|
3
3
|
import { Spacing } from '../../Consts';
|
|
4
4
|
import { isWhiteListDesignSystem } from '../utils';
|
|
5
5
|
import { useScreenLayout } from '../../Layout';
|
|
6
|
-
import { HeaderBarMetrics } from '../headerLayout';
|
|
7
|
-
|
|
8
|
-
const { sidePadding, buttonSize, titleGap, clusterWidth } = HeaderBarMetrics;
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* Header title font: MoMoTrustDisplay for design-system whitelisted mini apps,
|
|
@@ -17,28 +14,32 @@ const useHeaderFontFamily = (): string => {
|
|
|
17
14
|
};
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
|
-
*
|
|
21
|
-
|
|
17
|
+
* back button and toolkit sharing the navigation bar with the title
|
|
18
|
+
*/
|
|
19
|
+
const HEADER_ACTIONS_WIDTH = 172;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* extra width the 48pt back and toolkit take on the iPhone Duo status bar row
|
|
22
23
|
*/
|
|
23
|
-
const
|
|
24
|
-
sidePadding * 2 + buttonSize + titleGap * 2 + clusterWidth;
|
|
24
|
+
const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
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
|
|
29
|
-
* them, minus the status bar clusters on the iPhone Duo inner portrait row.
|
|
28
|
+
* the bar, so the title gets the content width; otherwise it shares the bar with them.
|
|
30
29
|
*/
|
|
31
30
|
const useTitleMaxWidth = () => {
|
|
32
31
|
const layout = useScreenLayout();
|
|
33
32
|
const { width, contentWidth, sideBar, statusBarClearance } = layout;
|
|
34
33
|
let clearance = 0;
|
|
34
|
+
let rowActionsExtra = 0;
|
|
35
35
|
if (statusBarClearance) {
|
|
36
36
|
clearance = statusBarClearance.left + statusBarClearance.right;
|
|
37
|
+
rowActionsExtra = STATUS_BAR_ROW_ACTIONS_EXTRA;
|
|
37
38
|
}
|
|
38
39
|
if (sideBar) {
|
|
39
40
|
return contentWidth - Spacing.M * 2 - clearance;
|
|
40
41
|
}
|
|
41
|
-
return width - HEADER_ACTIONS_WIDTH - clearance;
|
|
42
|
+
return width - HEADER_ACTIONS_WIDTH - rowActionsExtra - clearance;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
export { useHeaderFontFamily, HEADER_ACTIONS_WIDTH, useTitleMaxWidth };
|
|
@@ -4,15 +4,18 @@ import { NavigationButton } from './NavigationButton';
|
|
|
4
4
|
import { MiniAppContext } from '../../Context';
|
|
5
5
|
import { Colors, Spacing, Styles } from '../../Consts';
|
|
6
6
|
import { Icon } from '../../Icon';
|
|
7
|
-
import { DuoMetrics } from '../../Layout/utils';
|
|
7
|
+
import { DuoMetrics, useScreenLayout } from '../../Layout/utils';
|
|
8
8
|
import { SideBarButton, SideBarGroup, SideBarGroupItem } from './SideBarButton';
|
|
9
9
|
import { useDuoBarCompression } from '../BottomTab/DuoBarCompression';
|
|
10
10
|
import { styles } from './HeaderRight.styles';
|
|
11
11
|
import { useToolkitActions } from './useToolkitActions';
|
|
12
|
-
import { HeaderBarMetrics } from '../headerLayout';
|
|
13
12
|
|
|
14
13
|
const CLOSE_ICON = '16_navigation_close_circle';
|
|
15
14
|
const HOME_ICON = '16_basic_home';
|
|
15
|
+
const TOOLKIT_ICON_SIZE = 20;
|
|
16
|
+
// the capsule's two buttons extend their touch area outward only
|
|
17
|
+
const HELP_CENTER_HIT_SLOP = { top: 7, bottom: 7, left: 7, right: 0 };
|
|
18
|
+
const CLOSE_HIT_SLOP = { top: 7, bottom: 7, left: 0, right: 7 };
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Header toolkit: optional shortcut plus the help center / close capsule. When
|
|
@@ -32,13 +35,14 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
32
35
|
const compression = useDuoBarCompression();
|
|
33
36
|
const { toolbarCompressed, toolbarHidden } = compression;
|
|
34
37
|
const { expandToolbar, setToolbarHeight } = compression;
|
|
38
|
+
const { statusBarClearance } = useScreenLayout();
|
|
35
39
|
const {
|
|
36
40
|
isLoading,
|
|
37
41
|
iconShortcut,
|
|
38
42
|
shortcutOnPress,
|
|
39
43
|
showBadge,
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
handlePressHelpCenter,
|
|
45
|
+
handleClose,
|
|
42
46
|
} = useToolkitActions({
|
|
43
47
|
preventClose,
|
|
44
48
|
useSystemTools,
|
|
@@ -56,14 +60,14 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
56
60
|
};
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
const
|
|
63
|
+
const hasHelpCenter = context.appId !== 'vn.momo.helpcenter';
|
|
60
64
|
const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
|
|
61
65
|
let toolkitWidthStyle: ViewStyle = styles.toolkitNarrow;
|
|
62
|
-
if (
|
|
66
|
+
if (hasHelpCenter) {
|
|
63
67
|
toolkitWidthStyle = styles.toolkitWide;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
const groupCount =
|
|
70
|
+
const groupCount = hasHelpCenter ? 2 : 1;
|
|
67
71
|
let capsuleHeight = DuoMetrics.controlSize;
|
|
68
72
|
if (groupCount > 1) {
|
|
69
73
|
capsuleHeight = groupCount * DuoMetrics.itemPitch;
|
|
@@ -112,19 +116,52 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
112
116
|
/>
|
|
113
117
|
)}
|
|
114
118
|
<SideBarGroup tintColor={tintColor}>
|
|
115
|
-
{
|
|
119
|
+
{hasHelpCenter && (
|
|
116
120
|
<SideBarGroupItem
|
|
117
121
|
accessibilityLabel={'btn_navigation_help_center'}
|
|
118
122
|
icon={'help_center'}
|
|
119
123
|
tintColor={tintColor}
|
|
120
|
-
onPress={
|
|
124
|
+
onPress={handlePressHelpCenter}
|
|
121
125
|
/>
|
|
122
126
|
)}
|
|
123
127
|
<SideBarGroupItem
|
|
124
128
|
accessibilityLabel={'btn_navigation_close'}
|
|
125
129
|
icon={closeIcon}
|
|
126
130
|
tintColor={tintColor}
|
|
127
|
-
onPress={
|
|
131
|
+
onPress={handleClose}
|
|
132
|
+
/>
|
|
133
|
+
</SideBarGroup>
|
|
134
|
+
</View>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// on the status bar row the controls keep the side bar size
|
|
139
|
+
if (statusBarClearance) {
|
|
140
|
+
return (
|
|
141
|
+
<View style={styles.sideBarRow}>
|
|
142
|
+
{useShortcut && (
|
|
143
|
+
<SideBarButton
|
|
144
|
+
disabled={isLoading}
|
|
145
|
+
icon={iconShortcut}
|
|
146
|
+
onPress={shortcutOnPress}
|
|
147
|
+
tintColor={tintColor}
|
|
148
|
+
showBadge={showBadge}
|
|
149
|
+
/>
|
|
150
|
+
)}
|
|
151
|
+
<SideBarGroup tintColor={tintColor} horizontal>
|
|
152
|
+
{hasHelpCenter && (
|
|
153
|
+
<SideBarGroupItem
|
|
154
|
+
accessibilityLabel={'btn_navigation_help_center'}
|
|
155
|
+
icon={'help_center'}
|
|
156
|
+
tintColor={tintColor}
|
|
157
|
+
onPress={handlePressHelpCenter}
|
|
158
|
+
/>
|
|
159
|
+
)}
|
|
160
|
+
<SideBarGroupItem
|
|
161
|
+
accessibilityLabel={'btn_navigation_close'}
|
|
162
|
+
icon={closeIcon}
|
|
163
|
+
tintColor={tintColor}
|
|
164
|
+
onPress={handleClose}
|
|
128
165
|
/>
|
|
129
166
|
</SideBarGroup>
|
|
130
167
|
</View>
|
|
@@ -143,18 +180,18 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
143
180
|
/>
|
|
144
181
|
)}
|
|
145
182
|
<View style={[styles.toolkitContainer, buttonStyle, toolkitWidthStyle]}>
|
|
146
|
-
{
|
|
183
|
+
{hasHelpCenter && (
|
|
147
184
|
<>
|
|
148
185
|
<TouchableOpacity
|
|
149
186
|
accessibilityLabel={'btn_navigation_help_center'}
|
|
150
187
|
style={styles.toolkitButton}
|
|
151
|
-
onPress={
|
|
152
|
-
hitSlop={
|
|
188
|
+
onPress={handlePressHelpCenter}
|
|
189
|
+
hitSlop={HELP_CENTER_HIT_SLOP}
|
|
153
190
|
>
|
|
154
191
|
<Icon
|
|
155
192
|
color={tintColor}
|
|
156
193
|
source={'help_center'}
|
|
157
|
-
size={
|
|
194
|
+
size={TOOLKIT_ICON_SIZE}
|
|
158
195
|
/>
|
|
159
196
|
</TouchableOpacity>
|
|
160
197
|
<View style={[styles.divider, { backgroundColor: tintColor }]} />
|
|
@@ -162,15 +199,11 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
162
199
|
)}
|
|
163
200
|
<TouchableOpacity
|
|
164
201
|
accessibilityLabel={'btn_navigation_close'}
|
|
165
|
-
onPress={
|
|
202
|
+
onPress={handleClose}
|
|
166
203
|
style={styles.toolkitButton}
|
|
167
|
-
hitSlop={
|
|
204
|
+
hitSlop={CLOSE_HIT_SLOP}
|
|
168
205
|
>
|
|
169
|
-
<Icon
|
|
170
|
-
color={tintColor}
|
|
171
|
-
source={closeIcon}
|
|
172
|
-
size={HeaderBarMetrics.iconSize}
|
|
173
|
-
/>
|
|
206
|
+
<Icon color={tintColor} source={closeIcon} size={TOOLKIT_ICON_SIZE} />
|
|
174
207
|
</TouchableOpacity>
|
|
175
208
|
</View>
|
|
176
209
|
</View>
|
|
@@ -5,12 +5,9 @@ import { StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native';
|
|
|
5
5
|
import { Colors, Spacing } from '../../Consts';
|
|
6
6
|
import { Icon } from '../../Icon';
|
|
7
7
|
import { BadgeDot } from '../../Badge';
|
|
8
|
-
import { HeaderBarMetrics } from '../headerLayout';
|
|
9
|
-
|
|
10
|
-
const { buttonSize, iconSize } = HeaderBarMetrics;
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
|
-
* Round
|
|
10
|
+
* Round 28pt icon button of the header (back, shortcut) with an optional badge
|
|
14
11
|
* dot; switches to a dark fill under a white tint so the icon stays readable.
|
|
15
12
|
*/
|
|
16
13
|
const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
@@ -38,7 +35,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
38
35
|
<Icon
|
|
39
36
|
source={icon}
|
|
40
37
|
color={tintColor ?? theme.colors.text.default}
|
|
41
|
-
size={
|
|
38
|
+
size={20}
|
|
42
39
|
/>
|
|
43
40
|
</TouchableOpacity>
|
|
44
41
|
{showBadge && <BadgeDot size={'small'} style={styles.badgeDot} />}
|
|
@@ -48,12 +45,12 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
48
45
|
|
|
49
46
|
const styles = StyleSheet.create({
|
|
50
47
|
container: {
|
|
51
|
-
width:
|
|
48
|
+
width: 28,
|
|
52
49
|
},
|
|
53
50
|
navigationButton: {
|
|
54
|
-
height:
|
|
55
|
-
width:
|
|
56
|
-
borderRadius:
|
|
51
|
+
height: 28,
|
|
52
|
+
width: 28,
|
|
53
|
+
borderRadius: 14,
|
|
57
54
|
justifyContent: 'center',
|
|
58
55
|
alignItems: 'center',
|
|
59
56
|
backgroundColor: Colors.black_01 + '99',
|