@momo-kits/foundation 1.0.0 → 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 +118 -171
- package/CheckBox/index.tsx +63 -0
- package/CheckBox/styles.ts +14 -0
- package/CheckBox/types.ts +10 -0
- package/Consts/colors+spacing+radius.ts +6 -4
- package/Consts/index.ts +4 -73
- package/Consts/styles.ts +1 -1
- package/Consts/theme.ts +121 -0
- package/ContentLoader/index.tsx +9 -13
- package/Icon/index.tsx +14 -11
- package/Icon/types.ts +1 -4
- package/IconButton/index.tsx +67 -67
- package/IconButton/styles.ts +19 -0
- package/Image/index.tsx +22 -21
- package/Image/types.ts +0 -1
- package/Input/Input.tsx +161 -0
- package/Input/TextArea.tsx +162 -0
- package/Input/common.tsx +70 -0
- package/Input/index.tsx +26 -0
- package/Input/styles.ts +92 -0
- package/Layout/GridSystem.tsx +109 -0
- package/Layout/ScreenContainer.tsx +84 -0
- package/Layout/ScreenSection.tsx +117 -0
- package/Layout/SectionItem.tsx +63 -0
- package/Layout/index.ts +11 -5
- package/Layout/types.ts +6 -33
- package/Layout/utils.ts +69 -23
- package/Navigation/Components.tsx +42 -9
- package/Navigation/ModalScreen.tsx +65 -40
- package/Navigation/Navigation.ts +5 -2
- package/Navigation/NavigationButton.tsx +10 -5
- package/Navigation/NavigationContainer.tsx +17 -13
- package/Navigation/StackScreen.tsx +8 -2
- package/Navigation/index.ts +5 -3
- package/Navigation/types.ts +72 -38
- package/Navigation/utils.tsx +37 -14
- 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 +42 -0
- package/Radio/styles.ts +12 -0
- package/Radio/types.ts +7 -0
- package/Switch/index.tsx +35 -0
- package/Switch/styles.ts +23 -0
- package/Switch/types.ts +5 -0
- package/Text/index.tsx +36 -118
- package/Text/styles.ts +24 -23
- package/Text/types.ts +5 -12
- package/index.ts +21 -4
- package/package.json +3 -4
- package/Button/types.ts +0 -18
- package/CheckBox/index.js +0 -74
- package/CheckBox/styles.js +0 -3
- package/IconButton/types.ts +0 -17
- package/Layout/Row.tsx +0 -42
- package/Layout/Screen.tsx +0 -68
- package/Layout/Section.tsx +0 -30
- package/Layout/View.tsx +0 -84
- package/Layout/styles.ts +0 -24
- package/Navigation/ScreenContainer.tsx +0 -38
- package/SizedBox/index.js +0 -23
- package/SizedBox/styles.js +0 -7
- package/TextInput/index.js +0 -225
- package/TextInput/styles.js +0 -55
package/Consts/theme.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {Context, Theme} from '../Navigation/types';
|
|
2
|
+
import {Colors} from './colors+spacing+radius';
|
|
3
|
+
|
|
4
|
+
const defaultTheme: Theme = {
|
|
5
|
+
dark: false,
|
|
6
|
+
colors: {
|
|
7
|
+
primary: Colors.pink_03,
|
|
8
|
+
secondary: Colors.pink_07,
|
|
9
|
+
background: {
|
|
10
|
+
default: '#f2f2f6',
|
|
11
|
+
surface: Colors.black_01,
|
|
12
|
+
tonal: Colors.pink_09,
|
|
13
|
+
pressed: '#fef4fa',
|
|
14
|
+
selected: Colors.pink_10,
|
|
15
|
+
disable: '#ebebf2',
|
|
16
|
+
},
|
|
17
|
+
text: {
|
|
18
|
+
default: Colors.black_17,
|
|
19
|
+
secondary: Colors.black_15,
|
|
20
|
+
hint: Colors.black_12,
|
|
21
|
+
disable: Colors.black_08,
|
|
22
|
+
},
|
|
23
|
+
border: {
|
|
24
|
+
default: Colors.black_04,
|
|
25
|
+
disable: Colors.black_02,
|
|
26
|
+
},
|
|
27
|
+
success: {
|
|
28
|
+
primary: Colors.green_03,
|
|
29
|
+
secondary: Colors.green_07,
|
|
30
|
+
container: Colors.green_08,
|
|
31
|
+
},
|
|
32
|
+
warning: {
|
|
33
|
+
primary: Colors.orange_03,
|
|
34
|
+
secondary: Colors.orange_07,
|
|
35
|
+
container: Colors.orange_08,
|
|
36
|
+
},
|
|
37
|
+
error: {
|
|
38
|
+
primary: Colors.red_03,
|
|
39
|
+
secondary: Colors.red_07,
|
|
40
|
+
container: Colors.red_08,
|
|
41
|
+
},
|
|
42
|
+
highlight: {
|
|
43
|
+
primary: Colors.mint_03,
|
|
44
|
+
secondary: Colors.mint_07,
|
|
45
|
+
container: Colors.mint_08,
|
|
46
|
+
},
|
|
47
|
+
interactive: {
|
|
48
|
+
primary: Colors.blue_03,
|
|
49
|
+
secondary: Colors.blue_07,
|
|
50
|
+
container: Colors.blue_08,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
font: 'SFProText',
|
|
54
|
+
assets: {
|
|
55
|
+
headerBackground:
|
|
56
|
+
'https://static.momocdn.net/app/img/app/img/header-background.png',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
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
|
+
|
|
116
|
+
const defaultContext: Context = {
|
|
117
|
+
theme: defaultTheme,
|
|
118
|
+
navigator: undefined,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export {defaultContext, defaultTheme, defaultDarkTheme};
|
package/ContentLoader/index.tsx
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import React, {useContext, useEffect, useMemo, useRef} from 'react';
|
|
2
2
|
import {Animated, Platform, useWindowDimensions, View} from 'react-native';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Styles,
|
|
8
|
-
} from '../index';
|
|
3
|
+
import {ContentLoaderTypes} from './types';
|
|
4
|
+
import {ApplicationContext} from '../Navigation';
|
|
5
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
6
|
+
import {Styles} from '../Consts';
|
|
9
7
|
import styles from './styles';
|
|
10
|
-
|
|
11
|
-
const ContentLoader: React.FC<ContentLoaderTypes> = props => {
|
|
8
|
+
const ContentLoader: React.FC<ContentLoaderTypes> = ({style}) => {
|
|
12
9
|
const {width} = useWindowDimensions();
|
|
13
|
-
const {theme} = useContext(
|
|
10
|
+
const {theme} = useContext(ApplicationContext);
|
|
14
11
|
const beginShimmerPosition = useRef(new Animated.Value(-1)).current;
|
|
15
|
-
const {style} = props;
|
|
16
12
|
|
|
17
13
|
const shimmerColors = [
|
|
18
|
-
theme.colors.
|
|
19
|
-
theme.colors.
|
|
20
|
-
theme.colors.
|
|
14
|
+
theme.colors.text.disable + '40',
|
|
15
|
+
theme.colors.text.disable + '80',
|
|
16
|
+
theme.colors.text.disable,
|
|
21
17
|
];
|
|
22
18
|
const location = [0.3, 0.5, 0.7];
|
|
23
19
|
const linearTranslate = beginShimmerPosition.interpolate({
|
package/Icon/index.tsx
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
import React, {useContext} from 'react';
|
|
2
|
-
import {IconProps, Image,
|
|
3
|
-
import
|
|
2
|
+
import {IconProps, Image, ApplicationContext} from '../index';
|
|
3
|
+
import IconSources from './icon.json';
|
|
4
4
|
|
|
5
|
-
const Icon: React.FC<IconProps> = ({
|
|
6
|
-
const {theme} = useContext(
|
|
5
|
+
const Icon: React.FC<IconProps> = ({source, size, color}) => {
|
|
6
|
+
const {theme} = useContext(ApplicationContext);
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* get icon source maps or remote http
|
|
10
|
+
*/
|
|
8
11
|
const getIconSource = (): any => {
|
|
9
|
-
if (
|
|
10
|
-
let icon: {[key: string]: object} =
|
|
11
|
-
return icon[
|
|
12
|
+
if (source && !source.includes('http')) {
|
|
13
|
+
let icon: {[key: string]: object} = IconSources;
|
|
14
|
+
return icon[source] ?? icon.ic_warning;
|
|
12
15
|
}
|
|
13
|
-
return source;
|
|
16
|
+
return {uri: source};
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
return (
|
|
17
20
|
<Image
|
|
18
21
|
source={getIconSource()}
|
|
19
22
|
style={{width: size, height: size}}
|
|
20
|
-
tintColor={color ?? theme.colors.text}
|
|
23
|
+
tintColor={color ?? theme.colors.text.default}
|
|
21
24
|
/>
|
|
22
25
|
);
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
Icon.defaultProps = {
|
|
26
|
-
|
|
29
|
+
source: 'ic_back',
|
|
27
30
|
size: 24,
|
|
28
31
|
};
|
|
29
32
|
|
|
30
|
-
export {Icon};
|
|
33
|
+
export {Icon, IconSources};
|
package/Icon/types.ts
CHANGED
package/IconButton/index.tsx
CHANGED
|
@@ -1,127 +1,127 @@
|
|
|
1
1
|
import React, {useContext} from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
GestureResponderEvent,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
TouchableOpacityProps,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import {ApplicationContext} from '../Navigation';
|
|
9
|
+
import {Colors} from '../Consts';
|
|
10
|
+
import {Icon} from '../Icon';
|
|
11
|
+
import styles from './styles';
|
|
12
|
+
|
|
13
|
+
export interface IconButtonProps extends TouchableOpacityProps {
|
|
14
|
+
icon: string;
|
|
15
|
+
type?: 'primary' | 'tonal' | 'secondary' | 'danger' | 'outline' | 'disabled';
|
|
16
|
+
size?: 'large' | 'small';
|
|
17
|
+
}
|
|
4
18
|
|
|
5
19
|
const IconButton: React.FC<IconButtonProps> = ({
|
|
6
|
-
style,
|
|
7
20
|
type,
|
|
8
21
|
icon,
|
|
9
22
|
size,
|
|
10
|
-
|
|
23
|
+
onPress,
|
|
11
24
|
...rest
|
|
12
25
|
}) => {
|
|
13
|
-
const {theme} = useContext(
|
|
26
|
+
const {theme} = useContext(ApplicationContext);
|
|
14
27
|
|
|
15
28
|
/**
|
|
16
|
-
*
|
|
29
|
+
* get size icon button
|
|
17
30
|
*/
|
|
18
31
|
const getSizeStyle = () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return styles.large;
|
|
22
|
-
case 'small':
|
|
23
|
-
return styles.small;
|
|
24
|
-
|
|
25
|
-
default:
|
|
26
|
-
return styles.small;
|
|
32
|
+
if (size === 'small') {
|
|
33
|
+
return styles.small;
|
|
27
34
|
}
|
|
35
|
+
return styles.large;
|
|
28
36
|
};
|
|
29
37
|
|
|
30
38
|
/**
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
const getIconSize = () => {
|
|
34
|
-
switch (size) {
|
|
35
|
-
case 'large':
|
|
36
|
-
return 24;
|
|
37
|
-
case 'small':
|
|
38
|
-
return 16;
|
|
39
|
-
|
|
40
|
-
default:
|
|
41
|
-
return 16;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* export type style
|
|
39
|
+
* get style for icon button by type
|
|
47
40
|
*/
|
|
48
41
|
const getTypeStyle = () => {
|
|
49
42
|
switch (type) {
|
|
43
|
+
case 'disabled':
|
|
44
|
+
return {
|
|
45
|
+
backgroundColor: theme.colors.background.disable,
|
|
46
|
+
};
|
|
50
47
|
case 'primary':
|
|
51
48
|
return {backgroundColor: theme.colors.primary};
|
|
52
|
-
case 'tonal':
|
|
53
|
-
return {backgroundColor: theme.colors.primary + '33'};
|
|
54
49
|
case 'secondary':
|
|
55
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
backgroundColor: theme.colors.background.surface,
|
|
52
|
+
borderWidth: 1,
|
|
53
|
+
borderColor: theme.colors.border.default,
|
|
54
|
+
};
|
|
56
55
|
case 'outline':
|
|
57
56
|
return {
|
|
57
|
+
backgroundColor: theme.colors.background.surface,
|
|
58
58
|
borderWidth: 1,
|
|
59
59
|
borderColor: theme.colors.primary,
|
|
60
60
|
};
|
|
61
|
-
case '
|
|
62
|
-
return {
|
|
61
|
+
case 'tonal':
|
|
62
|
+
return {
|
|
63
|
+
backgroundColor: theme.colors.background.tonal,
|
|
64
|
+
};
|
|
65
|
+
case 'danger':
|
|
66
|
+
return {
|
|
67
|
+
backgroundColor: theme.colors.error.primary,
|
|
68
|
+
};
|
|
63
69
|
default:
|
|
64
70
|
return {backgroundColor: theme.colors.primary};
|
|
65
71
|
}
|
|
66
72
|
};
|
|
67
73
|
|
|
74
|
+
/**
|
|
75
|
+
* get color for icon
|
|
76
|
+
*/
|
|
68
77
|
const getIconColor = () => {
|
|
69
78
|
switch (type) {
|
|
79
|
+
case 'disabled':
|
|
80
|
+
return theme.colors.text.disable;
|
|
70
81
|
case 'primary':
|
|
71
82
|
return Colors.black_01;
|
|
83
|
+
case 'danger':
|
|
84
|
+
return Colors.black_01;
|
|
72
85
|
case 'tonal':
|
|
73
86
|
return theme.colors.primary;
|
|
74
87
|
case 'secondary':
|
|
75
|
-
return theme.colors.text;
|
|
88
|
+
return theme.colors.text.default;
|
|
76
89
|
case 'outline':
|
|
77
90
|
return theme.colors.primary;
|
|
78
|
-
case 'disabled':
|
|
79
|
-
return theme.colors.text + '4D';
|
|
80
91
|
default:
|
|
81
92
|
return Colors.black_01;
|
|
82
93
|
}
|
|
83
94
|
};
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
/**
|
|
97
|
+
* handle press
|
|
98
|
+
* @param e
|
|
99
|
+
*/
|
|
100
|
+
const onPressButton = (e: GestureResponderEvent) => {
|
|
101
|
+
if (type === 'disabled') {
|
|
102
|
+
return () => {};
|
|
103
|
+
}
|
|
104
|
+
onPress?.(e);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const activeOpacity = type === 'disabled' ? 0.75 : 0.5;
|
|
108
|
+
const buttonStyle = StyleSheet.flatten([getTypeStyle(), getSizeStyle()]);
|
|
109
|
+
const iconSize = size === 'small' ? 16 : 24;
|
|
91
110
|
|
|
92
111
|
return (
|
|
93
112
|
<TouchableOpacity
|
|
94
113
|
{...rest}
|
|
95
|
-
|
|
114
|
+
activeOpacity={activeOpacity}
|
|
115
|
+
onPress={onPressButton}
|
|
96
116
|
style={buttonStyle}>
|
|
97
|
-
<Icon
|
|
117
|
+
<Icon size={iconSize} source={icon} color={getIconColor()} />
|
|
98
118
|
</TouchableOpacity>
|
|
99
119
|
);
|
|
100
120
|
};
|
|
101
121
|
|
|
102
122
|
IconButton.defaultProps = {
|
|
103
|
-
icon: 'close',
|
|
104
123
|
type: 'primary',
|
|
105
|
-
|
|
106
|
-
shape: 'circle',
|
|
107
|
-
children: <View />,
|
|
124
|
+
disabled: false,
|
|
108
125
|
};
|
|
109
126
|
|
|
110
|
-
const styles = StyleSheet.create({
|
|
111
|
-
large: {
|
|
112
|
-
height: 48,
|
|
113
|
-
width: 48,
|
|
114
|
-
borderRadius: 24,
|
|
115
|
-
justifyContent: 'center',
|
|
116
|
-
alignItems: 'center',
|
|
117
|
-
},
|
|
118
|
-
small: {
|
|
119
|
-
height: 36,
|
|
120
|
-
width: 36,
|
|
121
|
-
borderRadius: 18,
|
|
122
|
-
justifyContent: 'center',
|
|
123
|
-
alignItems: 'center',
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
|
|
127
127
|
export {IconButton};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {StyleSheet} from 'react-native';
|
|
2
|
+
import {Radius} from '../Consts';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
large: {
|
|
6
|
+
width: 48,
|
|
7
|
+
height: 48,
|
|
8
|
+
borderRadius: Radius.XL,
|
|
9
|
+
justifyContent: 'center',
|
|
10
|
+
alignItems: 'center',
|
|
11
|
+
},
|
|
12
|
+
small: {
|
|
13
|
+
width: 40,
|
|
14
|
+
height: 40,
|
|
15
|
+
borderRadius: Radius.XL,
|
|
16
|
+
justifyContent: 'center',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
},
|
|
19
|
+
});
|
package/Image/index.tsx
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import React, {useContext, useState} from 'react';
|
|
2
2
|
import {StyleSheet, View} from 'react-native';
|
|
3
3
|
import FastImage from 'react-native-fast-image';
|
|
4
|
-
|
|
5
|
-
ContentLoader,
|
|
6
|
-
Icon,
|
|
7
|
-
ImageProps,
|
|
8
|
-
NavigationContext,
|
|
9
|
-
Styles,
|
|
10
|
-
Text,
|
|
11
|
-
} from '../index';
|
|
4
|
+
|
|
12
5
|
import styles from './styles';
|
|
6
|
+
import {ImageProps} from './types';
|
|
7
|
+
import {ApplicationContext} from '../Navigation';
|
|
8
|
+
import {ContentLoader} from '../ContentLoader';
|
|
9
|
+
import {Icon} from '../Icon';
|
|
10
|
+
import {Styles} from '../Consts';
|
|
13
11
|
|
|
14
|
-
const Image: React.FC<ImageProps> =
|
|
15
|
-
const {theme} = useContext(
|
|
16
|
-
const {style, placeholder, error, source} = props;
|
|
12
|
+
const Image: React.FC<ImageProps> = ({style, placeholder, source, ...rest}) => {
|
|
13
|
+
const {theme} = useContext(ApplicationContext);
|
|
17
14
|
const [loading, setLoading] = useState(typeof source === 'object');
|
|
18
15
|
const [fail, setFail] = useState(false);
|
|
19
16
|
|
|
20
17
|
/**
|
|
21
|
-
* render content
|
|
18
|
+
* render content loading | fail | rendered
|
|
22
19
|
* @returns {JSX.Element}
|
|
23
20
|
*/
|
|
24
21
|
const renderContent = () => {
|
|
@@ -27,18 +24,21 @@ const Image: React.FC<ImageProps> = props => {
|
|
|
27
24
|
<ContentLoader style={[StyleSheet.absoluteFill, styles.image]} />
|
|
28
25
|
);
|
|
29
26
|
if (fail) {
|
|
30
|
-
content =
|
|
31
|
-
<View
|
|
32
|
-
|
|
33
|
-
<Icon name="error-outline" />
|
|
34
|
-
<Text typography="subtitle" style={Styles.textCenter}>
|
|
35
|
-
Can't load image
|
|
36
|
-
</Text>
|
|
27
|
+
content = (
|
|
28
|
+
<View style={Styles.flexCenter}>
|
|
29
|
+
<Icon source="error-outline" />
|
|
37
30
|
</View>
|
|
38
31
|
);
|
|
39
32
|
}
|
|
40
33
|
return (
|
|
41
|
-
<View
|
|
34
|
+
<View
|
|
35
|
+
style={[
|
|
36
|
+
StyleSheet.absoluteFill,
|
|
37
|
+
styles.image,
|
|
38
|
+
{backgroundColor: theme.colors.background.disable},
|
|
39
|
+
]}>
|
|
40
|
+
{content}
|
|
41
|
+
</View>
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
@@ -46,7 +46,8 @@ const Image: React.FC<ImageProps> = props => {
|
|
|
46
46
|
return (
|
|
47
47
|
<View style={[styles.container, style]}>
|
|
48
48
|
<FastImage
|
|
49
|
-
{...
|
|
49
|
+
{...rest}
|
|
50
|
+
source={source}
|
|
50
51
|
style={styles.image}
|
|
51
52
|
onLoad={() => {
|
|
52
53
|
setFail(false);
|
package/Image/types.ts
CHANGED
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;
|