@momo-kits/foundation 0.92.1-rc.1 → 0.92.1-rc.10
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/BottomTab/index.tsx +8 -13
- package/Application/utils.tsx +8 -0
- package/Button/index.tsx +8 -2
- package/CheckBox/index.tsx +9 -3
- package/Input/Input.tsx +13 -7
- package/Input/InputDropDown.tsx +13 -7
- package/Input/InputMoney.tsx +11 -3
- package/Input/InputOTP.tsx +8 -2
- package/Input/InputSearch.tsx +1 -1
- package/Input/InputTextArea.tsx +9 -7
- package/Radio/index.tsx +8 -2
- package/Text/styles.ts +1 -1
- package/Text/types.ts +1 -1
- package/Title/index.tsx +7 -18
- package/Title/styles.ts +11 -5
- package/package.json +1 -2
- package/Application/BottomTab/BottomTabBar.tsx +0 -507
- package/Application/BottomTab/CurvedView.tsx +0 -27
- package/Application/BottomTab/path.ts +0 -202
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
|
2
|
-
import {NavigationContainer} from '@react-navigation/native';
|
|
3
|
-
import {createStackNavigator} from '@react-navigation/stack';
|
|
4
1
|
import React, {useContext, useEffect} from 'react';
|
|
5
2
|
import {Platform} from 'react-native';
|
|
3
|
+
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
|
4
|
+
import {createStackNavigator} from '@react-navigation/stack';
|
|
6
5
|
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
7
|
-
import
|
|
8
|
-
import {Icon} from '../../Icon';
|
|
9
|
-
import {Text} from '../../Text';
|
|
10
|
-
import {ApplicationContext} from '../index';
|
|
11
|
-
import {getAccessibilityID, getStackOptions} from '../utils';
|
|
12
|
-
import StackScreen from '../StackScreen';
|
|
6
|
+
import {NavigationContainer} from '@react-navigation/native';
|
|
13
7
|
import {BottomTabProps, NavigationScreenProps} from '../types';
|
|
8
|
+
import StackScreen from '../StackScreen';
|
|
9
|
+
import {getAccessibilityID, getStackOptions} from '../utils';
|
|
10
|
+
import {ApplicationContext} from '../index';
|
|
11
|
+
import {Text} from '../../Text';
|
|
12
|
+
import {Icon} from '../../Icon';
|
|
14
13
|
|
|
15
14
|
const Tab = createBottomTabNavigator();
|
|
16
15
|
const Stack = createStackNavigator();
|
|
@@ -48,7 +47,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
48
47
|
listeners,
|
|
49
48
|
navigation,
|
|
50
49
|
initialRouteName,
|
|
51
|
-
floatingButton,
|
|
52
50
|
}) => {
|
|
53
51
|
const {theme} = useContext(ApplicationContext);
|
|
54
52
|
const insets = useSafeAreaInsets();
|
|
@@ -66,9 +64,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
66
64
|
{...getAccessibilityID('tbr_main_bottom')}
|
|
67
65
|
initialRouteName={initialRouteName}
|
|
68
66
|
backBehavior={'firstRoute'}
|
|
69
|
-
tabBar={props => (
|
|
70
|
-
<BottomTabBar {...props} floatingButton={floatingButton} />
|
|
71
|
-
)}
|
|
72
67
|
tabBarOptions={{
|
|
73
68
|
style: {
|
|
74
69
|
height: 68 + Math.min(insets.bottom, 21),
|
package/Application/utils.tsx
CHANGED
|
@@ -158,6 +158,13 @@ const getAccessibilityID = (
|
|
|
158
158
|
}
|
|
159
159
|
};
|
|
160
160
|
|
|
161
|
+
const cleanNameForTracking = (word = ''): string => {
|
|
162
|
+
return word
|
|
163
|
+
.normalize('NFD')
|
|
164
|
+
.replace(/[\u0300-\u036f |\s]/g, '')
|
|
165
|
+
.toLowerCase();
|
|
166
|
+
};
|
|
167
|
+
|
|
161
168
|
const formatComponentName = (word = '') => {
|
|
162
169
|
const accentsMap = {
|
|
163
170
|
a: 'á|à|ả|ã|ạ|â|ấ|ầ|ẩ|ẫ|ậ|ă|ắ|ằ|ẳ|ẵ|ặ',
|
|
@@ -201,4 +208,5 @@ export {
|
|
|
201
208
|
formatComponentName,
|
|
202
209
|
formatNameLength,
|
|
203
210
|
getAccessibilityID,
|
|
211
|
+
cleanNameForTracking,
|
|
204
212
|
};
|
package/Button/index.tsx
CHANGED
|
@@ -14,7 +14,11 @@ import {Colors, Spacing} from '../Consts';
|
|
|
14
14
|
import styles from './styles';
|
|
15
15
|
import {Icon} from '../Icon';
|
|
16
16
|
import {Loader} from '../Loader';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
cleanNameForTracking,
|
|
19
|
+
formatComponentName,
|
|
20
|
+
getAccessibilityID,
|
|
21
|
+
} from '../Application/utils';
|
|
18
22
|
|
|
19
23
|
const {
|
|
20
24
|
ScreenContext,
|
|
@@ -323,7 +327,9 @@ const Button: FC<ButtonProps> = ({
|
|
|
323
327
|
<TouchableOpacity
|
|
324
328
|
{...rest}
|
|
325
329
|
{...getAccessibilityID(
|
|
326
|
-
`${appId}/${code}/${screenInfo.screenName}/btn_${
|
|
330
|
+
`${appId}/${code}/${screenInfo.screenName}/btn_${cleanNameForTracking(
|
|
331
|
+
title,
|
|
332
|
+
)}`,
|
|
327
333
|
accessibilityLabel,
|
|
328
334
|
)}
|
|
329
335
|
activeOpacity={activeOpacity}
|
package/CheckBox/index.tsx
CHANGED
|
@@ -5,7 +5,11 @@ import styles from './styles';
|
|
|
5
5
|
import {ApplicationContext} from '../Application';
|
|
6
6
|
import {Text} from '../Text';
|
|
7
7
|
import {Icon} from '../Icon';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
cleanNameForTracking,
|
|
10
|
+
formatNameLength,
|
|
11
|
+
getAccessibilityID,
|
|
12
|
+
} from '../Application/utils';
|
|
9
13
|
|
|
10
14
|
const {
|
|
11
15
|
ScreenContext,
|
|
@@ -22,7 +26,7 @@ const CheckBox: FC<CheckBoxProps> = ({
|
|
|
22
26
|
params,
|
|
23
27
|
componentId,
|
|
24
28
|
}) => {
|
|
25
|
-
const {appId} = useContext<any>(MiniAppContext);
|
|
29
|
+
const {appId, code} = useContext<any>(MiniAppContext);
|
|
26
30
|
const {theme} = useContext(ApplicationContext);
|
|
27
31
|
const screenInfo = useContext<any>(ScreenContext);
|
|
28
32
|
const haveValue = value || indeterminate;
|
|
@@ -45,7 +49,9 @@ const CheckBox: FC<CheckBoxProps> = ({
|
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
const accessibilityID = getAccessibilityID(
|
|
48
|
-
`${appId}/${screenInfo.screenName}/cbx_${
|
|
52
|
+
`${appId}/${code}/${screenInfo.screenName}/cbx_${cleanNameForTracking(
|
|
53
|
+
label,
|
|
54
|
+
)}_${indeterminate ? 'na' : value}`,
|
|
49
55
|
);
|
|
50
56
|
|
|
51
57
|
return (
|
package/Input/Input.tsx
CHANGED
|
@@ -14,7 +14,11 @@ import {InputProps} from './index';
|
|
|
14
14
|
import {Icon} from '../Icon';
|
|
15
15
|
import {checkTyping} from './utils';
|
|
16
16
|
import {Loader} from '../Loader';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
formatComponentName,
|
|
19
|
+
getAccessibilityID,
|
|
20
|
+
cleanNameForTracking,
|
|
21
|
+
} from '../Application/utils';
|
|
18
22
|
|
|
19
23
|
const {
|
|
20
24
|
ScreenContext,
|
|
@@ -120,14 +124,16 @@ const Input = forwardRef(
|
|
|
120
124
|
let iconTintColor = iconColor;
|
|
121
125
|
const accessibilityID = disabled
|
|
122
126
|
? getAccessibilityID(
|
|
123
|
-
`${appId}/${code}/${
|
|
124
|
-
|
|
125
|
-
}
|
|
127
|
+
`${appId}/${code}/${
|
|
128
|
+
screenInfo.screenName
|
|
129
|
+
}/input_${cleanNameForTracking(
|
|
130
|
+
floatingValue ?? placeholder,
|
|
131
|
+
)}_disabled`,
|
|
126
132
|
)
|
|
127
133
|
: getAccessibilityID(
|
|
128
|
-
`${appId}/${code}/${
|
|
129
|
-
|
|
130
|
-
}`,
|
|
134
|
+
`${appId}/${code}/${
|
|
135
|
+
screenInfo.screenName
|
|
136
|
+
}/input_${cleanNameForTracking(floatingValue ?? placeholder)}`,
|
|
131
137
|
);
|
|
132
138
|
|
|
133
139
|
if (disabled) {
|
package/Input/InputDropDown.tsx
CHANGED
|
@@ -7,7 +7,11 @@ import {InputDropDownProps} from './index';
|
|
|
7
7
|
import {Icon} from '../Icon';
|
|
8
8
|
import {Loader} from '../Loader';
|
|
9
9
|
import {Styles} from '../Consts';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
cleanNameForTracking,
|
|
12
|
+
formatComponentName,
|
|
13
|
+
getAccessibilityID,
|
|
14
|
+
} from '../Application/utils';
|
|
11
15
|
|
|
12
16
|
const {
|
|
13
17
|
ScreenContext,
|
|
@@ -57,14 +61,16 @@ const InputDropDown = ({
|
|
|
57
61
|
let iconTintColor = iconColor;
|
|
58
62
|
const accessibilityID = disabled
|
|
59
63
|
? getAccessibilityID(
|
|
60
|
-
`${appId}/${code}/${
|
|
61
|
-
|
|
62
|
-
}
|
|
64
|
+
`${appId}/${code}/${
|
|
65
|
+
screenInfo.screenName
|
|
66
|
+
}/input_${cleanNameForTracking(
|
|
67
|
+
floatingValue ?? placeholder,
|
|
68
|
+
)}_disabled`,
|
|
63
69
|
)
|
|
64
70
|
: getAccessibilityID(
|
|
65
|
-
`${appId}/${code}/${
|
|
66
|
-
|
|
67
|
-
}`,
|
|
71
|
+
`${appId}/${code}/${
|
|
72
|
+
screenInfo.screenName
|
|
73
|
+
}/input_${cleanNameForTracking(floatingValue ?? placeholder)}`,
|
|
68
74
|
);
|
|
69
75
|
|
|
70
76
|
if (disabled) {
|
package/Input/InputMoney.tsx
CHANGED
|
@@ -18,7 +18,11 @@ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
|
|
|
18
18
|
import {InputMoneyProps} from './index';
|
|
19
19
|
import {Icon} from '../Icon';
|
|
20
20
|
import {formatMoneyToNumber, formatNumberToMoney} from './utils';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
cleanNameForTracking,
|
|
23
|
+
formatComponentName,
|
|
24
|
+
getAccessibilityID,
|
|
25
|
+
} from '../Application/utils';
|
|
22
26
|
|
|
23
27
|
const {
|
|
24
28
|
ScreenContext,
|
|
@@ -101,10 +105,14 @@ const InputMoney = forwardRef(
|
|
|
101
105
|
let iconTintColor = iconColor;
|
|
102
106
|
const accessibilityID = disabled
|
|
103
107
|
? getAccessibilityID(
|
|
104
|
-
`${appId}/${code}/${
|
|
108
|
+
`${appId}/${code}/${
|
|
109
|
+
screenInfo.screenName
|
|
110
|
+
}/input_${cleanNameForTracking(floatingValue)}_disabled`,
|
|
105
111
|
)
|
|
106
112
|
: getAccessibilityID(
|
|
107
|
-
`${appId}/${code}/${
|
|
113
|
+
`${appId}/${code}/${
|
|
114
|
+
screenInfo.screenName
|
|
115
|
+
}/input_${cleanNameForTracking(floatingValue)}`,
|
|
108
116
|
);
|
|
109
117
|
|
|
110
118
|
if (disabled) {
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -21,7 +21,11 @@ import styles from './styles';
|
|
|
21
21
|
import {ErrorView, getBorderColor} from './common';
|
|
22
22
|
import {ApplicationContext} from '../Application';
|
|
23
23
|
import {Spacing} from '../Consts';
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
cleanNameForTracking,
|
|
26
|
+
formatComponentName,
|
|
27
|
+
getAccessibilityID,
|
|
28
|
+
} from '../Application/utils';
|
|
25
29
|
|
|
26
30
|
const {
|
|
27
31
|
ScreenContext,
|
|
@@ -192,7 +196,9 @@ const InputOTP = forwardRef(
|
|
|
192
196
|
};
|
|
193
197
|
|
|
194
198
|
const accessibilityID = getAccessibilityID(
|
|
195
|
-
`${appId}/${code}/${screenInfo.screenName}/input_${
|
|
199
|
+
`${appId}/${code}/${screenInfo.screenName}/input_${cleanNameForTracking(
|
|
200
|
+
floatingValue,
|
|
201
|
+
)}`,
|
|
196
202
|
);
|
|
197
203
|
|
|
198
204
|
const renderInputView = () => {
|
package/Input/InputSearch.tsx
CHANGED
package/Input/InputTextArea.tsx
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import {InputTextAreaProps} from './index';
|
|
19
19
|
import {Icon} from '../Icon';
|
|
20
20
|
import {ApplicationContext} from '../Application';
|
|
21
|
-
import {getAccessibilityID} from '../Application/utils';
|
|
21
|
+
import {cleanNameForTracking, getAccessibilityID} from '../Application/utils';
|
|
22
22
|
|
|
23
23
|
const {
|
|
24
24
|
ScreenContext,
|
|
@@ -98,14 +98,16 @@ const InputTextArea = forwardRef(
|
|
|
98
98
|
|
|
99
99
|
const accessibilityID = disabled
|
|
100
100
|
? getAccessibilityID(
|
|
101
|
-
`${appId}/${code}/${
|
|
102
|
-
|
|
103
|
-
}
|
|
101
|
+
`${appId}/${code}/${
|
|
102
|
+
screenInfo.screenName
|
|
103
|
+
}/input_${cleanNameForTracking(
|
|
104
|
+
floatingValue ?? placeholder,
|
|
105
|
+
)}_disabled`,
|
|
104
106
|
)
|
|
105
107
|
: getAccessibilityID(
|
|
106
|
-
`${appId}/${code}/${
|
|
107
|
-
|
|
108
|
-
}`,
|
|
108
|
+
`${appId}/${code}/${
|
|
109
|
+
screenInfo.screenName
|
|
110
|
+
}/input_${cleanNameForTracking(floatingValue ?? placeholder)}`,
|
|
109
111
|
);
|
|
110
112
|
|
|
111
113
|
return (
|
package/Radio/index.tsx
CHANGED
|
@@ -6,7 +6,11 @@ import {Text} from '../Text';
|
|
|
6
6
|
import styles from './styles';
|
|
7
7
|
import {ApplicationContext} from '../Application';
|
|
8
8
|
import {Spacing} from '../Consts';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
cleanNameForTracking,
|
|
11
|
+
formatNameLength,
|
|
12
|
+
getAccessibilityID,
|
|
13
|
+
} from '../Application/utils';
|
|
10
14
|
|
|
11
15
|
const {
|
|
12
16
|
ScreenContext,
|
|
@@ -48,7 +52,9 @@ const Radio: FC<RadioProps> = ({
|
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
const accessibilityID = getAccessibilityID?.(
|
|
51
|
-
`${appId}/${code}/${screenInfo.screenName}/rdo_${
|
|
55
|
+
`${appId}/${code}/${screenInfo.screenName}/rdo_${cleanNameForTracking(
|
|
56
|
+
label,
|
|
57
|
+
)}_${value}`,
|
|
52
58
|
);
|
|
53
59
|
|
|
54
60
|
return (
|
package/Text/styles.ts
CHANGED
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
header_s_semibold: {fontSize: 14, lineHeight: 20, fontWeight: '600'},
|
|
6
6
|
header_xs_semibold: {fontSize: 12, lineHeight: 18, fontWeight: '600'},
|
|
7
7
|
body_default_regular: {fontSize: 14, lineHeight: 20, fontWeight: '400'},
|
|
8
|
-
|
|
8
|
+
body_default_regularstrikethrought: {
|
|
9
9
|
fontSize: 14,
|
|
10
10
|
lineHeight: 20,
|
|
11
11
|
fontWeight: '400',
|
package/Text/types.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type Typography =
|
|
|
20
20
|
| 'header_s_semibold'
|
|
21
21
|
| 'header_xs_semibold'
|
|
22
22
|
| 'body_default_regular'
|
|
23
|
-
| '
|
|
23
|
+
| 'body_default_regularstrikethrought'
|
|
24
24
|
| 'description_default_regular'
|
|
25
25
|
| 'description_default_regularstrikethrought'
|
|
26
26
|
| 'description_xs_regular'
|
package/Title/index.tsx
CHANGED
|
@@ -4,7 +4,6 @@ import styles from './styles';
|
|
|
4
4
|
import {Icon} from '../Icon';
|
|
5
5
|
import {scaleSize, Text} from '../Text';
|
|
6
6
|
import {ApplicationContext} from '../Application';
|
|
7
|
-
import {Colors, Spacing} from '../Consts';
|
|
8
7
|
import {Badge} from '../Badge';
|
|
9
8
|
import {Typography} from '../Text/types';
|
|
10
9
|
import {TitleProps} from './types';
|
|
@@ -25,6 +24,7 @@ const Title: FC<TitleProps> = ({
|
|
|
25
24
|
buttonSize = 'small',
|
|
26
25
|
}) => {
|
|
27
26
|
const {theme} = useContext(ApplicationContext);
|
|
27
|
+
|
|
28
28
|
const styleSheet: {
|
|
29
29
|
[key: string]: any;
|
|
30
30
|
} = styles;
|
|
@@ -33,10 +33,6 @@ const Title: FC<TitleProps> = ({
|
|
|
33
33
|
const numberOfLines = showLeftAction || !!badgeLabel ? 1 : 2;
|
|
34
34
|
const buttonTypo: Typography =
|
|
35
35
|
buttonSize === 'small' ? 'action_xs_bold' : 'action_s_bold';
|
|
36
|
-
const widthStyle =
|
|
37
|
-
!showLeftAction && !showRightAction && !badgeLabel
|
|
38
|
-
? {width: '100%'}
|
|
39
|
-
: {width: '75%'};
|
|
40
36
|
|
|
41
37
|
const renderIcon = () => {
|
|
42
38
|
if (!icon) return null;
|
|
@@ -62,23 +58,16 @@ const Title: FC<TitleProps> = ({
|
|
|
62
58
|
|
|
63
59
|
const renderContent = () => {
|
|
64
60
|
return (
|
|
65
|
-
<View style={
|
|
66
|
-
<View style={[styles.iconLeftView
|
|
61
|
+
<View style={styles.contentView}>
|
|
62
|
+
<View style={[styles.iconLeftView]}>
|
|
67
63
|
<RNText
|
|
68
64
|
numberOfLines={numberOfLines}
|
|
69
|
-
style={[
|
|
70
|
-
styleSheet[typography],
|
|
71
|
-
{
|
|
72
|
-
fontWeight: 'bold',
|
|
73
|
-
marginBottom: Spacing.XS,
|
|
74
|
-
},
|
|
75
|
-
]}>
|
|
65
|
+
style={[styleSheet[typography], styles.title]}>
|
|
76
66
|
{title}
|
|
77
67
|
</RNText>
|
|
68
|
+
|
|
78
69
|
<View style={{alignItems: 'center'}}>
|
|
79
|
-
{badgeLabel &&
|
|
80
|
-
<Badge style={{marginLeft: Spacing.XS}} label={badgeLabel} />
|
|
81
|
-
)}
|
|
70
|
+
{badgeLabel && <Badge style={styles.badge} label={badgeLabel} />}
|
|
82
71
|
</View>
|
|
83
72
|
{renderActionLeft()}
|
|
84
73
|
</View>
|
|
@@ -95,7 +84,7 @@ const Title: FC<TitleProps> = ({
|
|
|
95
84
|
return (
|
|
96
85
|
<View style={styles.iconLeftView}>
|
|
97
86
|
{showLeftAction && !showRightAction && (
|
|
98
|
-
<View style={
|
|
87
|
+
<View style={styles.iconLeft}>
|
|
99
88
|
<Icon
|
|
100
89
|
source={'arrow_chevron_right_small'}
|
|
101
90
|
size={scaleSize(18)}
|
package/Title/styles.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {StyleSheet} from 'react-native';
|
|
2
2
|
import {scaleSize} from '../Text';
|
|
3
|
-
import {Radius, Spacing} from '../Consts';
|
|
3
|
+
import {Colors, Radius, Spacing} from '../Consts';
|
|
4
4
|
|
|
5
5
|
export default StyleSheet.create({
|
|
6
6
|
card_small: {
|
|
@@ -39,10 +39,6 @@ export default StyleSheet.create({
|
|
|
39
39
|
wrapper: {
|
|
40
40
|
flexDirection: 'row',
|
|
41
41
|
},
|
|
42
|
-
textContent: {
|
|
43
|
-
marginRight: Spacing.S,
|
|
44
|
-
flex: 1,
|
|
45
|
-
},
|
|
46
42
|
margin: {
|
|
47
43
|
marginTop: Spacing.S,
|
|
48
44
|
},
|
|
@@ -64,9 +60,19 @@ export default StyleSheet.create({
|
|
|
64
60
|
alignItems: 'center',
|
|
65
61
|
justifyContent: 'center',
|
|
66
62
|
marginHorizontal: Spacing.XS,
|
|
63
|
+
backgroundColor: Colors.black_06,
|
|
67
64
|
},
|
|
68
65
|
iconLeftView: {
|
|
69
66
|
flexDirection: 'row',
|
|
70
67
|
alignItems: 'center',
|
|
71
68
|
},
|
|
69
|
+
contentView: {marginRight: Spacing.S, flex: 1},
|
|
70
|
+
title: {
|
|
71
|
+
fontWeight: 'bold',
|
|
72
|
+
marginBottom: Spacing.XS,
|
|
73
|
+
flex: 1,
|
|
74
|
+
},
|
|
75
|
+
badge: {
|
|
76
|
+
marginLeft: Spacing.XS,
|
|
77
|
+
},
|
|
72
78
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momo-kits/foundation",
|
|
3
|
-
"version": "0.92.1-rc.
|
|
3
|
+
"version": "0.92.1-rc.10",
|
|
4
4
|
"description": "React Native Component Kits",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"@react-navigation/native": "5.9.8",
|
|
22
22
|
"@react-navigation/routers": "5.7.4",
|
|
23
23
|
"lottie-react-native": "3.3.2",
|
|
24
|
-
"d3-shape": "1.3.7",
|
|
25
24
|
"@react-navigation/stack": "git+https://gitlab.com/momo-platform/react-navigation-stack.git#main"
|
|
26
25
|
},
|
|
27
26
|
"peerDependencies": {
|
|
@@ -1,507 +0,0 @@
|
|
|
1
|
-
import {BottomTabBarProps} from '@react-navigation/bottom-tabs';
|
|
2
|
-
import {LabelPosition} from '@react-navigation/bottom-tabs/src/types';
|
|
3
|
-
import BottomTabBarHeightCallbackContext from '@react-navigation/bottom-tabs/src/utils/BottomTabBarHeightCallbackContext';
|
|
4
|
-
import BottomTabBarItem from '@react-navigation/bottom-tabs/src/views/BottomTabItem';
|
|
5
|
-
import {
|
|
6
|
-
CommonActions,
|
|
7
|
-
NavigationContext,
|
|
8
|
-
NavigationRouteContext,
|
|
9
|
-
ParamListBase,
|
|
10
|
-
TabNavigationState,
|
|
11
|
-
useLinkBuilder,
|
|
12
|
-
useTheme,
|
|
13
|
-
} from '@react-navigation/native';
|
|
14
|
-
import React, {useCallback} from 'react';
|
|
15
|
-
import {
|
|
16
|
-
Animated,
|
|
17
|
-
Dimensions,
|
|
18
|
-
LayoutChangeEvent,
|
|
19
|
-
Platform,
|
|
20
|
-
StyleProp,
|
|
21
|
-
StyleSheet,
|
|
22
|
-
TouchableOpacity,
|
|
23
|
-
useWindowDimensions,
|
|
24
|
-
View,
|
|
25
|
-
ViewStyle,
|
|
26
|
-
} from 'react-native';
|
|
27
|
-
import {EdgeInsets, useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
28
|
-
import {Radius} from '../../Consts';
|
|
29
|
-
import {Icon} from '../../Icon';
|
|
30
|
-
import {Text} from '../../Text';
|
|
31
|
-
import {CurvedViewComponent} from './CurvedView';
|
|
32
|
-
import {getPathUp} from './path';
|
|
33
|
-
import {FloatingButtonProps} from '../types';
|
|
34
|
-
const {width} = Dimensions.get('window');
|
|
35
|
-
|
|
36
|
-
type Props = BottomTabBarProps & {
|
|
37
|
-
activeTintColor?: string;
|
|
38
|
-
inactiveTintColor?: string;
|
|
39
|
-
floatingButton?: FloatingButtonProps;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type Options = {
|
|
43
|
-
state: TabNavigationState<ParamListBase>;
|
|
44
|
-
layout: {height: number; width: number};
|
|
45
|
-
dimensions: {height: number; width: number};
|
|
46
|
-
tabStyle: StyleProp<ViewStyle>;
|
|
47
|
-
labelPosition: LabelPosition | undefined;
|
|
48
|
-
adaptive: boolean | undefined;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
|
|
52
|
-
const DEFAULT_TABBAR_HEIGHT = 49;
|
|
53
|
-
const COMPACT_TABBAR_HEIGHT = 32;
|
|
54
|
-
|
|
55
|
-
const shouldUseHorizontalLabels = ({
|
|
56
|
-
state,
|
|
57
|
-
layout,
|
|
58
|
-
dimensions,
|
|
59
|
-
adaptive = true,
|
|
60
|
-
labelPosition,
|
|
61
|
-
tabStyle,
|
|
62
|
-
}: Options) => {
|
|
63
|
-
if (labelPosition) {
|
|
64
|
-
return labelPosition === 'beside-icon';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!adaptive) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (layout.width >= 768) {
|
|
72
|
-
// Screen size matches a tablet
|
|
73
|
-
let maxTabItemWidth = DEFAULT_MAX_TAB_ITEM_WIDTH;
|
|
74
|
-
|
|
75
|
-
const flattenedStyle = StyleSheet.flatten(tabStyle);
|
|
76
|
-
|
|
77
|
-
if (flattenedStyle) {
|
|
78
|
-
if (typeof flattenedStyle.width === 'number') {
|
|
79
|
-
maxTabItemWidth = flattenedStyle.width;
|
|
80
|
-
} else if (typeof flattenedStyle.maxWidth === 'number') {
|
|
81
|
-
maxTabItemWidth = flattenedStyle.maxWidth;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return state.routes.length * maxTabItemWidth <= layout.width;
|
|
86
|
-
} else {
|
|
87
|
-
return dimensions.width > dimensions.height;
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
const getPaddingBottom = (insets: EdgeInsets) =>
|
|
91
|
-
Math.max(insets.bottom - Platform.select({ios: 4, default: 0}), 0);
|
|
92
|
-
|
|
93
|
-
export const getTabBarHeight = ({
|
|
94
|
-
dimensions,
|
|
95
|
-
insets,
|
|
96
|
-
style,
|
|
97
|
-
...rest
|
|
98
|
-
}: Options & {
|
|
99
|
-
insets: EdgeInsets;
|
|
100
|
-
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
|
101
|
-
}) => {
|
|
102
|
-
// @ts-ignore
|
|
103
|
-
const customHeight = StyleSheet.flatten(style)?.height;
|
|
104
|
-
|
|
105
|
-
if (typeof customHeight === 'number') {
|
|
106
|
-
return customHeight;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const isLandscape = dimensions.width > dimensions.height;
|
|
110
|
-
const horizontalLabels = shouldUseHorizontalLabels({dimensions, ...rest});
|
|
111
|
-
const paddingBottom = getPaddingBottom(insets);
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
Platform.OS === 'ios' &&
|
|
115
|
-
!Platform.isPad &&
|
|
116
|
-
isLandscape &&
|
|
117
|
-
horizontalLabels
|
|
118
|
-
) {
|
|
119
|
-
return COMPACT_TABBAR_HEIGHT + paddingBottom;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return DEFAULT_TABBAR_HEIGHT + paddingBottom;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
export default function BottomTabBar({
|
|
126
|
-
state,
|
|
127
|
-
navigation,
|
|
128
|
-
descriptors,
|
|
129
|
-
activeBackgroundColor,
|
|
130
|
-
activeTintColor,
|
|
131
|
-
adaptive,
|
|
132
|
-
allowFontScaling,
|
|
133
|
-
inactiveBackgroundColor,
|
|
134
|
-
inactiveTintColor,
|
|
135
|
-
keyboardHidesTabBar = false,
|
|
136
|
-
labelPosition,
|
|
137
|
-
labelStyle,
|
|
138
|
-
iconStyle,
|
|
139
|
-
safeAreaInsets,
|
|
140
|
-
showLabel,
|
|
141
|
-
style,
|
|
142
|
-
tabStyle,
|
|
143
|
-
floatingButton,
|
|
144
|
-
}: Props) {
|
|
145
|
-
const {routes} = state;
|
|
146
|
-
const dimensions = useWindowDimensions();
|
|
147
|
-
const isFloating = floatingButton?.icon && state.routes.length == 4;
|
|
148
|
-
const itemWidth = isFloating
|
|
149
|
-
? dimensions.width / 5
|
|
150
|
-
: dimensions.width / state.routes.length;
|
|
151
|
-
|
|
152
|
-
const [layout, setLayout] = React.useState({
|
|
153
|
-
height: 0,
|
|
154
|
-
width: dimensions.width,
|
|
155
|
-
});
|
|
156
|
-
const buildLink = useLinkBuilder();
|
|
157
|
-
const {colors} = useTheme();
|
|
158
|
-
|
|
159
|
-
const hasHorizontalLabels = shouldUseHorizontalLabels({
|
|
160
|
-
state,
|
|
161
|
-
dimensions,
|
|
162
|
-
layout,
|
|
163
|
-
adaptive,
|
|
164
|
-
labelPosition,
|
|
165
|
-
tabStyle,
|
|
166
|
-
});
|
|
167
|
-
const focusedRoute = state.routes[state.index] as any;
|
|
168
|
-
const focusedDescriptor = descriptors[focusedRoute.key] as any;
|
|
169
|
-
const focusedOptions = focusedDescriptor.options;
|
|
170
|
-
const shouldShowTabBar =
|
|
171
|
-
focusedOptions.tabBarVisible !== false && !keyboardHidesTabBar;
|
|
172
|
-
const [visible] = React.useState(
|
|
173
|
-
() => new Animated.Value(shouldShowTabBar ? 1 : 0),
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
const indicatorAnimated = React.useRef(new Animated.Value(0)).current;
|
|
177
|
-
|
|
178
|
-
const visibilityAnimationConfigRef = React.useRef(
|
|
179
|
-
focusedOptions.tabBarVisibilityAnimationConfig,
|
|
180
|
-
);
|
|
181
|
-
const onHeightChange = React.useContext(BottomTabBarHeightCallbackContext);
|
|
182
|
-
const [isTabBarHidden, setIsTabBarHidden] = React.useState(!shouldShowTabBar);
|
|
183
|
-
React.useEffect(() => {
|
|
184
|
-
visibilityAnimationConfigRef.current =
|
|
185
|
-
focusedOptions.tabBarVisibilityAnimationConfig;
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const handleLayout = (e: LayoutChangeEvent) => {
|
|
189
|
-
const {height, width} = e.nativeEvent.layout;
|
|
190
|
-
|
|
191
|
-
onHeightChange?.(height);
|
|
192
|
-
|
|
193
|
-
setLayout(layout => {
|
|
194
|
-
if (height === layout.height && width === layout.width) {
|
|
195
|
-
return layout;
|
|
196
|
-
} else {
|
|
197
|
-
return {
|
|
198
|
-
height,
|
|
199
|
-
width,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
React.useEffect(() => {
|
|
206
|
-
const visibilityAnimationConfig = visibilityAnimationConfigRef.current;
|
|
207
|
-
|
|
208
|
-
if (shouldShowTabBar) {
|
|
209
|
-
const animation =
|
|
210
|
-
visibilityAnimationConfig?.show?.animation === 'spring'
|
|
211
|
-
? Animated.spring
|
|
212
|
-
: Animated.timing;
|
|
213
|
-
|
|
214
|
-
animation(visible, {
|
|
215
|
-
toValue: 1,
|
|
216
|
-
useNativeDriver: true,
|
|
217
|
-
duration: 250,
|
|
218
|
-
...visibilityAnimationConfig?.show?.config,
|
|
219
|
-
}).start(({finished}) => {
|
|
220
|
-
if (finished) {
|
|
221
|
-
setIsTabBarHidden(false);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
} else {
|
|
225
|
-
setIsTabBarHidden(true);
|
|
226
|
-
|
|
227
|
-
const animation =
|
|
228
|
-
visibilityAnimationConfig?.hide?.animation === 'spring'
|
|
229
|
-
? Animated.spring
|
|
230
|
-
: Animated.timing;
|
|
231
|
-
|
|
232
|
-
animation(visible, {
|
|
233
|
-
toValue: 0,
|
|
234
|
-
useNativeDriver: true,
|
|
235
|
-
duration: 200,
|
|
236
|
-
...visibilityAnimationConfig?.hide?.config,
|
|
237
|
-
}).start();
|
|
238
|
-
}
|
|
239
|
-
}, [visible, shouldShowTabBar]);
|
|
240
|
-
const defaultInsets = useSafeAreaInsets();
|
|
241
|
-
|
|
242
|
-
const insets = {
|
|
243
|
-
top: safeAreaInsets?.top ?? defaultInsets.top,
|
|
244
|
-
right: safeAreaInsets?.right ?? defaultInsets.right,
|
|
245
|
-
bottom: safeAreaInsets?.bottom ?? defaultInsets.bottom,
|
|
246
|
-
left: safeAreaInsets?.left ?? defaultInsets.left,
|
|
247
|
-
};
|
|
248
|
-
const paddingBottom = getPaddingBottom(insets);
|
|
249
|
-
|
|
250
|
-
const leftArr = routes.slice(0, 2);
|
|
251
|
-
const rightArr = routes.slice(2, routes.length);
|
|
252
|
-
const focusedTab = state?.routes[state.index]?.name || routes[0]?.params;
|
|
253
|
-
const tabBarHeight = getTabBarHeight({
|
|
254
|
-
state,
|
|
255
|
-
insets,
|
|
256
|
-
dimensions,
|
|
257
|
-
layout,
|
|
258
|
-
adaptive,
|
|
259
|
-
labelPosition,
|
|
260
|
-
tabStyle,
|
|
261
|
-
style,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
const renderTabItem = useCallback(
|
|
265
|
-
(data: any[]) => {
|
|
266
|
-
return data?.map((route, index) => {
|
|
267
|
-
const focused = route.params.name === focusedTab;
|
|
268
|
-
const {options} = descriptors[route.key] as any;
|
|
269
|
-
const onPress = () => {
|
|
270
|
-
const event = navigation.emit({
|
|
271
|
-
type: 'tabPress',
|
|
272
|
-
target: route.key,
|
|
273
|
-
canPreventDefault: true,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
if (!focused && !event.defaultPrevented) {
|
|
277
|
-
navigation.dispatch({
|
|
278
|
-
...CommonActions.navigate(route.name),
|
|
279
|
-
target: state.key,
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const onLongPress = () => {
|
|
285
|
-
navigation.emit({
|
|
286
|
-
type: 'tabLongPress',
|
|
287
|
-
target: route.key,
|
|
288
|
-
});
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
const label =
|
|
292
|
-
options.tabBarLabel !== undefined
|
|
293
|
-
? options.tabBarLabel
|
|
294
|
-
: options.title !== undefined
|
|
295
|
-
? options.title
|
|
296
|
-
: route.name;
|
|
297
|
-
|
|
298
|
-
const accessibilityLabel =
|
|
299
|
-
options.tabBarAccessibilityLabel !== undefined
|
|
300
|
-
? options.tabBarAccessibilityLabel
|
|
301
|
-
: typeof label === 'string'
|
|
302
|
-
? `${label}, tab, ${index + 1} of ${routes.length}`
|
|
303
|
-
: undefined;
|
|
304
|
-
|
|
305
|
-
return (
|
|
306
|
-
<NavigationContext.Provider
|
|
307
|
-
key={route.key}
|
|
308
|
-
value={(descriptors as any)[route.key].navigation}>
|
|
309
|
-
<NavigationRouteContext.Provider value={route}>
|
|
310
|
-
<BottomTabBarItem
|
|
311
|
-
route={route}
|
|
312
|
-
focused={focused}
|
|
313
|
-
horizontal={hasHorizontalLabels}
|
|
314
|
-
onPress={onPress}
|
|
315
|
-
onLongPress={onLongPress}
|
|
316
|
-
accessibilityLabel={accessibilityLabel}
|
|
317
|
-
to={buildLink(route.name, route.params)}
|
|
318
|
-
testID={options.tabBarTestID}
|
|
319
|
-
allowFontScaling={allowFontScaling}
|
|
320
|
-
activeTintColor={activeTintColor}
|
|
321
|
-
inactiveTintColor={inactiveTintColor}
|
|
322
|
-
activeBackgroundColor={activeBackgroundColor}
|
|
323
|
-
inactiveBackgroundColor={inactiveBackgroundColor}
|
|
324
|
-
button={options.tabBarButton}
|
|
325
|
-
icon={options.tabBarIcon}
|
|
326
|
-
badge={options.tabBarBadge}
|
|
327
|
-
badgeStyle={options.tabBarBadgeStyle}
|
|
328
|
-
label={label}
|
|
329
|
-
showLabel={showLabel}
|
|
330
|
-
labelStyle={labelStyle}
|
|
331
|
-
iconStyle={iconStyle}
|
|
332
|
-
style={tabStyle}
|
|
333
|
-
/>
|
|
334
|
-
</NavigationRouteContext.Provider>
|
|
335
|
-
</NavigationContext.Provider>
|
|
336
|
-
);
|
|
337
|
-
});
|
|
338
|
-
},
|
|
339
|
-
[JSON.stringify(routes), state, descriptors, focusedTab],
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
const renderCenterComponent = useCallback(() => {
|
|
343
|
-
const renderFloatingButton = () => {
|
|
344
|
-
if (typeof floatingButton?.icon == 'string') {
|
|
345
|
-
return <Icon source={floatingButton?.icon} size={32} color={'white'} />;
|
|
346
|
-
} else if (floatingButton?.icon) {
|
|
347
|
-
return floatingButton?.icon;
|
|
348
|
-
} else {
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
if (floatingButton?.icon && state.routes.length !== 4) {
|
|
353
|
-
console.warn(
|
|
354
|
-
"Can't render floating button with icon when routes length is not 4",
|
|
355
|
-
);
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
return (
|
|
359
|
-
<View style={{flex: 1}}>
|
|
360
|
-
<TouchableOpacity
|
|
361
|
-
onPress={floatingButton?.onPress}
|
|
362
|
-
style={styles.floatingContainer}>
|
|
363
|
-
<View
|
|
364
|
-
style={{
|
|
365
|
-
...styles.floatingContent,
|
|
366
|
-
backgroundColor: activeTintColor ?? colors.primary,
|
|
367
|
-
}}>
|
|
368
|
-
{renderFloatingButton()}
|
|
369
|
-
</View>
|
|
370
|
-
<View
|
|
371
|
-
style={{
|
|
372
|
-
top: -6,
|
|
373
|
-
backgroundColor: activeTintColor ?? colors.primary,
|
|
374
|
-
borderRadius: Radius.XL,
|
|
375
|
-
paddingHorizontal: 6,
|
|
376
|
-
}}>
|
|
377
|
-
<Text
|
|
378
|
-
numberOfLines={1}
|
|
379
|
-
typography={'description_xs_regular'}
|
|
380
|
-
color={'white'}>
|
|
381
|
-
{floatingButton?.label}
|
|
382
|
-
</Text>
|
|
383
|
-
</View>
|
|
384
|
-
</TouchableOpacity>
|
|
385
|
-
</View>
|
|
386
|
-
);
|
|
387
|
-
}, [floatingButton]);
|
|
388
|
-
|
|
389
|
-
React.useEffect(() => {
|
|
390
|
-
Animated.timing(indicatorAnimated, {
|
|
391
|
-
toValue:
|
|
392
|
-
isFloating && state.index > 1
|
|
393
|
-
? itemWidth * (state.index + 1)
|
|
394
|
-
: itemWidth * state.index,
|
|
395
|
-
useNativeDriver: false,
|
|
396
|
-
duration: 200,
|
|
397
|
-
}).start();
|
|
398
|
-
}, [state.index, itemWidth, indicatorAnimated, isFloating]);
|
|
399
|
-
|
|
400
|
-
return (
|
|
401
|
-
<Animated.View
|
|
402
|
-
style={[
|
|
403
|
-
styles.tabBar,
|
|
404
|
-
{
|
|
405
|
-
backgroundColor: colors.card,
|
|
406
|
-
borderTopColor: colors.border,
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
transform: [
|
|
410
|
-
{
|
|
411
|
-
translateY: visible.interpolate({
|
|
412
|
-
inputRange: [0, 1],
|
|
413
|
-
outputRange: [
|
|
414
|
-
layout.height + paddingBottom + StyleSheet.hairlineWidth,
|
|
415
|
-
0,
|
|
416
|
-
],
|
|
417
|
-
}),
|
|
418
|
-
},
|
|
419
|
-
],
|
|
420
|
-
// Absolutely position the tab bar so that the content is below it
|
|
421
|
-
// This is needed to avoid gap at bottom when the tab bar is hidden
|
|
422
|
-
position: isTabBarHidden ? 'absolute' : (null as any),
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
// height: tabBarHeight,
|
|
426
|
-
paddingBottom: paddingBottom - 0,
|
|
427
|
-
paddingHorizontal: Math.max(insets.left, insets.right),
|
|
428
|
-
},
|
|
429
|
-
style,
|
|
430
|
-
]}
|
|
431
|
-
pointerEvents={isTabBarHidden ? 'none' : 'auto'}
|
|
432
|
-
onLayout={handleLayout}>
|
|
433
|
-
<View style={styles.content}>
|
|
434
|
-
{isFloating && (
|
|
435
|
-
<CurvedViewComponent
|
|
436
|
-
width={width}
|
|
437
|
-
height={61 + Math.min(insets.bottom, 21)}
|
|
438
|
-
style={styles.curved}
|
|
439
|
-
bgColor="white"
|
|
440
|
-
path={getPathUp(width, tabBarHeight, 50, false, 'CENTER')}
|
|
441
|
-
/>
|
|
442
|
-
)}
|
|
443
|
-
<Animated.View
|
|
444
|
-
style={{
|
|
445
|
-
width: itemWidth,
|
|
446
|
-
position: 'absolute',
|
|
447
|
-
top: 0,
|
|
448
|
-
transform: [{translateX: indicatorAnimated}],
|
|
449
|
-
}}>
|
|
450
|
-
<View
|
|
451
|
-
style={[
|
|
452
|
-
styles.indicator,
|
|
453
|
-
{backgroundColor: activeTintColor ?? colors.primary},
|
|
454
|
-
]}
|
|
455
|
-
/>
|
|
456
|
-
</Animated.View>
|
|
457
|
-
|
|
458
|
-
{renderTabItem(leftArr)}
|
|
459
|
-
{renderCenterComponent()}
|
|
460
|
-
{renderTabItem(rightArr)}
|
|
461
|
-
</View>
|
|
462
|
-
</Animated.View>
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
const styles = StyleSheet.create({
|
|
467
|
-
curved: {
|
|
468
|
-
position: 'absolute',
|
|
469
|
-
bottom: 0,
|
|
470
|
-
// backgroundColor: 'red',
|
|
471
|
-
},
|
|
472
|
-
content: {
|
|
473
|
-
flex: 1,
|
|
474
|
-
flexDirection: 'row',
|
|
475
|
-
// backgroundColor: 'red',
|
|
476
|
-
},
|
|
477
|
-
tabBar: {
|
|
478
|
-
left: 0,
|
|
479
|
-
right: 0,
|
|
480
|
-
bottom: 0,
|
|
481
|
-
borderTopWidth: StyleSheet.hairlineWidth,
|
|
482
|
-
elevation: 8,
|
|
483
|
-
},
|
|
484
|
-
floatingContainer: {
|
|
485
|
-
position: 'absolute',
|
|
486
|
-
alignItems: 'center',
|
|
487
|
-
left: 0,
|
|
488
|
-
right: 0,
|
|
489
|
-
bottom: -6,
|
|
490
|
-
},
|
|
491
|
-
floatingContent: {
|
|
492
|
-
alignItems: 'center',
|
|
493
|
-
justifyContent: 'center',
|
|
494
|
-
height: 48,
|
|
495
|
-
width: 48,
|
|
496
|
-
|
|
497
|
-
borderRadius: 100,
|
|
498
|
-
top: -12,
|
|
499
|
-
},
|
|
500
|
-
indicator: {
|
|
501
|
-
height: 2,
|
|
502
|
-
width: 44,
|
|
503
|
-
alignSelf: 'center',
|
|
504
|
-
borderBottomLeftRadius: 1,
|
|
505
|
-
borderBottomRightRadius: 1,
|
|
506
|
-
},
|
|
507
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {StyleProp, View, ViewStyle} from 'react-native';
|
|
3
|
-
import {Path, Svg} from 'react-native-svg';
|
|
4
|
-
|
|
5
|
-
interface ICurvedView {
|
|
6
|
-
style?: StyleProp<ViewStyle>;
|
|
7
|
-
width: number;
|
|
8
|
-
height: number;
|
|
9
|
-
bgColor: string;
|
|
10
|
-
path: any;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type CurvedView = React.FC<ICurvedView>;
|
|
14
|
-
|
|
15
|
-
const CurvedContainer: CurvedView = props => {
|
|
16
|
-
const {style, width, height, bgColor, path} = props;
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<View style={style} pointerEvents="none">
|
|
20
|
-
<Svg width={width} height={height}>
|
|
21
|
-
<Path fill={bgColor} {...{d: path}} />
|
|
22
|
-
</Svg>
|
|
23
|
-
</View>
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const CurvedViewComponent: CurvedView = React.memo(CurvedContainer);
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import * as shape from 'd3-shape';
|
|
2
|
-
import {Platform} from 'react-native';
|
|
3
|
-
|
|
4
|
-
export const TABBAR_ITEM_TOP = Platform.OS === 'ios' ? 8 : 4;
|
|
5
|
-
export const CIRCLE_SIZE = 50;
|
|
6
|
-
export const TABBAR_ITEM_ICON_SIZE = 28;
|
|
7
|
-
export const CIRCLE_POSITION_TOP =
|
|
8
|
-
CIRCLE_SIZE - TABBAR_ITEM_ICON_SIZE - TABBAR_ITEM_TOP;
|
|
9
|
-
export const CIRCLE_OVER_SIZE = 10;
|
|
10
|
-
export const CIRCLE_OVER_TOP = CIRCLE_POSITION_TOP + CIRCLE_OVER_SIZE;
|
|
11
|
-
|
|
12
|
-
export const scale = (number: number) => {
|
|
13
|
-
return number;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const line = (width: number, height: number) => {
|
|
17
|
-
const path = (shape as any)
|
|
18
|
-
.line()
|
|
19
|
-
.x((d: {x: any}) => d.x)
|
|
20
|
-
.y((d: {y: any}) => d.y)([
|
|
21
|
-
{x: width / 2, y: scale(CIRCLE_OVER_TOP)},
|
|
22
|
-
{x: 0, y: scale(CIRCLE_OVER_TOP)},
|
|
23
|
-
{x: 0, y: height},
|
|
24
|
-
{x: width, y: height},
|
|
25
|
-
{x: width, y: scale(CIRCLE_OVER_TOP)},
|
|
26
|
-
{x: width / 2, y: scale(CIRCLE_OVER_TOP)},
|
|
27
|
-
]);
|
|
28
|
-
//M195,30L0,30L0,106L390,106L390,30L195,30
|
|
29
|
-
// MaxApi.copyToClipboard(path, 'copied');
|
|
30
|
-
return path;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
//** Path Line Border Left Right Up */
|
|
34
|
-
const lineBorder = (width: number, height: number) => {
|
|
35
|
-
const border = (shape as any)
|
|
36
|
-
.line()
|
|
37
|
-
.x((d: {x: any}) => d.x)
|
|
38
|
-
.y((d: {y: any}) => d.y)
|
|
39
|
-
.curve(shape.curveBasis)([
|
|
40
|
-
// right
|
|
41
|
-
{x: width / 2, y: scale(30)},
|
|
42
|
-
{x: width - scale(20), y: scale(30)},
|
|
43
|
-
{x: width - scale(10), y: scale(32)},
|
|
44
|
-
{x: width - scale(2), y: scale(40)},
|
|
45
|
-
{x: width, y: scale(50)},
|
|
46
|
-
{x: width, y: height},
|
|
47
|
-
{x: width, y: height},
|
|
48
|
-
// bottom
|
|
49
|
-
{x: width, y: height},
|
|
50
|
-
{x: 0, y: height},
|
|
51
|
-
// left
|
|
52
|
-
{x: 0, y: height},
|
|
53
|
-
{x: 0, y: height},
|
|
54
|
-
{x: 0, y: scale(50)},
|
|
55
|
-
{x: 0 + scale(2), y: scale(40)},
|
|
56
|
-
{x: 0 + scale(10), y: scale(32)},
|
|
57
|
-
{x: 0 + scale(20), y: scale(30)},
|
|
58
|
-
{x: width / 2, y: scale(30)},
|
|
59
|
-
]);
|
|
60
|
-
|
|
61
|
-
return border;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
//** Path Line Border Left Right Down */
|
|
65
|
-
const lineBorderLeft = (width: number, height: number) => {
|
|
66
|
-
const border = (shape as any)
|
|
67
|
-
.line()
|
|
68
|
-
.x((d: {x: any}) => d.x)
|
|
69
|
-
.y((d: {y: any}) => d.y)
|
|
70
|
-
.curve(shape.curveBasis)([
|
|
71
|
-
// right
|
|
72
|
-
{x: width, y: scale(30)},
|
|
73
|
-
{x: width, y: height},
|
|
74
|
-
{x: width, y: height},
|
|
75
|
-
// bottom
|
|
76
|
-
{x: width, y: height},
|
|
77
|
-
{x: 0, y: height},
|
|
78
|
-
// left
|
|
79
|
-
{x: 0, y: height},
|
|
80
|
-
{x: 0, y: height},
|
|
81
|
-
{x: 0, y: scale(50)},
|
|
82
|
-
{x: 0 + scale(2), y: scale(40)},
|
|
83
|
-
{x: 0 + scale(10), y: scale(32)},
|
|
84
|
-
{x: 0 + scale(20), y: scale(30)},
|
|
85
|
-
{x: width / 2, y: scale(30)},
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
return border;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
//** Path Line Border Left Right Down */
|
|
92
|
-
const lineBorderRight = (width: number, height: number) => {
|
|
93
|
-
const border = (shape as any)
|
|
94
|
-
.line()
|
|
95
|
-
.x((d: {x: any}) => d.x)
|
|
96
|
-
.y((d: {y: any}) => d.y)
|
|
97
|
-
.curve(shape.curveBasis)([
|
|
98
|
-
// right
|
|
99
|
-
{x: width / 2, y: scale(30)},
|
|
100
|
-
{x: width - scale(20), y: scale(30)},
|
|
101
|
-
{x: width - scale(10), y: scale(32)},
|
|
102
|
-
{x: width - scale(2), y: scale(40)},
|
|
103
|
-
{x: width, y: scale(50)},
|
|
104
|
-
{x: width, y: height},
|
|
105
|
-
{x: width, y: height},
|
|
106
|
-
// bottom
|
|
107
|
-
{x: width, y: height},
|
|
108
|
-
{x: 0, y: height},
|
|
109
|
-
// left
|
|
110
|
-
{x: 0, y: height},
|
|
111
|
-
{x: 0, y: height},
|
|
112
|
-
{x: 0, y: scale(30)},
|
|
113
|
-
]);
|
|
114
|
-
|
|
115
|
-
return border;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
//** Path Curved Up */
|
|
119
|
-
const lineCurved = (position: number, circle: number) => {
|
|
120
|
-
const circleR = circle / 2;
|
|
121
|
-
const circleRightPos = circle + position;
|
|
122
|
-
const circleOverR = circle / 2 + CIRCLE_OVER_SIZE;
|
|
123
|
-
const centerX = (position + circleRightPos) / 2; //195
|
|
124
|
-
const centerY = CIRCLE_OVER_SIZE + circleR; //33
|
|
125
|
-
// {"circle":50,"position":170,"centerX":195,"circleWidth":220}
|
|
126
|
-
// WIDTH_SCREEN: 390
|
|
127
|
-
|
|
128
|
-
const curved = (shape as any)
|
|
129
|
-
.line()
|
|
130
|
-
.x((d: {x: any}) => d.x)
|
|
131
|
-
.y((d: {y: any}) => d.y)
|
|
132
|
-
.curve(shape.curveBundle.beta(1))([
|
|
133
|
-
{x: centerX - circleOverR - scale(50), y: CIRCLE_OVER_TOP},
|
|
134
|
-
{x: centerX - circleOverR, y: CIRCLE_OVER_TOP}, //y 20
|
|
135
|
-
{
|
|
136
|
-
x: centerX + circleOverR * Math.cos(-Math.PI / 2 - Math.PI / 4),
|
|
137
|
-
y: centerY + circleOverR * Math.sin(-Math.PI / 2 - Math.PI / 4),
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
{x: centerX, y: 0},
|
|
141
|
-
|
|
142
|
-
{
|
|
143
|
-
x: centerX + circleOverR * Math.cos(-Math.PI / 2 + Math.PI / 4),
|
|
144
|
-
y: centerY + circleOverR * Math.sin(-Math.PI / 2 + Math.PI / 4),
|
|
145
|
-
},
|
|
146
|
-
{x: centerX + circleOverR, y: CIRCLE_OVER_TOP}, //y 20
|
|
147
|
-
{x: centerX + circleOverR + scale(50), y: CIRCLE_OVER_TOP},
|
|
148
|
-
]);
|
|
149
|
-
|
|
150
|
-
return curved;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
export const getPathUp = (
|
|
154
|
-
width: number,
|
|
155
|
-
iHeight: number,
|
|
156
|
-
centerWidth = 50,
|
|
157
|
-
borderTopLeftRight = false,
|
|
158
|
-
position: 'CENTER' | 'LEFT' | 'RIGHT',
|
|
159
|
-
) => {
|
|
160
|
-
const height = scale(iHeight);
|
|
161
|
-
const circleWidth = scale(centerWidth);
|
|
162
|
-
|
|
163
|
-
if (borderTopLeftRight && position === 'LEFT') {
|
|
164
|
-
return `${lineBorderRight(width, height)} ${lineCurved(
|
|
165
|
-
circleWidth / 1.7,
|
|
166
|
-
circleWidth,
|
|
167
|
-
)}`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (borderTopLeftRight && position === 'RIGHT') {
|
|
171
|
-
return `${lineBorderLeft(width, height)} ${lineCurved(
|
|
172
|
-
width - circleWidth * 1.6,
|
|
173
|
-
circleWidth,
|
|
174
|
-
)}`;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (borderTopLeftRight && position === 'CENTER') {
|
|
178
|
-
return `${lineBorder(width, height)} ${lineCurved(
|
|
179
|
-
width / 2 - circleWidth / 2,
|
|
180
|
-
circleWidth,
|
|
181
|
-
)}`;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (position === 'LEFT') {
|
|
185
|
-
return `${line(width, height)} ${lineCurved(
|
|
186
|
-
circleWidth / 1.7,
|
|
187
|
-
circleWidth,
|
|
188
|
-
)}`;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (position === 'RIGHT') {
|
|
192
|
-
return `${line(width, height)} ${lineCurved(
|
|
193
|
-
width - circleWidth * 1.6,
|
|
194
|
-
circleWidth,
|
|
195
|
-
)}`;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return `${line(width, height)} ${lineCurved(
|
|
199
|
-
width / 2 - circleWidth / 2,
|
|
200
|
-
circleWidth,
|
|
201
|
-
)}`;
|
|
202
|
-
};
|