@momo-kits/foundation 1.0.1 → 1.0.3
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/Button/index.tsx +39 -22
- package/CheckBox/index.tsx +10 -4
- package/CheckBox/types.ts +6 -3
- package/Consts/theme.ts +57 -1
- package/ContentLoader/index.tsx +1 -2
- package/Icon/index.tsx +3 -0
- package/IconButton/index.tsx +24 -4
- package/Image/index.tsx +4 -4
- package/Input/Input.tsx +161 -0
- package/Input/TextArea.tsx +15 -55
- package/Input/common.tsx +70 -0
- package/Input/index.tsx +23 -197
- package/Layout/GridSystem.tsx +1 -1
- package/Layout/ScreenContainer.tsx +6 -0
- package/Layout/ScreenSection.tsx +13 -0
- package/Layout/SectionItem.tsx +3 -0
- package/Layout/utils.ts +14 -0
- package/Navigation/Components.tsx +35 -10
- package/Navigation/ModalScreen.tsx +53 -38
- package/Navigation/Navigation.ts +3 -0
- package/Navigation/NavigationButton.tsx +5 -2
- package/Navigation/NavigationContainer.tsx +8 -46
- package/Navigation/StackScreen.tsx +2 -2
- package/Navigation/types.ts +20 -3
- package/Navigation/utils.tsx +30 -9
- package/Playground/index.tsx +132 -0
- package/Playground/styles.ts +16 -0
- package/Playground/types.ts +15 -0
- package/Popup/PopupNotify.tsx +210 -0
- package/Popup/PopupPromotion.tsx +66 -0
- package/Popup/index.tsx +4 -0
- package/Popup/types.ts +23 -0
- package/Radio/index.tsx +13 -5
- package/Radio/styles.ts +1 -0
- package/Radio/types.ts +3 -3
- package/Switch/index.tsx +2 -4
- package/Switch/types.ts +2 -2
- package/Text/index.tsx +9 -7
- package/Text/styles.ts +3 -3
- package/Text/types.ts +5 -14
- package/index.ts +6 -6
- package/package.json +3 -4
- package/publish.sh +6 -8
- package/ActivityIndicator.tsx +0 -244
- package/Button/types.ts +0 -27
- package/IconButton/types.ts +0 -16
- package/Input/types.ts +0 -23
package/Button/index.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, {FC, useContext} from 'react';
|
|
2
2
|
import {
|
|
3
|
-
GestureResponderEvent,
|
|
4
3
|
StyleSheet,
|
|
5
4
|
TouchableOpacity,
|
|
5
|
+
TouchableOpacityProps,
|
|
6
6
|
View,
|
|
7
7
|
} from 'react-native';
|
|
8
8
|
import {ApplicationContext} from '../Navigation';
|
|
@@ -10,25 +10,43 @@ import {Text} from '../Text';
|
|
|
10
10
|
import {Typography} from '../Text/types';
|
|
11
11
|
import {Colors} from '../Consts';
|
|
12
12
|
import styles from './styles';
|
|
13
|
-
import {ButtonProps} from './types';
|
|
14
13
|
import {Image} from '../Image';
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
export interface ButtonProps extends TouchableOpacityProps {
|
|
16
|
+
type?:
|
|
17
|
+
| 'primary'
|
|
18
|
+
| 'secondary'
|
|
19
|
+
| 'tonal'
|
|
20
|
+
| 'outline'
|
|
21
|
+
| 'danger'
|
|
22
|
+
| 'text'
|
|
23
|
+
| 'disabled';
|
|
24
|
+
size?: 'large' | 'medium' | 'small';
|
|
25
|
+
full?: boolean;
|
|
26
|
+
iconRight?: string;
|
|
27
|
+
iconLeft?: string;
|
|
28
|
+
title: string;
|
|
29
|
+
useTintColor?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const Button: FC<ButtonProps> = ({
|
|
33
|
+
type,
|
|
34
|
+
size,
|
|
35
|
+
useTintColor,
|
|
36
|
+
full,
|
|
37
|
+
iconRight,
|
|
38
|
+
iconLeft,
|
|
39
|
+
title,
|
|
40
|
+
onPress,
|
|
41
|
+
...rest
|
|
42
|
+
}) => {
|
|
17
43
|
const {theme} = useContext(ApplicationContext);
|
|
18
|
-
const {type, size, useTintColor, full, iconRight, iconLeft, title, onPress} =
|
|
19
|
-
props;
|
|
20
44
|
|
|
21
|
-
/**
|
|
22
|
-
* export size style
|
|
23
|
-
*/
|
|
24
45
|
const getSizeStyle = () => {
|
|
25
46
|
const styleSheet: {[key: string]: any} = styles;
|
|
26
47
|
return styleSheet[size ?? 'small'];
|
|
27
48
|
};
|
|
28
49
|
|
|
29
|
-
/**
|
|
30
|
-
* export type style
|
|
31
|
-
*/
|
|
32
50
|
const getTypeStyle = () => {
|
|
33
51
|
switch (type) {
|
|
34
52
|
case 'disabled':
|
|
@@ -39,13 +57,13 @@ const Button: FC<ButtonProps> = props => {
|
|
|
39
57
|
return {backgroundColor: theme.colors.primary};
|
|
40
58
|
case 'secondary':
|
|
41
59
|
return {
|
|
42
|
-
backgroundColor: theme.colors.background.
|
|
60
|
+
backgroundColor: theme.colors.background.surface,
|
|
43
61
|
borderWidth: 1,
|
|
44
62
|
borderColor: theme.colors.border.default,
|
|
45
63
|
};
|
|
46
64
|
case 'outline':
|
|
47
65
|
return {
|
|
48
|
-
backgroundColor:
|
|
66
|
+
backgroundColor: theme.colors.background.surface,
|
|
49
67
|
borderWidth: 1,
|
|
50
68
|
borderColor: theme.colors.primary,
|
|
51
69
|
};
|
|
@@ -58,9 +76,7 @@ const Button: FC<ButtonProps> = props => {
|
|
|
58
76
|
backgroundColor: theme.colors.error.primary,
|
|
59
77
|
};
|
|
60
78
|
case 'text':
|
|
61
|
-
return {
|
|
62
|
-
backgroundColor: Colors.black_01,
|
|
63
|
-
};
|
|
79
|
+
return {};
|
|
64
80
|
default:
|
|
65
81
|
return {backgroundColor: theme.colors.primary};
|
|
66
82
|
}
|
|
@@ -137,9 +153,9 @@ const Button: FC<ButtonProps> = props => {
|
|
|
137
153
|
};
|
|
138
154
|
|
|
139
155
|
/**
|
|
140
|
-
* render
|
|
156
|
+
* render title
|
|
141
157
|
*/
|
|
142
|
-
const
|
|
158
|
+
const renderTitle = () => {
|
|
143
159
|
const typography = getTypography();
|
|
144
160
|
const color = getTextColor();
|
|
145
161
|
return (
|
|
@@ -204,7 +220,7 @@ const Button: FC<ButtonProps> = props => {
|
|
|
204
220
|
full && {width: '100%'},
|
|
205
221
|
]);
|
|
206
222
|
|
|
207
|
-
const onPressButton = (e:
|
|
223
|
+
const onPressButton = (e: any) => {
|
|
208
224
|
if (type === 'disabled') {
|
|
209
225
|
return () => {};
|
|
210
226
|
}
|
|
@@ -212,14 +228,15 @@ const Button: FC<ButtonProps> = props => {
|
|
|
212
228
|
};
|
|
213
229
|
|
|
214
230
|
const activeOpacity = type === 'disabled' ? 0.75 : 0.5;
|
|
231
|
+
|
|
215
232
|
return (
|
|
216
233
|
<TouchableOpacity
|
|
217
|
-
{...
|
|
234
|
+
{...rest}
|
|
218
235
|
activeOpacity={activeOpacity}
|
|
219
236
|
onPress={onPressButton}
|
|
220
237
|
style={buttonStyle}>
|
|
221
238
|
{renderLeading()}
|
|
222
|
-
{
|
|
239
|
+
{renderTitle()}
|
|
223
240
|
{renderTrailing()}
|
|
224
241
|
</TouchableOpacity>
|
|
225
242
|
);
|
|
@@ -234,4 +251,4 @@ Button.defaultProps = {
|
|
|
234
251
|
useTintColor: true,
|
|
235
252
|
};
|
|
236
253
|
|
|
237
|
-
export
|
|
254
|
+
export {Button};
|
package/CheckBox/index.tsx
CHANGED
|
@@ -13,13 +13,15 @@ const IC_CHECKED_DEFAULT =
|
|
|
13
13
|
'https://img.mservice.com.vn/app/img/kits/checked_ic.png';
|
|
14
14
|
const CheckBox: FC<CheckBoxProps> = props => {
|
|
15
15
|
const {theme} = useContext(ApplicationContext);
|
|
16
|
-
const {value, disabled, onChange, label, indeterminated} = props;
|
|
16
|
+
const {value, disabled, onChange, style, label, indeterminated} = props;
|
|
17
17
|
|
|
18
18
|
const haveValue = value || indeterminated;
|
|
19
19
|
let borderColor = haveValue
|
|
20
20
|
? theme.colors.primary
|
|
21
21
|
: theme.colors.text.default;
|
|
22
|
-
let backgroundColor = haveValue
|
|
22
|
+
let backgroundColor = haveValue
|
|
23
|
+
? theme.colors.primary
|
|
24
|
+
: theme.colors.background.surface;
|
|
23
25
|
|
|
24
26
|
let iconSource = value ? IC_CHECKED_DEFAULT : undefined;
|
|
25
27
|
|
|
@@ -33,7 +35,7 @@ const CheckBox: FC<CheckBoxProps> = props => {
|
|
|
33
35
|
: theme.colors.border.disable;
|
|
34
36
|
backgroundColor = haveValue
|
|
35
37
|
? theme.colors.background.disable
|
|
36
|
-
:
|
|
38
|
+
: theme.colors.background.surface;
|
|
37
39
|
}
|
|
38
40
|
const checkboxStyle = {borderColor, backgroundColor, borderWidth: 1};
|
|
39
41
|
|
|
@@ -45,7 +47,7 @@ const CheckBox: FC<CheckBoxProps> = props => {
|
|
|
45
47
|
activeOpacity={0.8}
|
|
46
48
|
onPress={onChangeValue}
|
|
47
49
|
disabled={disabled}
|
|
48
|
-
style={styles.container}>
|
|
50
|
+
style={[style, styles.container]}>
|
|
49
51
|
<View style={[checkboxStyle, styles.checkbox]}>
|
|
50
52
|
<Image style={styles.icon} source={{uri: iconSource}} />
|
|
51
53
|
</View>
|
|
@@ -54,4 +56,8 @@ const CheckBox: FC<CheckBoxProps> = props => {
|
|
|
54
56
|
);
|
|
55
57
|
};
|
|
56
58
|
|
|
59
|
+
CheckBox.defaultProps = {
|
|
60
|
+
disabled: false,
|
|
61
|
+
};
|
|
62
|
+
|
|
57
63
|
export {CheckBox};
|
package/CheckBox/types.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import {ViewStyle} from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type CheckBoxProps = {
|
|
2
4
|
value: boolean;
|
|
3
5
|
disabled?: boolean;
|
|
4
6
|
label?: string;
|
|
5
7
|
onChange: (value: boolean) => void;
|
|
6
|
-
indeterminated
|
|
7
|
-
|
|
8
|
+
indeterminated?: boolean;
|
|
9
|
+
style?: ViewStyle;
|
|
10
|
+
};
|
package/Consts/theme.ts
CHANGED
|
@@ -57,9 +57,65 @@ const defaultTheme: Theme = {
|
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
const defaultDarkTheme: Theme = {
|
|
61
|
+
dark: true,
|
|
62
|
+
colors: {
|
|
63
|
+
primary: '#ff79b0',
|
|
64
|
+
secondary: '#ffacdc',
|
|
65
|
+
background: {
|
|
66
|
+
default: '#121212', // Dark background
|
|
67
|
+
surface: '#1e1e1e', // Slightly lighter surface background
|
|
68
|
+
tonal: '#171717', // Tonal background
|
|
69
|
+
pressed: '#1a1a1a', // Pressed state background
|
|
70
|
+
selected: '#1a1a1a', // Selected state background
|
|
71
|
+
disable: '#303030', // Disabled state background
|
|
72
|
+
},
|
|
73
|
+
text: {
|
|
74
|
+
default: '#ffffff', // White text for better contrast
|
|
75
|
+
secondary: '#b0b0b0', // Light gray secondary text
|
|
76
|
+
hint: '#727272', // Hint text color
|
|
77
|
+
disable: '#505050', // Disabled text color
|
|
78
|
+
},
|
|
79
|
+
border: {
|
|
80
|
+
default: '#2a2a2a', // Darker borders
|
|
81
|
+
disable: '#242424', // Disabled state border color
|
|
82
|
+
},
|
|
83
|
+
success: {
|
|
84
|
+
primary: Colors.green_03,
|
|
85
|
+
secondary: Colors.green_07,
|
|
86
|
+
container: Colors.green_08,
|
|
87
|
+
},
|
|
88
|
+
warning: {
|
|
89
|
+
primary: Colors.orange_03,
|
|
90
|
+
secondary: Colors.orange_07,
|
|
91
|
+
container: Colors.orange_08,
|
|
92
|
+
},
|
|
93
|
+
error: {
|
|
94
|
+
primary: Colors.red_03,
|
|
95
|
+
secondary: Colors.red_07,
|
|
96
|
+
container: Colors.red_08,
|
|
97
|
+
},
|
|
98
|
+
highlight: {
|
|
99
|
+
primary: Colors.mint_03,
|
|
100
|
+
secondary: Colors.mint_07,
|
|
101
|
+
container: Colors.mint_08,
|
|
102
|
+
},
|
|
103
|
+
interactive: {
|
|
104
|
+
primary: Colors.blue_03,
|
|
105
|
+
secondary: Colors.blue_07,
|
|
106
|
+
container: Colors.blue_08,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
font: 'SFProText',
|
|
110
|
+
assets: {
|
|
111
|
+
headerBackground:
|
|
112
|
+
'https://static.momocdn.net/app/img/app/img/header-background.png',
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
60
116
|
const defaultContext: Context = {
|
|
61
117
|
theme: defaultTheme,
|
|
62
118
|
navigator: undefined,
|
|
63
119
|
};
|
|
64
120
|
|
|
65
|
-
export {defaultContext, defaultTheme};
|
|
121
|
+
export {defaultContext, defaultTheme, defaultDarkTheme};
|
package/ContentLoader/index.tsx
CHANGED
|
@@ -5,11 +5,10 @@ import {ApplicationContext} from '../Navigation';
|
|
|
5
5
|
import LinearGradient from 'react-native-linear-gradient';
|
|
6
6
|
import {Styles} from '../Consts';
|
|
7
7
|
import styles from './styles';
|
|
8
|
-
const ContentLoader: React.FC<ContentLoaderTypes> =
|
|
8
|
+
const ContentLoader: React.FC<ContentLoaderTypes> = ({style}) => {
|
|
9
9
|
const {width} = useWindowDimensions();
|
|
10
10
|
const {theme} = useContext(ApplicationContext);
|
|
11
11
|
const beginShimmerPosition = useRef(new Animated.Value(-1)).current;
|
|
12
|
-
const {style} = props;
|
|
13
12
|
|
|
14
13
|
const shimmerColors = [
|
|
15
14
|
theme.colors.text.disable + '40',
|
package/Icon/index.tsx
CHANGED
|
@@ -5,6 +5,9 @@ import IconSources from './icon.json';
|
|
|
5
5
|
const Icon: React.FC<IconProps> = ({source, size, color}) => {
|
|
6
6
|
const {theme} = useContext(ApplicationContext);
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* get icon source maps or remote http
|
|
10
|
+
*/
|
|
8
11
|
const getIconSource = (): any => {
|
|
9
12
|
if (source && !source.includes('http')) {
|
|
10
13
|
let icon: {[key: string]: object} = IconSources;
|
package/IconButton/index.tsx
CHANGED
|
@@ -3,13 +3,19 @@ import {
|
|
|
3
3
|
GestureResponderEvent,
|
|
4
4
|
StyleSheet,
|
|
5
5
|
TouchableOpacity,
|
|
6
|
+
TouchableOpacityProps,
|
|
6
7
|
} from 'react-native';
|
|
7
|
-
import {IconButtonProps} from './types';
|
|
8
8
|
import {ApplicationContext} from '../Navigation';
|
|
9
9
|
import {Colors} from '../Consts';
|
|
10
10
|
import {Icon} from '../Icon';
|
|
11
11
|
import styles from './styles';
|
|
12
12
|
|
|
13
|
+
export interface IconButtonProps extends TouchableOpacityProps {
|
|
14
|
+
icon: string;
|
|
15
|
+
type?: 'primary' | 'tonal' | 'secondary' | 'danger' | 'outline' | 'disabled';
|
|
16
|
+
size?: 'large' | 'small';
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
const IconButton: React.FC<IconButtonProps> = ({
|
|
14
20
|
type,
|
|
15
21
|
icon,
|
|
@@ -19,12 +25,19 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
19
25
|
}) => {
|
|
20
26
|
const {theme} = useContext(ApplicationContext);
|
|
21
27
|
|
|
28
|
+
/**
|
|
29
|
+
* get size icon button
|
|
30
|
+
*/
|
|
22
31
|
const getSizeStyle = () => {
|
|
23
32
|
if (size === 'small') {
|
|
24
33
|
return styles.small;
|
|
25
34
|
}
|
|
26
35
|
return styles.large;
|
|
27
36
|
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* get style for icon button by type
|
|
40
|
+
*/
|
|
28
41
|
const getTypeStyle = () => {
|
|
29
42
|
switch (type) {
|
|
30
43
|
case 'disabled':
|
|
@@ -35,13 +48,13 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
35
48
|
return {backgroundColor: theme.colors.primary};
|
|
36
49
|
case 'secondary':
|
|
37
50
|
return {
|
|
38
|
-
backgroundColor:
|
|
51
|
+
backgroundColor: theme.colors.background.surface,
|
|
39
52
|
borderWidth: 1,
|
|
40
53
|
borderColor: theme.colors.border.default,
|
|
41
54
|
};
|
|
42
55
|
case 'outline':
|
|
43
56
|
return {
|
|
44
|
-
backgroundColor:
|
|
57
|
+
backgroundColor: theme.colors.background.surface,
|
|
45
58
|
borderWidth: 1,
|
|
46
59
|
borderColor: theme.colors.primary,
|
|
47
60
|
};
|
|
@@ -58,6 +71,9 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
58
71
|
}
|
|
59
72
|
};
|
|
60
73
|
|
|
74
|
+
/**
|
|
75
|
+
* get color for icon
|
|
76
|
+
*/
|
|
61
77
|
const getIconColor = () => {
|
|
62
78
|
switch (type) {
|
|
63
79
|
case 'disabled':
|
|
@@ -77,6 +93,10 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
77
93
|
}
|
|
78
94
|
};
|
|
79
95
|
|
|
96
|
+
/**
|
|
97
|
+
* handle press
|
|
98
|
+
* @param e
|
|
99
|
+
*/
|
|
80
100
|
const onPressButton = (e: GestureResponderEvent) => {
|
|
81
101
|
if (type === 'disabled') {
|
|
82
102
|
return () => {};
|
|
@@ -85,9 +105,9 @@ const IconButton: React.FC<IconButtonProps> = ({
|
|
|
85
105
|
};
|
|
86
106
|
|
|
87
107
|
const activeOpacity = type === 'disabled' ? 0.75 : 0.5;
|
|
88
|
-
|
|
89
108
|
const buttonStyle = StyleSheet.flatten([getTypeStyle(), getSizeStyle()]);
|
|
90
109
|
const iconSize = size === 'small' ? 16 : 24;
|
|
110
|
+
|
|
91
111
|
return (
|
|
92
112
|
<TouchableOpacity
|
|
93
113
|
{...rest}
|
package/Image/index.tsx
CHANGED
|
@@ -9,14 +9,13 @@ import {ContentLoader} from '../ContentLoader';
|
|
|
9
9
|
import {Icon} from '../Icon';
|
|
10
10
|
import {Styles} from '../Consts';
|
|
11
11
|
|
|
12
|
-
const Image: React.FC<ImageProps> =
|
|
12
|
+
const Image: React.FC<ImageProps> = ({style, placeholder, source, ...rest}) => {
|
|
13
13
|
const {theme} = useContext(ApplicationContext);
|
|
14
|
-
const {style, placeholder, source} = props;
|
|
15
14
|
const [loading, setLoading] = useState(typeof source === 'object');
|
|
16
15
|
const [fail, setFail] = useState(false);
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
|
-
* render content
|
|
18
|
+
* render content loading | fail | rendered
|
|
20
19
|
* @returns {JSX.Element}
|
|
21
20
|
*/
|
|
22
21
|
const renderContent = () => {
|
|
@@ -47,7 +46,8 @@ const Image: React.FC<ImageProps> = props => {
|
|
|
47
46
|
return (
|
|
48
47
|
<View style={[styles.container, style]}>
|
|
49
48
|
<FastImage
|
|
50
|
-
{...
|
|
49
|
+
{...rest}
|
|
50
|
+
source={source}
|
|
51
51
|
style={styles.image}
|
|
52
52
|
onLoad={() => {
|
|
53
53
|
setFail(false);
|
package/Input/Input.tsx
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import React, {FC, useContext, useRef, useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
NativeSyntheticEvent,
|
|
4
|
+
TextInput,
|
|
5
|
+
TextInputFocusEventData,
|
|
6
|
+
TextInputProps,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
View,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import {ApplicationContext} from '../Navigation';
|
|
11
|
+
import styles from './styles';
|
|
12
|
+
import {Text} from '../Text';
|
|
13
|
+
import {Image} from '../Image';
|
|
14
|
+
import {getBorderColor, renderFloatingView} from './common';
|
|
15
|
+
import {InputProps} from './index';
|
|
16
|
+
|
|
17
|
+
const errorMessageIcon =
|
|
18
|
+
'https://img.mservice.com.vn/app/img/kits/error_mess_icon.png';
|
|
19
|
+
const ic_clear =
|
|
20
|
+
'https://img.mservice.io/momo_app_v2/new_version/img/appx_icon/24_navigation_close_circle_full.png';
|
|
21
|
+
const MAX_LENGTH = 100;
|
|
22
|
+
|
|
23
|
+
const Input: FC<InputProps> = ({
|
|
24
|
+
value,
|
|
25
|
+
onChangeText,
|
|
26
|
+
floatingValue,
|
|
27
|
+
floatingIcon,
|
|
28
|
+
size,
|
|
29
|
+
placeholder,
|
|
30
|
+
onBlur,
|
|
31
|
+
onFocus,
|
|
32
|
+
errorMessage,
|
|
33
|
+
icon,
|
|
34
|
+
disabled,
|
|
35
|
+
floatingIconColor,
|
|
36
|
+
iconColor,
|
|
37
|
+
}) => {
|
|
38
|
+
const {theme} = useContext(ApplicationContext);
|
|
39
|
+
|
|
40
|
+
const [focused, setFocused] = useState(false);
|
|
41
|
+
const inputRef = useRef(null);
|
|
42
|
+
|
|
43
|
+
const onClearText = () => {
|
|
44
|
+
inputRef?.current?.clear();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const _onChangeText = (text: string) => {
|
|
48
|
+
onChangeText?.(text);
|
|
49
|
+
};
|
|
50
|
+
const getSizeStyle = () => {
|
|
51
|
+
if (size === 'small') {
|
|
52
|
+
return styles.smallContainer;
|
|
53
|
+
}
|
|
54
|
+
return styles.container;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
58
|
+
setFocused(true);
|
|
59
|
+
onFocus?.(e);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
63
|
+
setFocused(false);
|
|
64
|
+
onBlur?.(e);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const renderInputView = () => {
|
|
68
|
+
const disabledColor = theme.colors.text.disable;
|
|
69
|
+
let textColor = theme.colors.text.default;
|
|
70
|
+
let placeholderColor = theme.colors.text.hint;
|
|
71
|
+
let iconTintColor = iconColor;
|
|
72
|
+
|
|
73
|
+
if (disabled) {
|
|
74
|
+
textColor = disabledColor;
|
|
75
|
+
placeholderColor = disabledColor;
|
|
76
|
+
iconTintColor = disabledColor;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<View
|
|
81
|
+
style={[
|
|
82
|
+
getSizeStyle(),
|
|
83
|
+
getBorderColor(focused, errorMessage, disabled),
|
|
84
|
+
styles.inputWrapper,
|
|
85
|
+
]}>
|
|
86
|
+
{renderFloatingView(
|
|
87
|
+
floatingValue,
|
|
88
|
+
floatingIconColor,
|
|
89
|
+
disabled,
|
|
90
|
+
floatingIcon,
|
|
91
|
+
)}
|
|
92
|
+
<View style={styles.inputView}>
|
|
93
|
+
<TextInput
|
|
94
|
+
editable={!disabled}
|
|
95
|
+
textAlignVertical="top"
|
|
96
|
+
ref={inputRef}
|
|
97
|
+
style={[
|
|
98
|
+
styles.input,
|
|
99
|
+
{
|
|
100
|
+
color: textColor,
|
|
101
|
+
},
|
|
102
|
+
]}
|
|
103
|
+
value={value}
|
|
104
|
+
onChangeText={_onChangeText}
|
|
105
|
+
onFocus={_onFocus}
|
|
106
|
+
onBlur={_onBlur}
|
|
107
|
+
placeholder={placeholder}
|
|
108
|
+
selectionColor={theme.colors.primary}
|
|
109
|
+
placeholderTextColor={placeholderColor}
|
|
110
|
+
/>
|
|
111
|
+
</View>
|
|
112
|
+
<View style={styles.iconView}>
|
|
113
|
+
{focused && (
|
|
114
|
+
<TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
|
|
115
|
+
<Image
|
|
116
|
+
tintColor={theme.colors.text.hint}
|
|
117
|
+
source={{uri: ic_clear}}
|
|
118
|
+
style={styles.iconClose}
|
|
119
|
+
/>
|
|
120
|
+
</TouchableOpacity>
|
|
121
|
+
)}
|
|
122
|
+
{icon && (
|
|
123
|
+
<Image
|
|
124
|
+
tintColor={iconTintColor}
|
|
125
|
+
source={{uri: icon}}
|
|
126
|
+
style={styles.icon}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
</View>
|
|
130
|
+
</View>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const renderErrorView = () => {
|
|
135
|
+
if (errorMessage) {
|
|
136
|
+
return (
|
|
137
|
+
<View style={styles.errorView}>
|
|
138
|
+
<Image style={styles.errorIcon} source={{uri: errorMessageIcon}} />
|
|
139
|
+
<Text color={theme.colors.error.primary} typography={'description_s'}>
|
|
140
|
+
{errorMessage}
|
|
141
|
+
</Text>
|
|
142
|
+
</View>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<View style={styles.wrapper}>
|
|
149
|
+
{renderInputView()}
|
|
150
|
+
{renderErrorView()}
|
|
151
|
+
</View>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
Input.defaultProps = {
|
|
156
|
+
size: 'large',
|
|
157
|
+
maxLength: MAX_LENGTH,
|
|
158
|
+
disabled: false,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default Input;
|
package/Input/TextArea.tsx
CHANGED
|
@@ -3,15 +3,16 @@ import {
|
|
|
3
3
|
NativeSyntheticEvent,
|
|
4
4
|
TextInput,
|
|
5
5
|
TextInputFocusEventData,
|
|
6
|
+
TextInputProps,
|
|
6
7
|
TouchableOpacity,
|
|
7
8
|
View,
|
|
8
9
|
} from 'react-native';
|
|
9
10
|
import styles from './styles';
|
|
10
11
|
import {Image} from '../Image';
|
|
11
12
|
import {Text} from '../Text';
|
|
12
|
-
import {TextAreaProps} from './types';
|
|
13
13
|
import {ApplicationContext} from '../Navigation';
|
|
14
|
-
import {
|
|
14
|
+
import {getBorderColor, renderFloatingView} from './common';
|
|
15
|
+
import {TextAreaProps} from './index';
|
|
15
16
|
|
|
16
17
|
const errorMessageIcon =
|
|
17
18
|
'https://img.mservice.com.vn/app/img/kits/error_mess_icon.png';
|
|
@@ -19,6 +20,7 @@ const ic_clear =
|
|
|
19
20
|
'https://img.mservice.io/momo_app_v2/new_version/img/appx_icon/24_navigation_close_circle_full.png';
|
|
20
21
|
const DEFAULT_HEIGHT = 112;
|
|
21
22
|
const MAX_LENGTH = 300;
|
|
23
|
+
|
|
22
24
|
const TextArea: FC<TextAreaProps> = props => {
|
|
23
25
|
const {theme} = useContext(ApplicationContext);
|
|
24
26
|
const {
|
|
@@ -59,56 +61,6 @@ const TextArea: FC<TextAreaProps> = props => {
|
|
|
59
61
|
setFocused(false);
|
|
60
62
|
onBlur?.(e);
|
|
61
63
|
};
|
|
62
|
-
const getBorderColor = () => {
|
|
63
|
-
let borderColor = theme.colors.border.default;
|
|
64
|
-
|
|
65
|
-
if (focused) {
|
|
66
|
-
borderColor = theme.colors.primary;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!!errorMessage) {
|
|
70
|
-
borderColor = theme.colors.error.primary;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (disabled) {
|
|
74
|
-
borderColor = theme.colors.border.disable;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {borderColor};
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const renderFloatingView = () => {
|
|
81
|
-
if (floatingValue) {
|
|
82
|
-
let floatingTextColor = theme.colors.text.hint;
|
|
83
|
-
let floatingIconTintColor = floatingIconColor;
|
|
84
|
-
if (disabled) {
|
|
85
|
-
floatingTextColor = theme.colors.text.disable;
|
|
86
|
-
floatingIconTintColor = theme.colors.text.disable;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<View
|
|
91
|
-
style={[
|
|
92
|
-
styles.floatingView,
|
|
93
|
-
{
|
|
94
|
-
backgroundColor: Colors.black_01,
|
|
95
|
-
zIndex: 10,
|
|
96
|
-
},
|
|
97
|
-
]}>
|
|
98
|
-
<Text color={floatingTextColor} typography={'label_s'}>
|
|
99
|
-
{floatingValue}
|
|
100
|
-
</Text>
|
|
101
|
-
{floatingIcon && (
|
|
102
|
-
<Image
|
|
103
|
-
tintColor={floatingIconTintColor}
|
|
104
|
-
source={{uri: floatingIcon}}
|
|
105
|
-
style={styles.floatingIcon}
|
|
106
|
-
/>
|
|
107
|
-
)}
|
|
108
|
-
</View>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
64
|
|
|
113
65
|
const renderCountingView = () => {
|
|
114
66
|
return (
|
|
@@ -132,11 +84,19 @@ const TextArea: FC<TextAreaProps> = props => {
|
|
|
132
84
|
return (
|
|
133
85
|
<View
|
|
134
86
|
style={[
|
|
135
|
-
getBorderColor(),
|
|
87
|
+
getBorderColor(focused, errorMessage, disabled),
|
|
136
88
|
styles.textAreaWrapper,
|
|
137
|
-
{
|
|
89
|
+
{
|
|
90
|
+
height: height || DEFAULT_HEIGHT,
|
|
91
|
+
backgroundColor: theme.colors.background.surface,
|
|
92
|
+
},
|
|
138
93
|
]}>
|
|
139
|
-
{renderFloatingView(
|
|
94
|
+
{renderFloatingView(
|
|
95
|
+
floatingValue,
|
|
96
|
+
floatingIconColor,
|
|
97
|
+
disabled,
|
|
98
|
+
floatingIcon,
|
|
99
|
+
)}
|
|
140
100
|
<View style={styles.rowArea}>
|
|
141
101
|
<View style={styles.textAreaView}>
|
|
142
102
|
<TextInput
|