@momo-kits/foundation 0.167.1-beta.13 → 0.167.1-beta.14
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 +246 -42
- package/Application/BottomTab/BottomTabBar.tsx +6 -3
- package/Application/BottomTab/CustomBottomTabItem.tsx +3 -5
- package/Application/BottomTab/DuoBarCompression.tsx +135 -0
- package/Application/BottomTab/DuoTabBar.tsx +537 -0
- package/Application/BottomTab/index.tsx +145 -134
- package/Application/Components/DuoGlassBackground.tsx +158 -0
- package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
- package/Application/Components/HeaderAnimated.tsx +9 -3
- package/Application/Components/HeaderExtendHeader.tsx +70 -28
- package/Application/Components/HeaderLeft.tsx +38 -0
- package/Application/Components/HeaderRight.tsx +144 -9
- package/Application/Components/HeaderTitle.tsx +43 -8
- package/Application/Components/SearchHeader.tsx +20 -15
- package/Application/Components/SideBarButton.tsx +134 -0
- package/Application/ModalScreen.tsx +20 -2
- package/Application/NavigationContainer.tsx +22 -38
- package/Application/Navigator.ts +1 -3
- package/Application/StackScreen.tsx +2 -5
- package/Application/device.ts +52 -0
- package/Application/types.ts +10 -1
- package/Application/utils.tsx +10 -7
- package/Badge/Badge.tsx +2 -3
- package/Badge/BadgeDotAnimation.tsx +3 -3
- package/Button/index.tsx +11 -11
- package/Consts/theme.ts +3 -4
- package/Input/Input.tsx +3 -4
- package/Input/InputMoney.tsx +123 -195
- package/Input/InputOTP.tsx +9 -12
- package/Input/InputPhoneNumber.tsx +3 -7
- package/Input/InputSearch.tsx +17 -14
- package/Input/InputTextArea.tsx +3 -4
- package/Input/SystemTextInput.tsx +2 -3
- package/Input/TextTyping.tsx +11 -6
- package/Input/styles.ts +9 -0
- package/Input/utils.ts +2 -3
- package/Layout/Card.tsx +8 -26
- package/Layout/FloatingButton.tsx +3 -4
- package/Layout/Item.tsx +1 -10
- package/Layout/ReservedRegions.tsx +78 -0
- package/Layout/Screen.tsx +242 -48
- package/Layout/Section.tsx +5 -18
- package/Layout/index.ts +4 -4
- package/Layout/styles.ts +5 -0
- package/Layout/utils.ts +478 -5
- package/Pagination/Dot.tsx +3 -6
- package/Popup/PopupNotify.tsx +49 -28
- package/Popup/PopupPromotion.tsx +21 -8
- package/Popup/utils.ts +26 -0
- package/Radio/index.tsx +2 -3
- package/Text/index.tsx +4 -0
- package/Text/utils.ts +2 -3
- package/Title/index.tsx +8 -11
- package/Title/styles.ts +0 -1
- package/package.json +10 -7
- package/Layout/GridSystem.tsx +0 -137
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { Ref, useContext } from 'react';
|
|
2
2
|
import LinearGradient from 'react-native-linear-gradient';
|
|
3
|
-
import {
|
|
3
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
4
4
|
import Animated, {
|
|
5
5
|
Extrapolation,
|
|
6
6
|
interpolate,
|
|
@@ -14,21 +14,19 @@ import { Colors, Radius, Spacing } from '../../Consts';
|
|
|
14
14
|
import { Image } from '../../Image';
|
|
15
15
|
import { SearchHeaderProps } from '../types';
|
|
16
16
|
import BackgroundImageView from './BackgroundImageView';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from '../utils';
|
|
17
|
+
import { AnimatedCompatValue, useNormalizedSharedValue } from '../utils';
|
|
18
|
+
import { useScreenLayout } from '../../Layout';
|
|
19
|
+
import { DuoMetrics, HeaderMetrics } from '../../Layout/utils';
|
|
21
20
|
|
|
22
21
|
const SCREEN_PADDING = 12;
|
|
23
22
|
const BACK_WIDTH = 28;
|
|
24
|
-
|
|
25
|
-
const { width: screenWidth } = Dimensions.get('window');
|
|
23
|
+
const TOOLKIT_WIDTH = 73;
|
|
26
24
|
|
|
27
25
|
const HeaderExtendHeader: React.FC<{
|
|
28
26
|
headerType?: HeaderType;
|
|
29
27
|
animatedValue: AnimatedCompatValue;
|
|
30
28
|
heightHeader: number;
|
|
31
|
-
headerRightWidth
|
|
29
|
+
headerRightWidth?: number;
|
|
32
30
|
inputSearchProps?: SearchHeaderProps;
|
|
33
31
|
inputSearchRef?: Ref<InputRef>;
|
|
34
32
|
navigation?: Navigation;
|
|
@@ -39,7 +37,7 @@ const HeaderExtendHeader: React.FC<{
|
|
|
39
37
|
headerType = 'default',
|
|
40
38
|
animatedValue,
|
|
41
39
|
heightHeader,
|
|
42
|
-
headerRightWidth
|
|
40
|
+
headerRightWidth: customHeaderRightWidth,
|
|
43
41
|
inputSearchProps,
|
|
44
42
|
inputSearchRef,
|
|
45
43
|
useShadowHeader: useShadowHeaderProp = true,
|
|
@@ -51,7 +49,32 @@ const HeaderExtendHeader: React.FC<{
|
|
|
51
49
|
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
52
50
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
53
51
|
const headerBackground = customBackground ?? theme.assets?.headerBackground;
|
|
54
|
-
const
|
|
52
|
+
const layout = useScreenLayout();
|
|
53
|
+
|
|
54
|
+
// the iPhone Duo status bar row keeps side bar sized back and toolkit
|
|
55
|
+
const isStatusBarRow = !!layout.statusBarClearance;
|
|
56
|
+
const duoToolkitWidth = DuoMetrics.itemPitch * 2 + SCREEN_PADDING;
|
|
57
|
+
const backWidth = isStatusBarRow ? DuoMetrics.controlSize : BACK_WIDTH;
|
|
58
|
+
const toolkitWidth = isStatusBarRow ? duoToolkitWidth : TOOLKIT_WIDTH;
|
|
59
|
+
const defaultLeft = layout.sideBar ? SCREEN_PADDING : backWidth + 20;
|
|
60
|
+
const leftPosition = inputSearchProps?.leftPosition || defaultLeft;
|
|
61
|
+
const defaultRightWidth = layout.sideBar ? 0 : toolkitWidth;
|
|
62
|
+
const headerRightWidth = customHeaderRightWidth ?? defaultRightWidth;
|
|
63
|
+
const contentLeft = layout.insetLeft + SCREEN_PADDING;
|
|
64
|
+
const contentRight = layout.insetLeft + layout.contentWidth;
|
|
65
|
+
// collapsed on the status bar row, the search stays between its clusters
|
|
66
|
+
const collapsedLeft =
|
|
67
|
+
layout.insetLeft + leftPosition + (layout.statusBarClearance?.left ?? 0);
|
|
68
|
+
const collapsedRight = contentRight - (layout.statusBarClearance?.right ?? 0);
|
|
69
|
+
const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
|
|
70
|
+
const collapsedSearchWidth =
|
|
71
|
+
collapsedRight - collapsedLeft - SCREEN_PADDING - headerRightWidth;
|
|
72
|
+
// a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
|
|
73
|
+
const collapsedRaise =
|
|
74
|
+
(layout.headerHeight -
|
|
75
|
+
layout.headerStatusBarHeight -
|
|
76
|
+
HeaderMetrics.barHeight) /
|
|
77
|
+
2;
|
|
55
78
|
|
|
56
79
|
let useShadowHeader = useShadowHeaderProp;
|
|
57
80
|
if (inputSearchProps && Platform.OS === 'android') {
|
|
@@ -70,7 +93,12 @@ const HeaderExtendHeader: React.FC<{
|
|
|
70
93
|
}));
|
|
71
94
|
|
|
72
95
|
const gradientOpacityStyle = useAnimatedStyle(() => ({
|
|
73
|
-
opacity: interpolate(
|
|
96
|
+
opacity: interpolate(
|
|
97
|
+
sharedValue.value,
|
|
98
|
+
[0, 52],
|
|
99
|
+
[1, 0],
|
|
100
|
+
Extrapolation.CLAMP,
|
|
101
|
+
),
|
|
74
102
|
}));
|
|
75
103
|
|
|
76
104
|
const searchTranslateStyle = useAnimatedStyle(() => ({
|
|
@@ -79,7 +107,15 @@ const HeaderExtendHeader: React.FC<{
|
|
|
79
107
|
translateX: interpolate(
|
|
80
108
|
sharedValue.value,
|
|
81
109
|
[0, 100],
|
|
82
|
-
[
|
|
110
|
+
[contentLeft, collapsedLeft],
|
|
111
|
+
Extrapolation.CLAMP,
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
translateY: interpolate(
|
|
116
|
+
sharedValue.value,
|
|
117
|
+
[0, 100],
|
|
118
|
+
[0, -collapsedRaise],
|
|
83
119
|
Extrapolation.CLAMP,
|
|
84
120
|
),
|
|
85
121
|
},
|
|
@@ -87,19 +123,17 @@ const HeaderExtendHeader: React.FC<{
|
|
|
87
123
|
width: interpolate(
|
|
88
124
|
sharedValue.value,
|
|
89
125
|
[0, 100],
|
|
90
|
-
[
|
|
91
|
-
screenWidth - SCREEN_PADDING * 2,
|
|
92
|
-
screenWidth - leftPosition - 12 - headerRightWidth,
|
|
93
|
-
],
|
|
126
|
+
[searchWidth, collapsedSearchWidth],
|
|
94
127
|
Extrapolation.CLAMP,
|
|
95
128
|
),
|
|
96
129
|
}));
|
|
97
130
|
|
|
98
131
|
const searchBackgroundStyle = useAnimatedStyle(() => {
|
|
99
132
|
const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
:
|
|
133
|
+
const { background } = theme.colors;
|
|
134
|
+
return {
|
|
135
|
+
backgroundColor: t === 0 ? background.surface : background.default,
|
|
136
|
+
};
|
|
103
137
|
});
|
|
104
138
|
|
|
105
139
|
const extendedHeightStyle = useAnimatedStyle(() => ({
|
|
@@ -124,14 +158,17 @@ const HeaderExtendHeader: React.FC<{
|
|
|
124
158
|
<Animated.View
|
|
125
159
|
style={[
|
|
126
160
|
styles.headerBox,
|
|
127
|
-
headerType === 'extended'
|
|
128
|
-
? heightStyle
|
|
129
|
-
: { height: heightHeader },
|
|
161
|
+
headerType === 'extended' ? heightStyle : { height: heightHeader },
|
|
130
162
|
]}
|
|
131
163
|
>
|
|
132
164
|
{!!gradientColor && (
|
|
133
165
|
<Animated.View
|
|
134
|
-
style={[
|
|
166
|
+
style={[
|
|
167
|
+
styles.extendedHeader,
|
|
168
|
+
// fade out within the header box, as DuoScrollGradient does
|
|
169
|
+
!!layout.sideBar && styles.fillHeaderBox,
|
|
170
|
+
gradientOpacityStyle,
|
|
171
|
+
]}
|
|
135
172
|
>
|
|
136
173
|
<LinearGradient
|
|
137
174
|
colors={[gradientColor, gradientColor + '00']}
|
|
@@ -162,10 +199,7 @@ const HeaderExtendHeader: React.FC<{
|
|
|
162
199
|
style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
|
|
163
200
|
>
|
|
164
201
|
<Animated.View
|
|
165
|
-
style={[
|
|
166
|
-
{ borderRadius: Radius.XL },
|
|
167
|
-
searchBackgroundStyle,
|
|
168
|
-
]}
|
|
202
|
+
style={[{ borderRadius: Radius.XL }, searchBackgroundStyle]}
|
|
169
203
|
>
|
|
170
204
|
<InputSearch
|
|
171
205
|
{...inputSearchProps}
|
|
@@ -191,7 +225,11 @@ const HeaderExtendHeader: React.FC<{
|
|
|
191
225
|
/>
|
|
192
226
|
{!!gradientColor && (
|
|
193
227
|
<Animated.View
|
|
194
|
-
style={[
|
|
228
|
+
style={[
|
|
229
|
+
styles.extendedHeader,
|
|
230
|
+
gradientOpacityStyle,
|
|
231
|
+
extendedHeightStyle,
|
|
232
|
+
]}
|
|
195
233
|
>
|
|
196
234
|
<LinearGradient
|
|
197
235
|
colors={[gradientColor, gradientColor + '00']}
|
|
@@ -227,6 +265,10 @@ const styles = StyleSheet.create({
|
|
|
227
265
|
position: 'absolute',
|
|
228
266
|
width: '100%',
|
|
229
267
|
},
|
|
268
|
+
fillHeaderBox: {
|
|
269
|
+
aspectRatio: undefined,
|
|
270
|
+
height: '100%',
|
|
271
|
+
},
|
|
230
272
|
headerBox: {
|
|
231
273
|
width: '100%',
|
|
232
274
|
position: 'absolute',
|
|
@@ -4,6 +4,10 @@ import React, { useCallback, useContext, useEffect } from 'react';
|
|
|
4
4
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
5
5
|
import { PopupNotify } from '../../Popup';
|
|
6
6
|
import { NavigationButton } from './NavigationButton';
|
|
7
|
+
import { SideBarButton } from './SideBarButton';
|
|
8
|
+
import Animated from 'react-native-reanimated';
|
|
9
|
+
import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
|
|
10
|
+
import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* default header left used for nav
|
|
@@ -13,9 +17,13 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
13
17
|
preventBack,
|
|
14
18
|
onPressLeftHeader,
|
|
15
19
|
onBackHandler,
|
|
20
|
+
floating = false,
|
|
16
21
|
}) => {
|
|
17
22
|
const context = useContext<any>(MiniAppContext);
|
|
18
23
|
const { navigator } = useContext(ApplicationContext);
|
|
24
|
+
const sideBarStyle = useSideBarItemStyle('left', 0);
|
|
25
|
+
const counterStyle = useSideBarCounterStyle();
|
|
26
|
+
const { statusBarClearance } = useScreenLayout();
|
|
19
27
|
|
|
20
28
|
const goBack = useCallback(() => {
|
|
21
29
|
const canGoBack = navigator?.ref?.current?.canGoBack?.();
|
|
@@ -72,6 +80,36 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
72
80
|
return () => backHandler.remove();
|
|
73
81
|
}, [goBackSafe]);
|
|
74
82
|
|
|
83
|
+
if (floating && sideBarStyle) {
|
|
84
|
+
return (
|
|
85
|
+
<Animated.View
|
|
86
|
+
style={[sideBarStyle, counterStyle]}
|
|
87
|
+
pointerEvents={'box-none'}
|
|
88
|
+
>
|
|
89
|
+
<SideBarButton
|
|
90
|
+
accessibilityLabel="btn_navigation_back"
|
|
91
|
+
icon="arrow-back"
|
|
92
|
+
tintColor={tintColor}
|
|
93
|
+
onPress={goBackSafe}
|
|
94
|
+
/>
|
|
95
|
+
</Animated.View>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// on the status bar row the controls keep the side bar size
|
|
100
|
+
if (statusBarClearance) {
|
|
101
|
+
return (
|
|
102
|
+
<View style={styles.headerLeft}>
|
|
103
|
+
<SideBarButton
|
|
104
|
+
accessibilityLabel="btn_navigation_back"
|
|
105
|
+
icon="arrow-back"
|
|
106
|
+
tintColor={tintColor}
|
|
107
|
+
onPress={goBackSafe}
|
|
108
|
+
/>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
75
113
|
return (
|
|
76
114
|
<View style={styles.headerLeft}>
|
|
77
115
|
<NavigationButton
|
|
@@ -13,16 +13,26 @@ import { PopupNotify } from '../../Popup';
|
|
|
13
13
|
import { Tool, ToolGroup } from '../types';
|
|
14
14
|
import { Icon } from '../../Icon';
|
|
15
15
|
import { Text } from '../../Text';
|
|
16
|
+
import Animated from 'react-native-reanimated';
|
|
17
|
+
import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
|
|
18
|
+
import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
|
|
19
|
+
import { SideBarButton, SideBarGroup, SideBarGroupItem } from './SideBarButton';
|
|
20
|
+
import { useDuoBarCompression } from '../BottomTab/DuoBarCompression';
|
|
21
|
+
import { DuoMetrics } from '../../Layout/utils';
|
|
16
22
|
|
|
17
23
|
const DID_SYNC_NEW_HOME = 'did_sync_new_home';
|
|
24
|
+
const CLOSE_ICON = '16_navigation_close_circle';
|
|
25
|
+
const HOME_ICON = '16_basic_home';
|
|
18
26
|
|
|
19
27
|
/**
|
|
20
28
|
* main component for header right
|
|
21
29
|
*/
|
|
22
30
|
const HeaderRight: React.FC<any> = props => {
|
|
23
31
|
const { translate } = useContext(ApplicationContext);
|
|
24
|
-
const { headerRight = {} } = props;
|
|
32
|
+
const { headerRight = {}, floating = false } = props;
|
|
25
33
|
const { useOnBoarding = false, buttonOnBoarding, onPress } = headerRight;
|
|
34
|
+
const sideBarStyle = useSideBarItemStyle('right', 1);
|
|
35
|
+
const counterStyle = useSideBarCounterStyle();
|
|
26
36
|
|
|
27
37
|
if (typeof props.headerRight === 'function') {
|
|
28
38
|
return props.headerRight(props);
|
|
@@ -38,6 +48,21 @@ const HeaderRight: React.FC<any> = props => {
|
|
|
38
48
|
);
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
if (floating && sideBarStyle) {
|
|
52
|
+
return (
|
|
53
|
+
<Animated.View
|
|
54
|
+
style={[sideBarStyle, counterStyle]}
|
|
55
|
+
pointerEvents={'box-none'}
|
|
56
|
+
>
|
|
57
|
+
<HeaderToolkitAction
|
|
58
|
+
{...props.headerRight}
|
|
59
|
+
tintColor={props.tintColor}
|
|
60
|
+
vertical
|
|
61
|
+
/>
|
|
62
|
+
</Animated.View>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
41
66
|
return (
|
|
42
67
|
<View style={styles.headerRightButton}>
|
|
43
68
|
<HeaderToolkitAction {...props.headerRight} tintColor={props.tintColor} />
|
|
@@ -56,9 +81,13 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
56
81
|
useMore = false,
|
|
57
82
|
tools = [],
|
|
58
83
|
useCloseIcon = false,
|
|
84
|
+
vertical = false,
|
|
59
85
|
}) => {
|
|
60
86
|
const { navigator } = useContext(ApplicationContext);
|
|
61
87
|
const context = useContext<any>(MiniAppContext);
|
|
88
|
+
const { toolbarCompressed, toolbarHidden, expandToolbar, setToolbarHeight } =
|
|
89
|
+
useDuoBarCompression();
|
|
90
|
+
const { statusBarClearance } = useScreenLayout();
|
|
62
91
|
|
|
63
92
|
const [isFavorite, setIsFavorite] = useState<boolean>(false);
|
|
64
93
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -260,6 +289,111 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
260
289
|
group?.items?.some?.(tool => tool?.showBadge),
|
|
261
290
|
);
|
|
262
291
|
|
|
292
|
+
const useHelpCenter = context.appId !== 'vn.momo.helpcenter';
|
|
293
|
+
const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
|
|
294
|
+
|
|
295
|
+
const groupCount = useHelpCenter ? 2 : 1;
|
|
296
|
+
let capsuleHeight = DuoMetrics.controlSize;
|
|
297
|
+
if (groupCount > 1) {
|
|
298
|
+
capsuleHeight = groupCount * DuoMetrics.itemPitch;
|
|
299
|
+
}
|
|
300
|
+
// Back slot, the optional shortcut, then the help/home capsule
|
|
301
|
+
const fullToolbarHeight =
|
|
302
|
+
DuoMetrics.controlSize +
|
|
303
|
+
Spacing.M +
|
|
304
|
+
(useShortcut ? DuoMetrics.controlSize + Spacing.M : 0) +
|
|
305
|
+
capsuleHeight;
|
|
306
|
+
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
if (vertical) {
|
|
309
|
+
setToolbarHeight?.(fullToolbarHeight);
|
|
310
|
+
}
|
|
311
|
+
}, [vertical, fullToolbarHeight, setToolbarHeight]);
|
|
312
|
+
|
|
313
|
+
// an open tab rail takes this slot for a moment
|
|
314
|
+
if (vertical && toolbarHidden) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// HIG "Toolbar compressed": tools fold into one button; tapping it expands the
|
|
319
|
+
// toolbar and compresses the tab bar instead. Not the useMore icon, to tell them apart
|
|
320
|
+
if (vertical && toolbarCompressed) {
|
|
321
|
+
return (
|
|
322
|
+
<SideBarButton
|
|
323
|
+
accessibilityLabel={'btn_navigation_toolbar'}
|
|
324
|
+
icon={'navigation_menu'}
|
|
325
|
+
tintColor={tintColor}
|
|
326
|
+
showBadge={showBadge}
|
|
327
|
+
onPress={expandToolbar}
|
|
328
|
+
/>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (vertical) {
|
|
333
|
+
return (
|
|
334
|
+
<View style={styles.sideBarColumn}>
|
|
335
|
+
{useShortcut && (
|
|
336
|
+
<SideBarButton
|
|
337
|
+
disabled={isLoading}
|
|
338
|
+
icon={iconShortcut}
|
|
339
|
+
onPress={shortcutOnPress}
|
|
340
|
+
tintColor={tintColor}
|
|
341
|
+
showBadge={showBadge}
|
|
342
|
+
/>
|
|
343
|
+
)}
|
|
344
|
+
<SideBarGroup tintColor={tintColor}>
|
|
345
|
+
{useHelpCenter && (
|
|
346
|
+
<SideBarGroupItem
|
|
347
|
+
accessibilityLabel={'btn_navigation_help_center'}
|
|
348
|
+
icon={'help_center'}
|
|
349
|
+
tintColor={tintColor}
|
|
350
|
+
onPress={onPressHelpCenter}
|
|
351
|
+
/>
|
|
352
|
+
)}
|
|
353
|
+
<SideBarGroupItem
|
|
354
|
+
accessibilityLabel={'btn_navigation_close'}
|
|
355
|
+
icon={closeIcon}
|
|
356
|
+
tintColor={tintColor}
|
|
357
|
+
onPress={onClose}
|
|
358
|
+
/>
|
|
359
|
+
</SideBarGroup>
|
|
360
|
+
</View>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// on the status bar row the controls keep the side bar size
|
|
365
|
+
if (statusBarClearance) {
|
|
366
|
+
return (
|
|
367
|
+
<View style={styles.sideBarRow}>
|
|
368
|
+
{useShortcut && (
|
|
369
|
+
<SideBarButton
|
|
370
|
+
disabled={isLoading}
|
|
371
|
+
icon={iconShortcut}
|
|
372
|
+
onPress={shortcutOnPress}
|
|
373
|
+
tintColor={tintColor}
|
|
374
|
+
showBadge={showBadge}
|
|
375
|
+
/>
|
|
376
|
+
)}
|
|
377
|
+
<SideBarGroup tintColor={tintColor} horizontal>
|
|
378
|
+
{useHelpCenter && (
|
|
379
|
+
<SideBarGroupItem
|
|
380
|
+
accessibilityLabel={'btn_navigation_help_center'}
|
|
381
|
+
icon={'help_center'}
|
|
382
|
+
tintColor={tintColor}
|
|
383
|
+
onPress={onPressHelpCenter}
|
|
384
|
+
/>
|
|
385
|
+
)}
|
|
386
|
+
<SideBarGroupItem
|
|
387
|
+
accessibilityLabel={'btn_navigation_close'}
|
|
388
|
+
icon={closeIcon}
|
|
389
|
+
tintColor={tintColor}
|
|
390
|
+
onPress={onClose}
|
|
391
|
+
/>
|
|
392
|
+
</SideBarGroup>
|
|
393
|
+
</View>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
263
397
|
return (
|
|
264
398
|
<View style={Styles.row}>
|
|
265
399
|
{useShortcut && (
|
|
@@ -280,7 +414,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
280
414
|
},
|
|
281
415
|
]}
|
|
282
416
|
>
|
|
283
|
-
{
|
|
417
|
+
{useHelpCenter && (
|
|
284
418
|
<>
|
|
285
419
|
<TouchableOpacity
|
|
286
420
|
accessibilityLabel={'btn_navigation_help_center'}
|
|
@@ -299,13 +433,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
299
433
|
style={styles.toolkitButton}
|
|
300
434
|
hitSlop={{ top: 7, bottom: 7, left: 0, right: 7 }}
|
|
301
435
|
>
|
|
302
|
-
<Icon
|
|
303
|
-
color={tintColor}
|
|
304
|
-
source={
|
|
305
|
-
useCloseIcon ? '16_navigation_close_circle' : '16_basic_home'
|
|
306
|
-
}
|
|
307
|
-
size={20}
|
|
308
|
-
/>
|
|
436
|
+
<Icon color={tintColor} source={closeIcon} size={20} />
|
|
309
437
|
</TouchableOpacity>
|
|
310
438
|
</View>
|
|
311
439
|
</View>
|
|
@@ -376,6 +504,13 @@ const styles = StyleSheet.create({
|
|
|
376
504
|
width: 0.5,
|
|
377
505
|
height: 12,
|
|
378
506
|
},
|
|
507
|
+
sideBarColumn: {
|
|
508
|
+
gap: Spacing.M,
|
|
509
|
+
},
|
|
510
|
+
sideBarRow: {
|
|
511
|
+
flexDirection: 'row',
|
|
512
|
+
gap: Spacing.M,
|
|
513
|
+
},
|
|
379
514
|
headerLeft: {
|
|
380
515
|
marginLeft: 12,
|
|
381
516
|
},
|
|
@@ -21,12 +21,44 @@ import {
|
|
|
21
21
|
isWhiteListDesignSystem,
|
|
22
22
|
useNormalizedSharedValue,
|
|
23
23
|
} from '../utils';
|
|
24
|
+
import { useScreenLayout } from '../../Layout';
|
|
24
25
|
|
|
25
26
|
const useHeaderFontFamily = (): string => {
|
|
26
27
|
const context = useContext<any>(MiniAppContext);
|
|
27
28
|
return isWhiteListDesignSystem(context) ? 'MoMoTrustDisplay' : 'SFProText';
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* back button and toolkit sharing the navigation bar with the title
|
|
33
|
+
*/
|
|
34
|
+
const HEADER_ACTIONS_WIDTH = 172;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* extra width the 48pt back and toolkit take on the iPhone Duo status bar row
|
|
38
|
+
*/
|
|
39
|
+
const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* max title width, the iPhone Duo side bar takes back and toolkit out of the bar
|
|
43
|
+
*/
|
|
44
|
+
const useTitleMaxWidth = () => {
|
|
45
|
+
const { width, contentWidth, sideBar, statusBarClearance } =
|
|
46
|
+
useScreenLayout();
|
|
47
|
+
let clearance = 0;
|
|
48
|
+
if (statusBarClearance) {
|
|
49
|
+
clearance = statusBarClearance.left + statusBarClearance.right;
|
|
50
|
+
}
|
|
51
|
+
if (sideBar) {
|
|
52
|
+
return contentWidth - Spacing.M * 2 - clearance;
|
|
53
|
+
}
|
|
54
|
+
return (
|
|
55
|
+
width -
|
|
56
|
+
HEADER_ACTIONS_WIDTH -
|
|
57
|
+
(statusBarClearance ? STATUS_BAR_ROW_ACTIONS_EXTRA : 0) -
|
|
58
|
+
clearance
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
30
62
|
type HeaderTitleInterpolate = {
|
|
31
63
|
inputRange: number[];
|
|
32
64
|
outputRange: number[];
|
|
@@ -47,6 +79,7 @@ const HeaderTitle: React.FC<
|
|
|
47
79
|
> = props => {
|
|
48
80
|
const context = useContext<any>(MiniAppContext);
|
|
49
81
|
const headerFontFamily = useHeaderFontFamily();
|
|
82
|
+
const maxWidth = useTitleMaxWidth();
|
|
50
83
|
|
|
51
84
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
52
85
|
|
|
@@ -77,7 +110,7 @@ const HeaderTitle: React.FC<
|
|
|
77
110
|
style={[
|
|
78
111
|
{
|
|
79
112
|
justifyContent: 'center',
|
|
80
|
-
maxWidth
|
|
113
|
+
maxWidth,
|
|
81
114
|
},
|
|
82
115
|
showBaseLineDebug && styles.debugBaseLine,
|
|
83
116
|
]}
|
|
@@ -90,7 +123,6 @@ const HeaderTitle: React.FC<
|
|
|
90
123
|
{
|
|
91
124
|
fontSize: useScaleSize(15),
|
|
92
125
|
lineHeight: useScaleSize(22),
|
|
93
|
-
fontWeight: 'bold',
|
|
94
126
|
},
|
|
95
127
|
{
|
|
96
128
|
fontFamily: exportFontFamily(
|
|
@@ -142,7 +174,7 @@ const TitleUser: React.FC<TitleUserProps> = ({
|
|
|
142
174
|
}) => {
|
|
143
175
|
const { theme } = useContext(ApplicationContext);
|
|
144
176
|
const headerFontFamily = useHeaderFontFamily();
|
|
145
|
-
const maxWidth =
|
|
177
|
+
const maxWidth = useTitleMaxWidth();
|
|
146
178
|
const getShortName = (name: string) => {
|
|
147
179
|
const words = name.split(' ');
|
|
148
180
|
const lastTwoWords = words.slice(-2);
|
|
@@ -425,7 +457,7 @@ const TitleLocation: React.FC<TitleLocationProps> = ({
|
|
|
425
457
|
isLoading,
|
|
426
458
|
}) => {
|
|
427
459
|
const headerFontFamily = useHeaderFontFamily();
|
|
428
|
-
const maxWidth =
|
|
460
|
+
const maxWidth = useTitleMaxWidth();
|
|
429
461
|
const renderShimmer = () => {
|
|
430
462
|
return (
|
|
431
463
|
<View style={Styles.flex}>
|
|
@@ -494,10 +526,13 @@ const TitleJourney: React.FC<TitleJourneyProps> = ({
|
|
|
494
526
|
isLoading,
|
|
495
527
|
}) => {
|
|
496
528
|
const headerFontFamily = useHeaderFontFamily();
|
|
497
|
-
const
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
const
|
|
529
|
+
const maxWidth = useTitleMaxWidth();
|
|
530
|
+
const { sideBar } = useScreenLayout();
|
|
531
|
+
// journey split is tuned against the whole bar width
|
|
532
|
+
const screenWidth = Dimensions.get('screen').width;
|
|
533
|
+
const barWidth = sideBar ? maxWidth + HEADER_ACTIONS_WIDTH : screenWidth;
|
|
534
|
+
const startWidth = barWidth * 0.25;
|
|
535
|
+
const endWidth = barWidth * 0.75 - 212;
|
|
501
536
|
const renderShimmer = () => {
|
|
502
537
|
return (
|
|
503
538
|
<View style={Styles.flex}>
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { Colors, Radius, Spacing, Styles } from '../../Consts';
|
|
2
2
|
import { InputRef, InputSearch } from '../../Input';
|
|
3
|
-
import {
|
|
4
|
-
Dimensions,
|
|
5
|
-
StyleSheet,
|
|
6
|
-
TouchableOpacity,
|
|
7
|
-
View,
|
|
8
|
-
} from 'react-native';
|
|
3
|
+
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
9
4
|
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
|
|
10
5
|
import React, { useContext } from 'react';
|
|
11
6
|
import { SearchHeaderProps } from '../types';
|
|
12
7
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
13
8
|
import { Text } from '../../Text';
|
|
14
9
|
import { useNormalizedSharedValue } from '../utils';
|
|
10
|
+
import { useScreenLayout } from '../../Layout';
|
|
11
|
+
import { DuoMetrics } from '../../Layout/utils';
|
|
15
12
|
|
|
16
13
|
const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
17
14
|
(
|
|
@@ -26,19 +23,29 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
26
23
|
) => {
|
|
27
24
|
const { theme, navigator } = useContext(ApplicationContext);
|
|
28
25
|
const context = useContext<any>(MiniAppContext);
|
|
29
|
-
const
|
|
30
|
-
|
|
26
|
+
const layout = useScreenLayout();
|
|
27
|
+
// the iPhone Duo status bar row keeps a side bar sized back button
|
|
28
|
+
const BACK_WIDTH = layout.statusBarClearance ? DuoMetrics.controlSize : 28;
|
|
31
29
|
|
|
32
30
|
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
33
31
|
const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
|
|
34
32
|
const headerRightWidth = props?.headerRightWidth ?? 73;
|
|
35
33
|
|
|
34
|
+
// iPhone Duo side bar holds back: the search shares the content width with the buttons only
|
|
35
|
+
let searchMaxWidth = layout.width - leftPosition - 12 - headerRightWidth;
|
|
36
|
+
if (layout.sideBar) {
|
|
37
|
+
searchMaxWidth = layout.contentWidth - Spacing.M * 2 - headerRightWidth;
|
|
38
|
+
}
|
|
39
|
+
if (layout.statusBarClearance) {
|
|
40
|
+
searchMaxWidth -=
|
|
41
|
+
layout.statusBarClearance.left + layout.statusBarClearance.right;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
const backgroundStyle = useAnimatedStyle(() => {
|
|
37
45
|
const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
|
|
46
|
+
const { background } = theme.colors;
|
|
38
47
|
return {
|
|
39
|
-
backgroundColor: t === 0
|
|
40
|
-
? theme.colors.background.surface
|
|
41
|
-
: theme.colors.background.default,
|
|
48
|
+
backgroundColor: t === 0 ? background.surface : background.default,
|
|
42
49
|
};
|
|
43
50
|
});
|
|
44
51
|
|
|
@@ -77,13 +84,11 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
77
84
|
style={[
|
|
78
85
|
Styles.flex,
|
|
79
86
|
renderButtons && {
|
|
80
|
-
maxWidth:
|
|
87
|
+
maxWidth: searchMaxWidth,
|
|
81
88
|
},
|
|
82
89
|
]}
|
|
83
90
|
>
|
|
84
|
-
<Animated.View
|
|
85
|
-
style={[{ borderRadius: Radius.XL }, backgroundStyle]}
|
|
86
|
-
>
|
|
91
|
+
<Animated.View style={[{ borderRadius: Radius.XL }, backgroundStyle]}>
|
|
87
92
|
<InputSearch
|
|
88
93
|
ref={ref}
|
|
89
94
|
{...props}
|