@momo-kits/foundation 0.79.6-beta.7 → 0.80.1
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 +2 -1
- package/Application/NavigationContainer.tsx +3 -0
- package/Application/StackScreen.tsx +2 -2
- package/Input/Input.tsx +11 -4
- 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/Screen.tsx +4 -6
- package/Layout/Section.tsx +4 -1
- package/Layout/utils.ts +20 -37
- package/package.json +1 -1
- 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
|
);
|
|
@@ -241,11 +241,12 @@ const styles = StyleSheet.create({
|
|
|
241
241
|
indicatorContainer: {
|
|
242
242
|
width: '100%',
|
|
243
243
|
paddingTop: Spacing.S,
|
|
244
|
+
paddingBottom: Spacing.XS,
|
|
244
245
|
alignItems: 'center',
|
|
245
246
|
justifyContent: 'center',
|
|
246
247
|
},
|
|
247
248
|
indicator: {
|
|
248
|
-
width:
|
|
249
|
+
width: 40,
|
|
249
250
|
height: 4,
|
|
250
251
|
borderRadius: Radius.S,
|
|
251
252
|
},
|
|
@@ -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/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,7 +101,15 @@ 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`}
|
|
@@ -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/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/package.json
CHANGED
|
Binary file
|