@momo-kits/foundation 0.79.6-beta.7 → 0.80.1-beta.2
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/Components.tsx +11 -11
- package/Application/ModalScreen.tsx +8 -1
- package/Application/NavigationContainer.tsx +3 -0
- package/Application/StackScreen.tsx +2 -2
- package/Application/utils.tsx +0 -7
- package/Consts/theme.ts +3 -0
- package/Input/Input.tsx +12 -5
- package/Input/InputDropDown.tsx +127 -0
- package/Input/InputOTP.tsx +213 -0
- package/Input/common.tsx +4 -1
- package/Input/index.tsx +116 -7
- package/Input/styles.ts +26 -9
- package/Layout/Card.tsx +4 -1
- package/Layout/Screen.tsx +4 -6
- package/Layout/Section.tsx +4 -1
- package/Layout/utils.ts +20 -37
- package/Title/index.tsx +9 -9
- package/package.json +1 -1
- package/verify.js +7 -0
- package/Assets/header-background.png +0 -0
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
Alert,
|
|
4
4
|
Animated,
|
|
5
5
|
DeviceEventEmitter,
|
|
6
|
+
Platform,
|
|
6
7
|
StatusBar,
|
|
7
8
|
StyleSheet,
|
|
8
|
-
TouchableOpacity,
|
|
9
9
|
View,
|
|
10
|
+
TouchableOpacity as TouchableRN,
|
|
10
11
|
} from 'react-native';
|
|
12
|
+
import {TouchableOpacity as TouchableGesture} from 'react-native-gesture-handler';
|
|
11
13
|
import {ApplicationContext} from './index';
|
|
12
14
|
import {Colors, Radius, Spacing, Styles} from '../Consts';
|
|
13
15
|
import {Image} from '../Image';
|
|
@@ -22,6 +24,8 @@ import {scaleSize, Text} from '../Text';
|
|
|
22
24
|
import {Icon} from '../Icon';
|
|
23
25
|
import {PopupNotify} from '../Popup';
|
|
24
26
|
|
|
27
|
+
const Touchable = Platform.OS === 'android' ? TouchableGesture : TouchableRN;
|
|
28
|
+
|
|
25
29
|
const styles = StyleSheet.create({
|
|
26
30
|
navigationButton: {
|
|
27
31
|
height: 28,
|
|
@@ -101,7 +105,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
return (
|
|
104
|
-
<
|
|
108
|
+
<Touchable
|
|
105
109
|
accessibilityLabel={`NavigationButton/${accessibilityLabel}`}
|
|
106
110
|
style={[
|
|
107
111
|
styles.navigationButton,
|
|
@@ -116,7 +120,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
116
120
|
color={tintColor ?? theme.colors.text.default}
|
|
117
121
|
size={20}
|
|
118
122
|
/>
|
|
119
|
-
</
|
|
123
|
+
</Touchable>
|
|
120
124
|
);
|
|
121
125
|
};
|
|
122
126
|
|
|
@@ -392,21 +396,17 @@ const HeaderToolkitAction: React.FC<any> = ({tintColor}) => {
|
|
|
392
396
|
borderWidth: backgroundColor ? 0.5 : 0,
|
|
393
397
|
},
|
|
394
398
|
]}>
|
|
395
|
-
<
|
|
396
|
-
accessibilityLabel={'Toolkit/More'}
|
|
397
|
-
onPress={onMore}>
|
|
399
|
+
<Touchable accessibilityLabel={'Toolkit/More'} onPress={onMore}>
|
|
398
400
|
<Icon color={tintColor} source="navigation_more_horiz" size={20} />
|
|
399
|
-
</
|
|
401
|
+
</Touchable>
|
|
400
402
|
<View style={[styles.divider, {backgroundColor: tintColor}]} />
|
|
401
|
-
<
|
|
402
|
-
accessibilityLabel={`Toolkit/Close`}
|
|
403
|
-
onPress={onClose}>
|
|
403
|
+
<Touchable accessibilityLabel={`Toolkit/Close`} onPress={onClose}>
|
|
404
404
|
<Icon
|
|
405
405
|
color={tintColor}
|
|
406
406
|
source="16_navigation_close_circle"
|
|
407
407
|
size={20}
|
|
408
408
|
/>
|
|
409
|
-
</
|
|
409
|
+
</Touchable>
|
|
410
410
|
</View>
|
|
411
411
|
</View>
|
|
412
412
|
);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Pressable,
|
|
6
6
|
StyleSheet,
|
|
7
7
|
View,
|
|
8
|
+
Keyboard,
|
|
8
9
|
} from 'react-native';
|
|
9
10
|
import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
10
11
|
import {
|
|
@@ -115,9 +116,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
useEffect(() => {
|
|
119
|
+
const keyboardDidShow = Keyboard.addListener('keyboardDidShow', () =>
|
|
120
|
+
bottomSheetRef.current?.expand(),
|
|
121
|
+
);
|
|
122
|
+
|
|
118
123
|
bottomSheetRef.current?.present();
|
|
119
124
|
return () => {
|
|
120
125
|
mountedRef.current = false;
|
|
126
|
+
keyboardDidShow.remove();
|
|
121
127
|
};
|
|
122
128
|
}, []);
|
|
123
129
|
|
|
@@ -241,11 +247,12 @@ const styles = StyleSheet.create({
|
|
|
241
247
|
indicatorContainer: {
|
|
242
248
|
width: '100%',
|
|
243
249
|
paddingTop: Spacing.S,
|
|
250
|
+
paddingBottom: Spacing.XS,
|
|
244
251
|
alignItems: 'center',
|
|
245
252
|
justifyContent: 'center',
|
|
246
253
|
},
|
|
247
254
|
indicator: {
|
|
248
|
-
width:
|
|
255
|
+
width: 40,
|
|
249
256
|
height: 4,
|
|
250
257
|
borderRadius: Radius.S,
|
|
251
258
|
},
|
|
@@ -42,6 +42,9 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
42
42
|
}, []);
|
|
43
43
|
|
|
44
44
|
useEffect(() => {
|
|
45
|
+
maxApi?.getLanguage?.((lang: 'vi' | 'en') => {
|
|
46
|
+
setLanguage(lang);
|
|
47
|
+
});
|
|
45
48
|
const listener = maxApi?.observer?.('language', (lang: 'vi' | 'en') => {
|
|
46
49
|
setLanguage(lang);
|
|
47
50
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {useContext,
|
|
1
|
+
import React, {useContext, useLayoutEffect} from 'react';
|
|
2
2
|
import {ScreenParams} from './types';
|
|
3
3
|
import Navigation from './Navigation';
|
|
4
4
|
import {ApplicationContext} from './index';
|
|
@@ -16,7 +16,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
16
16
|
|
|
17
17
|
delete params.screen;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
20
|
if (options) {
|
|
21
21
|
navigation.setOptions(options);
|
|
22
22
|
}
|
package/Application/utils.tsx
CHANGED
|
@@ -139,13 +139,6 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
|
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
if (params?.headerRight == undefined) {
|
|
143
|
-
options = {
|
|
144
|
-
...options,
|
|
145
|
-
headerRight: (props: any) => <HeaderToolkitAction {...props} />,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
142
|
if (params.hiddenBack) {
|
|
150
143
|
options = {
|
|
151
144
|
...options,
|
package/Consts/theme.ts
CHANGED
package/Input/Input.tsx
CHANGED
|
@@ -13,7 +13,6 @@ import {InputProps} from './index';
|
|
|
13
13
|
import {Icon} from '../Icon';
|
|
14
14
|
import {checkTyping} from './utils';
|
|
15
15
|
import {Loader} from '../Loader';
|
|
16
|
-
import {Image} from '../Image';
|
|
17
16
|
|
|
18
17
|
const Input = forwardRef(
|
|
19
18
|
(
|
|
@@ -34,7 +33,8 @@ const Input = forwardRef(
|
|
|
34
33
|
required,
|
|
35
34
|
errorSpacing,
|
|
36
35
|
loading,
|
|
37
|
-
|
|
36
|
+
leadingIcon,
|
|
37
|
+
leadingIconColor,
|
|
38
38
|
...props
|
|
39
39
|
}: InputProps,
|
|
40
40
|
ref,
|
|
@@ -78,7 +78,6 @@ const Input = forwardRef(
|
|
|
78
78
|
let textColor = theme.colors.text.default;
|
|
79
79
|
let placeholderColor = theme.colors.text.hint;
|
|
80
80
|
let iconTintColor = iconColor;
|
|
81
|
-
const imageStyle = size === 'small' ? styles.imageSmall : styles.image;
|
|
82
81
|
|
|
83
82
|
if (disabled) {
|
|
84
83
|
textColor = disabledColor;
|
|
@@ -102,12 +101,20 @@ const Input = forwardRef(
|
|
|
102
101
|
floatingIcon={floatingIcon}
|
|
103
102
|
/>
|
|
104
103
|
<View style={styles.inputView}>
|
|
105
|
-
{!!
|
|
104
|
+
{!!leadingIcon && (
|
|
105
|
+
<View style={styles.leadingIconContainer}>
|
|
106
|
+
<Icon
|
|
107
|
+
color={leadingIconColor}
|
|
108
|
+
source={leadingIcon}
|
|
109
|
+
size={size === 'small' ? 24 : 32}
|
|
110
|
+
/>
|
|
111
|
+
</View>
|
|
112
|
+
)}
|
|
106
113
|
<TextInput
|
|
107
114
|
{...props}
|
|
108
115
|
accessibilityLabel={`TextInput`}
|
|
109
116
|
editable={!disabled}
|
|
110
|
-
textAlignVertical="
|
|
117
|
+
textAlignVertical="center"
|
|
111
118
|
ref={inputRef}
|
|
112
119
|
style={[
|
|
113
120
|
styles.input,
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React, {forwardRef, useContext, useRef} from 'react';
|
|
2
|
+
import {TextInput, TouchableOpacity, View} from 'react-native';
|
|
3
|
+
import {ApplicationContext} from '../Application';
|
|
4
|
+
import styles from './styles';
|
|
5
|
+
import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
|
|
6
|
+
import {InputDropDownProps} from './index';
|
|
7
|
+
import {Icon} from '../Icon';
|
|
8
|
+
import {Loader} from '../Loader';
|
|
9
|
+
import {Styles} from '../Consts';
|
|
10
|
+
|
|
11
|
+
const InputDropDown = forwardRef(
|
|
12
|
+
(
|
|
13
|
+
{
|
|
14
|
+
value,
|
|
15
|
+
floatingValue,
|
|
16
|
+
floatingIcon,
|
|
17
|
+
size,
|
|
18
|
+
onPress,
|
|
19
|
+
placeholder,
|
|
20
|
+
errorMessage,
|
|
21
|
+
icon = 'arrow_chevron_down_small',
|
|
22
|
+
disabled,
|
|
23
|
+
floatingIconColor,
|
|
24
|
+
iconColor,
|
|
25
|
+
required,
|
|
26
|
+
errorSpacing,
|
|
27
|
+
loading,
|
|
28
|
+
leadingIcon,
|
|
29
|
+
leadingIconColor,
|
|
30
|
+
...props
|
|
31
|
+
}: InputDropDownProps,
|
|
32
|
+
ref,
|
|
33
|
+
) => {
|
|
34
|
+
const {theme} = useContext(ApplicationContext);
|
|
35
|
+
const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
|
|
36
|
+
const full = !!value || !!placeholder;
|
|
37
|
+
const renderIcon = (color: string | undefined) => {
|
|
38
|
+
if (loading) {
|
|
39
|
+
return <Loader type={'spinner'} color={color} />;
|
|
40
|
+
}
|
|
41
|
+
if (icon) {
|
|
42
|
+
return <Icon color={color} source={icon} size={24} />;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const renderInputView = () => {
|
|
47
|
+
const disabledColor = theme.colors.text.disable;
|
|
48
|
+
let textColor = theme.colors.text.default;
|
|
49
|
+
let placeholderColor = theme.colors.text.hint;
|
|
50
|
+
let iconTintColor = iconColor;
|
|
51
|
+
|
|
52
|
+
if (disabled) {
|
|
53
|
+
textColor = disabledColor;
|
|
54
|
+
placeholderColor = disabledColor;
|
|
55
|
+
iconTintColor = disabledColor;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<View
|
|
60
|
+
style={[
|
|
61
|
+
styles.inputWrapper,
|
|
62
|
+
{backgroundColor: theme.colors.background.surface},
|
|
63
|
+
getSizeStyle(size),
|
|
64
|
+
getBorderColor(theme, false, errorMessage, disabled),
|
|
65
|
+
]}>
|
|
66
|
+
<FloatingView
|
|
67
|
+
floatingValue={floatingValue}
|
|
68
|
+
floatingIconColor={floatingIconColor}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
required={required}
|
|
71
|
+
floatingIcon={floatingIcon}
|
|
72
|
+
/>
|
|
73
|
+
<View style={full ? styles.inputView : Styles.row}>
|
|
74
|
+
{!!leadingIcon && (
|
|
75
|
+
<View style={styles.leadingIconContainer}>
|
|
76
|
+
<Icon
|
|
77
|
+
color={leadingIconColor}
|
|
78
|
+
source={leadingIcon}
|
|
79
|
+
size={size === 'small' ? 24 : 32}
|
|
80
|
+
/>
|
|
81
|
+
</View>
|
|
82
|
+
)}
|
|
83
|
+
{full && (
|
|
84
|
+
<TextInput
|
|
85
|
+
{...props}
|
|
86
|
+
onPressIn={onPress}
|
|
87
|
+
accessibilityLabel={`TextInput`}
|
|
88
|
+
editable={false}
|
|
89
|
+
textAlignVertical="top"
|
|
90
|
+
ref={inputRef}
|
|
91
|
+
style={[
|
|
92
|
+
styles.input,
|
|
93
|
+
{
|
|
94
|
+
color: textColor,
|
|
95
|
+
fontFamily: `${theme.font}-Regular`,
|
|
96
|
+
},
|
|
97
|
+
]}
|
|
98
|
+
textBreakStrategy="highQuality"
|
|
99
|
+
value={value}
|
|
100
|
+
placeholder={placeholder}
|
|
101
|
+
selectionColor={theme.colors.primary}
|
|
102
|
+
placeholderTextColor={placeholderColor}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
</View>
|
|
106
|
+
<View style={styles.iconView}>{renderIcon(iconTintColor)}</View>
|
|
107
|
+
</View>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<TouchableOpacity
|
|
113
|
+
onPress={onPress}
|
|
114
|
+
style={full && styles.wrapper}
|
|
115
|
+
accessibilityLabel={`InputDropDown/${floatingValue}`}>
|
|
116
|
+
{renderInputView()}
|
|
117
|
+
<ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
|
|
118
|
+
</TouchableOpacity>
|
|
119
|
+
);
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
InputDropDown.defaultProps = {
|
|
124
|
+
size: 'small',
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export default InputDropDown;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React, {FC, useContext, useEffect, useRef, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
KeyboardType,
|
|
5
|
+
NativeSyntheticEvent,
|
|
6
|
+
TextInput,
|
|
7
|
+
TextInputFocusEventData,
|
|
8
|
+
TouchableOpacity,
|
|
9
|
+
View,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import {CaretProps, InputOTPProps} from './index';
|
|
12
|
+
import {scaleSize, Text} from '../Text';
|
|
13
|
+
import styles from './styles';
|
|
14
|
+
import {ErrorView, getBorderColor} from './common';
|
|
15
|
+
import {ApplicationContext} from '../Application';
|
|
16
|
+
import {Spacing} from '../Consts';
|
|
17
|
+
|
|
18
|
+
const OTPCaret: FC<CaretProps> = ({index, length}) => {
|
|
19
|
+
const DURATION = 300;
|
|
20
|
+
const {theme} = useContext(ApplicationContext);
|
|
21
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
Animated.loop(
|
|
25
|
+
Animated.sequence([
|
|
26
|
+
Animated.timing(opacity, {
|
|
27
|
+
toValue: 1,
|
|
28
|
+
duration: DURATION,
|
|
29
|
+
useNativeDriver: true,
|
|
30
|
+
}),
|
|
31
|
+
Animated.delay(DURATION * 2),
|
|
32
|
+
Animated.timing(opacity, {
|
|
33
|
+
toValue: 0,
|
|
34
|
+
duration: DURATION,
|
|
35
|
+
useNativeDriver: true,
|
|
36
|
+
}),
|
|
37
|
+
]),
|
|
38
|
+
).start();
|
|
39
|
+
}, []);
|
|
40
|
+
const spacingStyle = !isNaN(Number(length)) &&
|
|
41
|
+
index !== Number(length) - 1 && {marginRight: Spacing.L};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View style={[{flexDirection: 'row', alignItems: 'center'}, spacingStyle]}>
|
|
45
|
+
<Animated.View
|
|
46
|
+
style={{
|
|
47
|
+
height: scaleSize(12),
|
|
48
|
+
width: 1,
|
|
49
|
+
backgroundColor: theme.colors.primary,
|
|
50
|
+
opacity,
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
<Text color={theme.colors.text.hint} typography={'paragraph_default'}>
|
|
54
|
+
-
|
|
55
|
+
</Text>
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const InputOTP: FC<InputOTPProps> = ({
|
|
61
|
+
length,
|
|
62
|
+
errorMessage,
|
|
63
|
+
errorSpacing,
|
|
64
|
+
floatingValue,
|
|
65
|
+
floatingIconColor,
|
|
66
|
+
floatingIcon,
|
|
67
|
+
placeholder,
|
|
68
|
+
onChangeText,
|
|
69
|
+
onFocus,
|
|
70
|
+
onBlur,
|
|
71
|
+
dataType = 'number',
|
|
72
|
+
...props
|
|
73
|
+
}) => {
|
|
74
|
+
const MAX_LENGTH = 10;
|
|
75
|
+
const [value, setValue] = useState('');
|
|
76
|
+
const [focused, setFocused] = useState(false);
|
|
77
|
+
const inputRef = useRef<TextInput>(null);
|
|
78
|
+
const {theme} = useContext(ApplicationContext);
|
|
79
|
+
|
|
80
|
+
const onFocusInput = () => {
|
|
81
|
+
setFocused(true);
|
|
82
|
+
inputRef.current?.focus();
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
86
|
+
setFocused(false);
|
|
87
|
+
onBlur?.(e);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const _onChangeText = (text: string) => {
|
|
91
|
+
if (text.length > MAX_LENGTH) return;
|
|
92
|
+
if (dataType === 'number' && isNaN(Number(text))) return;
|
|
93
|
+
if (
|
|
94
|
+
!isNaN(Number(text)) &&
|
|
95
|
+
value.length >= Number(length) &&
|
|
96
|
+
text.length >= value.length
|
|
97
|
+
)
|
|
98
|
+
return;
|
|
99
|
+
|
|
100
|
+
setValue(text);
|
|
101
|
+
onChangeText?.(text);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const renderInputs = (length: number) => {
|
|
105
|
+
const TextInputs = [];
|
|
106
|
+
for (let i = 0; i < length; i++) {
|
|
107
|
+
TextInputs.push(
|
|
108
|
+
<View>
|
|
109
|
+
{focused && value.length === i ? (
|
|
110
|
+
<OTPCaret index={i} length={length} />
|
|
111
|
+
) : (
|
|
112
|
+
<Text
|
|
113
|
+
style={i !== length - 1 && {marginRight: Spacing.L}}
|
|
114
|
+
color={
|
|
115
|
+
!!value[i] ? theme.colors.text.default : theme.colors.text.hint
|
|
116
|
+
}
|
|
117
|
+
typography={!!value[i] ? 'title_s' : 'paragraph_default'}>
|
|
118
|
+
{value[i] || '-'}
|
|
119
|
+
</Text>
|
|
120
|
+
)}
|
|
121
|
+
</View>,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return TextInputs;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const renderUnidentifiedInputs = () => {
|
|
128
|
+
const splitValue = value.split('');
|
|
129
|
+
|
|
130
|
+
if (!focused && value.length === 0 && !!placeholder) {
|
|
131
|
+
return (
|
|
132
|
+
<Text color={theme.colors.text.hint} typography={'description_s'}>
|
|
133
|
+
{placeholder}
|
|
134
|
+
</Text>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<View style={{flexDirection: 'row'}}>
|
|
140
|
+
{splitValue.map((letter, index) => {
|
|
141
|
+
return (
|
|
142
|
+
<Text
|
|
143
|
+
style={
|
|
144
|
+
(index !== value.length - 1 ||
|
|
145
|
+
(focused && value.length !== MAX_LENGTH)) && {
|
|
146
|
+
marginRight: Spacing.L,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
typography={'title_s'}>
|
|
150
|
+
{letter}
|
|
151
|
+
</Text>
|
|
152
|
+
);
|
|
153
|
+
})}
|
|
154
|
+
{focused && value.length !== MAX_LENGTH && <OTPCaret />}
|
|
155
|
+
</View>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const renderInputView = () => {
|
|
160
|
+
let placeholderColor = theme.colors.text.hint;
|
|
161
|
+
const keyboardType: KeyboardType =
|
|
162
|
+
dataType === 'number' ? 'number-pad' : 'default';
|
|
163
|
+
return (
|
|
164
|
+
<View
|
|
165
|
+
style={[
|
|
166
|
+
styles.otpInput,
|
|
167
|
+
{backgroundColor: theme.colors.background.surface},
|
|
168
|
+
getBorderColor(theme, focused, errorMessage),
|
|
169
|
+
]}>
|
|
170
|
+
{!!floatingValue && (
|
|
171
|
+
<View
|
|
172
|
+
style={[
|
|
173
|
+
styles.otpFloatingView,
|
|
174
|
+
{backgroundColor: theme.colors.background.surface},
|
|
175
|
+
]}>
|
|
176
|
+
<Text typography={'description_s'}>{floatingValue}</Text>
|
|
177
|
+
</View>
|
|
178
|
+
)}
|
|
179
|
+
<View style={styles.otpInputsView}>
|
|
180
|
+
{length ? renderInputs(length) : renderUnidentifiedInputs()}
|
|
181
|
+
</View>
|
|
182
|
+
<View style={styles.inputView}>
|
|
183
|
+
<TextInput
|
|
184
|
+
{...props}
|
|
185
|
+
accessibilityLabel={`TextInput`}
|
|
186
|
+
ref={inputRef}
|
|
187
|
+
value={value}
|
|
188
|
+
onChangeText={_onChangeText}
|
|
189
|
+
keyboardType={keyboardType}
|
|
190
|
+
onFocus={onFocusInput}
|
|
191
|
+
onBlur={onBlurInput}
|
|
192
|
+
style={{opacity: 0}}
|
|
193
|
+
selectionColor={theme.colors.primary}
|
|
194
|
+
placeholderTextColor={placeholderColor}
|
|
195
|
+
/>
|
|
196
|
+
</View>
|
|
197
|
+
</View>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<TouchableOpacity
|
|
203
|
+
activeOpacity={1}
|
|
204
|
+
onPress={onFocusInput}
|
|
205
|
+
style={styles.otpWrapper}
|
|
206
|
+
accessibilityLabel={`Input/${floatingValue}`}>
|
|
207
|
+
{renderInputView()}
|
|
208
|
+
<ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
|
|
209
|
+
</TouchableOpacity>
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export default InputOTP;
|
package/Input/common.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {View} from 'react-native';
|
|
1
|
+
import {View, ViewStyle} from 'react-native';
|
|
2
2
|
import styles from './styles';
|
|
3
3
|
import {Text} from '../Text';
|
|
4
4
|
import React, {FC, useContext} from 'react';
|
|
@@ -13,6 +13,7 @@ type FloatingViewProps = {
|
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
floatingIcon?: string;
|
|
15
15
|
required?: boolean;
|
|
16
|
+
style?: ViewStyle;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
export const DEFAULT_HEIGHT = 112;
|
|
@@ -81,6 +82,7 @@ export const FloatingView: FC<FloatingViewProps> = ({
|
|
|
81
82
|
disabled,
|
|
82
83
|
floatingIcon,
|
|
83
84
|
required,
|
|
85
|
+
style,
|
|
84
86
|
}) => {
|
|
85
87
|
const {theme} = useContext(ApplicationContext);
|
|
86
88
|
|
|
@@ -100,6 +102,7 @@ export const FloatingView: FC<FloatingViewProps> = ({
|
|
|
100
102
|
backgroundColor: theme.colors.background.surface,
|
|
101
103
|
zIndex: 10,
|
|
102
104
|
},
|
|
105
|
+
style,
|
|
103
106
|
]}>
|
|
104
107
|
<Text color={floatingTextColor} typography={'label_s'}>
|
|
105
108
|
{floatingValue}
|
package/Input/index.tsx
CHANGED
|
@@ -2,6 +2,8 @@ import Input from './Input';
|
|
|
2
2
|
import InputTextArea from './InputTextArea';
|
|
3
3
|
import InputSearch from './InputSearch';
|
|
4
4
|
import InputMoney from './InputMoney';
|
|
5
|
+
import InputOTP from './InputOTP';
|
|
6
|
+
import InputDropDown from './InputDropDown';
|
|
5
7
|
import {TextInputProps} from 'react-native';
|
|
6
8
|
|
|
7
9
|
export interface InputProps extends TextInputProps {
|
|
@@ -30,6 +32,11 @@ export interface InputProps extends TextInputProps {
|
|
|
30
32
|
*/
|
|
31
33
|
icon?: string;
|
|
32
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Optional. Represents the color of the icon in the Input component.
|
|
37
|
+
*/
|
|
38
|
+
iconColor?: string;
|
|
39
|
+
|
|
33
40
|
/**
|
|
34
41
|
* Optional. If `true`, the user won't be able to interact with the Input component.
|
|
35
42
|
* Defaults to `false` if not provided.
|
|
@@ -41,11 +48,6 @@ export interface InputProps extends TextInputProps {
|
|
|
41
48
|
*/
|
|
42
49
|
floatingIconColor?: string;
|
|
43
50
|
|
|
44
|
-
/**
|
|
45
|
-
* Optional. Represents the color of the icon in the Input component.
|
|
46
|
-
*/
|
|
47
|
-
iconColor?: string;
|
|
48
|
-
|
|
49
51
|
/**
|
|
50
52
|
* Optional. If `true`, the Input component is marked as required,
|
|
51
53
|
* indicating that the user must provide a value before submitting a form.
|
|
@@ -65,13 +67,22 @@ export interface InputProps extends TextInputProps {
|
|
|
65
67
|
*/
|
|
66
68
|
loading?: boolean;
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Optional. Represents the leading icon in the Input component.
|
|
72
|
+
*/
|
|
73
|
+
leadingIcon?: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Optional. Represents the color of the leading icon in the Input component.
|
|
77
|
+
*/
|
|
78
|
+
leadingIconColor?: string;
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
type InputPropsWithoutSizeAndIcon = Omit<
|
|
72
82
|
InputProps,
|
|
73
83
|
'size' | 'icon' | 'iconColor'
|
|
74
84
|
>;
|
|
85
|
+
|
|
75
86
|
type InputPropsOmitForSearch = Omit<
|
|
76
87
|
InputProps,
|
|
77
88
|
'required' | 'size' | 'errorMessage' | 'disabled'
|
|
@@ -109,4 +120,102 @@ export interface InputSearchProps extends InputPropsOmitForSearch {
|
|
|
109
120
|
|
|
110
121
|
export interface InputMoneyProps extends InputPropsWithoutPlaceholder {}
|
|
111
122
|
|
|
112
|
-
export
|
|
123
|
+
export type OTPInputLength = 2 | 4 | 6 | 8 | 10;
|
|
124
|
+
|
|
125
|
+
export interface InputOTPProps extends InputPropsWithoutSizeAndIcon {
|
|
126
|
+
length?: OTPInputLength;
|
|
127
|
+
dataType?: 'string' | 'number';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type CaretProps = {
|
|
131
|
+
index?: number;
|
|
132
|
+
length?: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type InputDropDownProps = {
|
|
136
|
+
/**
|
|
137
|
+
* Optional. Defines the size of the Input component.
|
|
138
|
+
*/
|
|
139
|
+
size?: 'small' | 'large';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Optional. Defines the value of InputDropDown.
|
|
143
|
+
*/
|
|
144
|
+
value?: string;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Optional. Defines the placeholder of InputDropDown.
|
|
148
|
+
*/
|
|
149
|
+
placeholder?: string;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Optional. Defines the onPress of InputDropDown.
|
|
153
|
+
*/
|
|
154
|
+
onPress?: () => void;
|
|
155
|
+
/**
|
|
156
|
+
* Optional. Represents the value for the floating title in the Input component.
|
|
157
|
+
*/
|
|
158
|
+
floatingValue?: string;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Optional. Represents the name or key of the floating icon to be displayed in the Input component.
|
|
162
|
+
*/
|
|
163
|
+
floatingIcon?: string;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Optional. Represents the error message to be displayed below the Input component when there is an error.
|
|
167
|
+
*/
|
|
168
|
+
errorMessage?: string;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Optional. Represents the name or key of the icon to be displayed in the Input component.
|
|
172
|
+
*/
|
|
173
|
+
icon?: string;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Optional. Represents the color of the icon in the Input component.
|
|
177
|
+
*/
|
|
178
|
+
iconColor?: string;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Optional. If `true`, the user won't be able to interact with the Input component.
|
|
182
|
+
* Defaults to `false` if not provided.
|
|
183
|
+
*/
|
|
184
|
+
disabled?: boolean;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Optional. Represents the color of the floating icon in the Input component.
|
|
188
|
+
*/
|
|
189
|
+
floatingIconColor?: string;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Optional. If `true`, the Input component is marked as required,
|
|
193
|
+
* indicating that the user must provide a value before submitting a form.
|
|
194
|
+
* Defaults to `false` if not provided.
|
|
195
|
+
*/
|
|
196
|
+
required?: boolean;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Optional. If `true`,
|
|
200
|
+
* include spacing when Input not have error message.
|
|
201
|
+
* Defaults to `false` if not provided.
|
|
202
|
+
*/
|
|
203
|
+
errorSpacing?: boolean;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* If true, the icon of input will use be show indicator loading.
|
|
207
|
+
*/
|
|
208
|
+
loading?: boolean;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Optional. Represents the leading icon in the Input component.
|
|
212
|
+
*/
|
|
213
|
+
leadingIcon?: string;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Optional. Represents the color of the leading icon in the Input component.
|
|
217
|
+
*/
|
|
218
|
+
leadingIconColor?: string;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export {Input, InputTextArea, InputSearch, InputMoney, InputOTP, InputDropDown};
|
package/Input/styles.ts
CHANGED
|
@@ -42,17 +42,10 @@ export default StyleSheet.create({
|
|
|
42
42
|
flexDirection: 'row',
|
|
43
43
|
alignItems: 'center',
|
|
44
44
|
},
|
|
45
|
-
|
|
46
|
-
width: 32,
|
|
47
|
-
height: 32,
|
|
48
|
-
marginLeft: Spacing.M,
|
|
49
|
-
borderRadius: Radius.XS,
|
|
50
|
-
},
|
|
51
|
-
imageSmall: {
|
|
52
|
-
width: 24,
|
|
53
|
-
height: 24,
|
|
45
|
+
leadingIconContainer: {
|
|
54
46
|
marginLeft: Spacing.M,
|
|
55
47
|
borderRadius: Radius.XS,
|
|
48
|
+
overflow: 'hidden',
|
|
56
49
|
},
|
|
57
50
|
iconView: {
|
|
58
51
|
flexDirection: 'row',
|
|
@@ -142,4 +135,28 @@ export default StyleSheet.create({
|
|
|
142
135
|
paddingVertical: Spacing.S,
|
|
143
136
|
fontWeight: 'bold',
|
|
144
137
|
},
|
|
138
|
+
|
|
139
|
+
//OTP
|
|
140
|
+
otpWrapper: {
|
|
141
|
+
width: '100%',
|
|
142
|
+
marginTop: Spacing.M,
|
|
143
|
+
},
|
|
144
|
+
otpInput: {
|
|
145
|
+
height: scaleSize(56),
|
|
146
|
+
borderRadius: Radius.S,
|
|
147
|
+
borderWidth: 1,
|
|
148
|
+
},
|
|
149
|
+
otpFloatingView: {
|
|
150
|
+
position: 'absolute',
|
|
151
|
+
top: -Spacing.S,
|
|
152
|
+
alignSelf: 'center',
|
|
153
|
+
paddingHorizontal: Spacing.S,
|
|
154
|
+
},
|
|
155
|
+
otpInputsView: {
|
|
156
|
+
width: '100%',
|
|
157
|
+
height: '100%',
|
|
158
|
+
flexDirection: 'row',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
justifyContent: 'center',
|
|
161
|
+
},
|
|
145
162
|
});
|
package/Layout/Card.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {useContext} from 'react';
|
|
1
|
+
import React, {Fragment, useContext} from 'react';
|
|
2
2
|
import {Dimensions, View, ViewProps} from 'react-native';
|
|
3
3
|
import {useGridSystem, validateChildren} from './utils';
|
|
4
4
|
import {GridContextProps} from './types';
|
|
@@ -120,6 +120,9 @@ const Card: React.FC<CardProps> = ({children, style, backgroundImage}) => {
|
|
|
120
120
|
}
|
|
121
121
|
} else {
|
|
122
122
|
element.push(children);
|
|
123
|
+
if (children?.type == Fragment) {
|
|
124
|
+
return renderView(children?.props.children);
|
|
125
|
+
}
|
|
123
126
|
}
|
|
124
127
|
return element;
|
|
125
128
|
};
|
package/Layout/Screen.tsx
CHANGED
|
@@ -107,12 +107,10 @@ const Screen: React.FC<ScreenProps> = ({
|
|
|
107
107
|
if (useExtendedHeader) {
|
|
108
108
|
title = {headerTitle: HeaderTitle};
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
...title,
|
|
115
|
-
});
|
|
110
|
+
navigation?.setOptions({
|
|
111
|
+
animatedValue: animatedValue.current,
|
|
112
|
+
headerTransparent: headerTransparent,
|
|
113
|
+
...title,
|
|
116
114
|
});
|
|
117
115
|
}
|
|
118
116
|
}, [useExtendedHeader, useAnimatedHeader]);
|
package/Layout/Section.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {useContext} from 'react';
|
|
1
|
+
import React, {Fragment, useContext} from 'react';
|
|
2
2
|
import {Dimensions, View, ViewProps} from 'react-native';
|
|
3
3
|
import {useGridSystem, validateChildren} from './utils';
|
|
4
4
|
import {GridContextProps} from './types';
|
|
@@ -135,6 +135,9 @@ const Section: React.FC<SectionProps> = ({
|
|
|
135
135
|
}
|
|
136
136
|
} else {
|
|
137
137
|
element.push(children);
|
|
138
|
+
if (children?.type == Fragment) {
|
|
139
|
+
return renderView(children?.props.children);
|
|
140
|
+
}
|
|
138
141
|
}
|
|
139
142
|
return element;
|
|
140
143
|
};
|
package/Layout/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {Alert, Dimensions} from 'react-native';
|
|
2
|
-
import React, {useEffect, useState} from 'react';
|
|
2
|
+
import React, {Fragment, ReactElement, useEffect, useState} from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* validate children type
|
|
@@ -7,53 +7,36 @@ import React, {useEffect, useState} from 'react';
|
|
|
7
7
|
* @param childrenTypes
|
|
8
8
|
*/
|
|
9
9
|
const validateChildren = (
|
|
10
|
-
children:
|
|
11
|
-
|
|
12
|
-
| string
|
|
13
|
-
| number
|
|
14
|
-
| {}
|
|
15
|
-
| Iterable<React.ReactNode>
|
|
16
|
-
| React.ReactPortal
|
|
17
|
-
| null
|
|
18
|
-
| undefined
|
|
19
|
-
| boolean,
|
|
20
|
-
childrenTypes: Array<React.FC<any>>,
|
|
10
|
+
children: React.ReactNode | React.ReactNode[],
|
|
11
|
+
childrenTypes: Array<React.FC>,
|
|
21
12
|
) => {
|
|
22
13
|
if (!__DEV__) return children;
|
|
23
14
|
|
|
24
|
-
let results: React.ReactElement[] = [];
|
|
25
15
|
const validateType = (child: React.ReactElement) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const alertWrongType = () => {
|
|
30
|
-
Alert.alert(
|
|
31
|
-
'Component Kits',
|
|
32
|
-
`Wrong children type for this component, expected are ${childrenTypes
|
|
33
|
-
.map(type => type.displayName)
|
|
34
|
-
.join(', ')}.`,
|
|
16
|
+
const valid = childrenTypes.some(
|
|
17
|
+
childrenType => child?.type === childrenType || child?.type == Fragment,
|
|
35
18
|
);
|
|
19
|
+
if (!valid) {
|
|
20
|
+
Alert.alert(
|
|
21
|
+
'Component Kits',
|
|
22
|
+
`Wrong children type for this component, expected are ${childrenTypes
|
|
23
|
+
.map(type => type.displayName)
|
|
24
|
+
.join(', ')}.`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return valid;
|
|
36
28
|
};
|
|
37
29
|
|
|
38
30
|
if (Array.isArray(children)) {
|
|
39
31
|
const flattenChildren: React.ReactElement[] = children.flat();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return child;
|
|
43
|
-
} else {
|
|
44
|
-
alertWrongType();
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
} else {
|
|
48
|
-
const singleChild: any = children;
|
|
49
|
-
if (validateType(singleChild)) {
|
|
50
|
-
results = [singleChild];
|
|
51
|
-
} else {
|
|
52
|
-
alertWrongType();
|
|
32
|
+
for (const child of flattenChildren) {
|
|
33
|
+
validateType(child);
|
|
53
34
|
}
|
|
35
|
+
return flattenChildren;
|
|
36
|
+
} else {
|
|
37
|
+
validateType(children as ReactElement);
|
|
38
|
+
return children;
|
|
54
39
|
}
|
|
55
|
-
|
|
56
|
-
return results;
|
|
57
40
|
};
|
|
58
41
|
|
|
59
42
|
const useGridSystem = () => {
|
package/Title/index.tsx
CHANGED
|
@@ -26,20 +26,20 @@ const Title: FC<TitleProps> = ({
|
|
|
26
26
|
const actionColor = titleFlex
|
|
27
27
|
? theme.colors.primary
|
|
28
28
|
: theme.colors.text.default;
|
|
29
|
-
const actionBackground = titleFlex
|
|
29
|
+
const actionBackground = titleFlex
|
|
30
|
+
? theme.colors.primary + '0F'
|
|
31
|
+
: Colors.black_06;
|
|
30
32
|
const actionSize = titleFlex ? scaleSize(22) : scaleSize(18);
|
|
31
33
|
const titleWeight = type === 'screen' ? 'Semibold' : 'Bold';
|
|
32
34
|
const marginStyle = type === 'section' ? {marginTop: Spacing.M} : {};
|
|
33
35
|
|
|
34
36
|
const renderAction = () => {
|
|
35
37
|
return !!actionTitle && titleFlex ? (
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
title={actionTitle}
|
|
42
|
-
/>
|
|
38
|
+
<TouchableOpacity onPress={onPressAction}>
|
|
39
|
+
<Text typography={'action_s'} color={theme.colors.primary}>
|
|
40
|
+
{actionTitle}
|
|
41
|
+
</Text>
|
|
42
|
+
</TouchableOpacity>
|
|
43
43
|
) : (
|
|
44
44
|
<TouchableOpacity
|
|
45
45
|
style={[
|
|
@@ -76,7 +76,7 @@ const Title: FC<TitleProps> = ({
|
|
|
76
76
|
</RNText>
|
|
77
77
|
{showAction && type !== 'screen' && renderAction()}
|
|
78
78
|
</View>
|
|
79
|
-
{!!description &&
|
|
79
|
+
{!!description && type !== 'screen' && (
|
|
80
80
|
<Text typography={'description_s'} color={theme.colors.text.secondary}>
|
|
81
81
|
{description}
|
|
82
82
|
</Text>
|
package/package.json
CHANGED
package/verify.js
CHANGED
|
@@ -11,7 +11,14 @@ try {
|
|
|
11
11
|
const iOSAppTarget = appInfo?.client?.ios.deploymentTarget;
|
|
12
12
|
const androidAppTarget = appInfo?.client?.android.deploymentTarget;
|
|
13
13
|
|
|
14
|
+
if (appInfo.name === 'MoMoPlatform') {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
if (packageVersion > iOSAppTarget || packageVersion > androidAppTarget) {
|
|
19
|
+
console.error(
|
|
20
|
+
`Package ${packageInfo.name} version: ${packageInfo.version} require deploymentTarget ${packageVersion}`,
|
|
21
|
+
);
|
|
15
22
|
throw new Error(
|
|
16
23
|
`Package ${packageInfo.name} version: ${packageInfo.version} require deploymentTarget ${packageVersion}`,
|
|
17
24
|
);
|
|
Binary file
|