@momo-kits/foundation 0.74.2-react-native.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/BottomTab.tsx +120 -0
- package/Application/Components.tsx +534 -0
- package/Application/ModalScreen.tsx +268 -0
- package/Application/Navigation.ts +50 -0
- package/Application/NavigationContainer.tsx +120 -0
- package/Application/Navigator.ts +131 -0
- package/Application/StackScreen.tsx +71 -0
- package/Application/index.ts +41 -0
- package/Application/types.ts +216 -0
- package/Application/utils.tsx +151 -0
- package/Assets/DotAnimation.json +256 -0
- package/Assets/SpinnerAnimation.json +1027 -0
- package/Assets/icon.json +4031 -0
- package/Assets/icon_bank.json +506 -0
- package/Assets/language.json +202 -0
- package/Badge/Badge.tsx +56 -0
- package/Badge/BadgeDot.tsx +11 -0
- package/Badge/BadgeRibbon.tsx +125 -0
- package/Badge/Shape.tsx +29 -0
- package/Badge/index.tsx +7 -0
- package/Badge/styles.ts +58 -0
- package/Badge/types.ts +63 -0
- package/Button/index.tsx +310 -0
- package/Button/styles.ts +60 -0
- package/CheckBox/index.tsx +65 -0
- package/CheckBox/styles.ts +15 -0
- package/CheckBox/types.ts +38 -0
- package/Consts/colors+spacing+radius.ts +218 -0
- package/Consts/index.ts +4 -0
- package/Consts/styles.ts +52 -0
- package/Consts/theme.ts +125 -0
- package/Divider/index.tsx +21 -0
- package/Icon/index.tsx +41 -0
- package/Icon/types.ts +27 -0
- package/IconButton/index.tsx +139 -0
- package/IconButton/styles.ts +19 -0
- package/Image/index.tsx +83 -0
- package/Image/styles.ts +5 -0
- package/Image/types.ts +5 -0
- package/Input/Input.tsx +192 -0
- package/Input/InputDropDown.tsx +114 -0
- package/Input/InputMoney.tsx +163 -0
- package/Input/InputOTP.tsx +235 -0
- package/Input/InputSearch.tsx +151 -0
- package/Input/InputTextArea.tsx +159 -0
- package/Input/common.tsx +129 -0
- package/Input/index.tsx +251 -0
- package/Input/styles.ts +160 -0
- package/Input/utils.ts +69 -0
- package/Layout/Card.tsx +180 -0
- package/Layout/GridSystem.tsx +136 -0
- package/Layout/Item.tsx +56 -0
- package/Layout/ItemList.tsx +55 -0
- package/Layout/ItemSectionList.tsx +39 -0
- package/Layout/Screen.tsx +355 -0
- package/Layout/Section.tsx +179 -0
- package/Layout/index.ts +29 -0
- package/Layout/styles.ts +97 -0
- package/Layout/types.ts +23 -0
- package/Layout/utils.ts +55 -0
- package/Loader/DotLoader.tsx +37 -0
- package/Loader/Loader.tsx +13 -0
- package/Loader/ProgressBar.tsx +44 -0
- package/Loader/Spinner.tsx +28 -0
- package/Loader/index.tsx +4 -0
- package/Loader/styles.ts +10 -0
- package/Loader/types.ts +35 -0
- package/Loader/utils.ts +25 -0
- package/Pagination/Dot.tsx +16 -0
- package/Pagination/PaginationDot.tsx +38 -0
- package/Pagination/PaginationNumber.tsx +24 -0
- package/Pagination/PaginationScroll.tsx +88 -0
- package/Pagination/PaginationWhiteDot.tsx +35 -0
- package/Pagination/index.tsx +25 -0
- package/Pagination/styles.ts +50 -0
- package/Pagination/types.ts +40 -0
- package/Popup/PopupNotify.tsx +266 -0
- package/Popup/PopupPromotion.tsx +120 -0
- package/Popup/index.tsx +4 -0
- package/Popup/types.ts +87 -0
- package/Radio/index.tsx +64 -0
- package/Radio/styles.ts +11 -0
- package/Radio/types.ts +38 -0
- package/Skeleton/index.tsx +72 -0
- package/Skeleton/styles.ts +5 -0
- package/Skeleton/types.ts +6 -0
- package/Switch/index.tsx +37 -0
- package/Switch/styles.ts +23 -0
- package/Switch/types.ts +26 -0
- package/Tag/index.tsx +79 -0
- package/Tag/styles.ts +24 -0
- package/Tag/types.ts +36 -0
- package/Text/index.tsx +110 -0
- package/Text/styles.ts +73 -0
- package/Text/types.ts +34 -0
- package/Text/utils.ts +14 -0
- package/Title/index.tsx +132 -0
- package/Title/styles.ts +52 -0
- package/Title/types.ts +52 -0
- package/code-scanner.js +51 -0
- package/index.ts +51 -0
- package/package.json +36 -0
- package/publish.sh +23 -0
- package/tsconfig.json +24 -0
- package/verify.js +44 -0
package/Consts/styles.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {StyleSheet} from 'react-native';
|
|
2
|
+
|
|
3
|
+
const Styles = StyleSheet.create({
|
|
4
|
+
flex: {
|
|
5
|
+
flex: 1,
|
|
6
|
+
},
|
|
7
|
+
flexCenter: {
|
|
8
|
+
flex: 1,
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
justifyContent: 'center',
|
|
11
|
+
},
|
|
12
|
+
row: {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
},
|
|
16
|
+
rowSpace: {
|
|
17
|
+
flexDirection: 'row',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
justifyContent: 'space-between',
|
|
20
|
+
},
|
|
21
|
+
rowCenter: {
|
|
22
|
+
flexDirection: 'row',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
},
|
|
26
|
+
rowWrap: {
|
|
27
|
+
flexDirection: 'row',
|
|
28
|
+
flexWrap: 'wrap',
|
|
29
|
+
},
|
|
30
|
+
columnSpace: {
|
|
31
|
+
flexDirection: 'column',
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
justifyContent: 'space-between',
|
|
34
|
+
},
|
|
35
|
+
columnSpaceRight: {
|
|
36
|
+
flexDirection: 'column',
|
|
37
|
+
alignItems: 'flex-end',
|
|
38
|
+
justifyContent: 'space-between',
|
|
39
|
+
},
|
|
40
|
+
columnCenter: {
|
|
41
|
+
flexDirection: 'column',
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
},
|
|
45
|
+
columnCenterRight: {
|
|
46
|
+
flexDirection: 'column',
|
|
47
|
+
alignItems: 'flex-end',
|
|
48
|
+
justifyContent: 'center',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export {Styles};
|
package/Consts/theme.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {Context, Theme} from '../Application/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: '#dfdfe6',
|
|
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: Colors.pink_04,
|
|
64
|
+
secondary: Colors.pink_08,
|
|
65
|
+
background: {
|
|
66
|
+
default: '#121212', // Dark background
|
|
67
|
+
surface: '#1e1e1e', // Slightly lighter surface background
|
|
68
|
+
tonal: Colors.pink_10,
|
|
69
|
+
pressed: '#1a1a1a', // Pressed state background
|
|
70
|
+
selected: Colors.pink_11,
|
|
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
|
+
showGrid: false,
|
|
120
|
+
translate: (key: string) => {
|
|
121
|
+
return key;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export {defaultContext, defaultTheme, defaultDarkTheme};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
import {ApplicationContext} from '../Application';
|
|
4
|
+
import {Spacing} from '../Consts';
|
|
5
|
+
|
|
6
|
+
const Divider = () => {
|
|
7
|
+
const {theme} = useContext(ApplicationContext);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<View
|
|
11
|
+
style={{
|
|
12
|
+
height: 1,
|
|
13
|
+
width: '100%',
|
|
14
|
+
backgroundColor: theme.colors.border.default,
|
|
15
|
+
marginVertical: Spacing.XS,
|
|
16
|
+
}}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export {Divider};
|
package/Icon/index.tsx
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import IconSources from '../Assets/icon.json';
|
|
3
|
+
import IconBankSources from '../Assets/icon_bank.json';
|
|
4
|
+
import {IconProps} from './types';
|
|
5
|
+
import {ApplicationContext} from '../Application';
|
|
6
|
+
import {Image} from '../Image';
|
|
7
|
+
|
|
8
|
+
const Icon: React.FC<IconProps> = ({source, size, color, style = {}}) => {
|
|
9
|
+
const {theme} = useContext(ApplicationContext);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* get icon source maps or remote http
|
|
13
|
+
*/
|
|
14
|
+
const getIconSource = (): any => {
|
|
15
|
+
if (source && !source.includes('http')) {
|
|
16
|
+
let icon: {[key: string]: {uri: string}} = IconSources;
|
|
17
|
+
return icon[source] ?? icon.ic_warning;
|
|
18
|
+
}
|
|
19
|
+
return {uri: source};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const tintColorProps = color !== null && {
|
|
23
|
+
tintColor: color ?? theme.colors.text.default
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Image
|
|
28
|
+
loading={false}
|
|
29
|
+
source={getIconSource()}
|
|
30
|
+
style={[style, {width: size, height: size}]}
|
|
31
|
+
{...tintColorProps}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
Icon.defaultProps = {
|
|
37
|
+
source: 'ic_back',
|
|
38
|
+
size: 24,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export {Icon, IconSources, IconBankSources};
|
package/Icon/types.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {ImageStyle} from 'react-native-fast-image';
|
|
2
|
+
|
|
3
|
+
export type IconProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Represents the identifier or the path for the icon image source.
|
|
6
|
+
* It is a required property.
|
|
7
|
+
*/
|
|
8
|
+
source: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Optional. Represents the size of the icon.
|
|
12
|
+
* It can be used to set the width and height of the icon.
|
|
13
|
+
*/
|
|
14
|
+
size?: number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Optional. Represents the color of the icon.
|
|
18
|
+
* It can be any valid color in @momo-kits/foundation.
|
|
19
|
+
*/
|
|
20
|
+
color?: string | null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Optional. Used to apply custom styling to the Icon component.
|
|
24
|
+
* Accepts an object of style properties.
|
|
25
|
+
*/
|
|
26
|
+
style?: ImageStyle;
|
|
27
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
GestureResponderEvent,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
TouchableOpacityProps,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import {ApplicationContext} from '../Application';
|
|
9
|
+
import {Colors} from '../Consts';
|
|
10
|
+
import {Icon} from '../Icon';
|
|
11
|
+
import styles from './styles';
|
|
12
|
+
|
|
13
|
+
export interface IconButtonProps extends TouchableOpacityProps {
|
|
14
|
+
/**
|
|
15
|
+
* Represents the name or key of the icon to be displayed on the IconButton.
|
|
16
|
+
* It is a required property.
|
|
17
|
+
*/
|
|
18
|
+
icon: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Optional. Defines the visual style of the IconButton.
|
|
22
|
+
*/
|
|
23
|
+
type?: 'primary' | 'tonal' | 'secondary' | 'danger' | 'outline' | 'disabled';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optional. Defines the size of the IconButton.
|
|
27
|
+
*/
|
|
28
|
+
size?: 'large' | 'small';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const IconButton: React.FC<IconButtonProps> = ({
|
|
32
|
+
type,
|
|
33
|
+
icon,
|
|
34
|
+
size,
|
|
35
|
+
onPress,
|
|
36
|
+
...rest
|
|
37
|
+
}) => {
|
|
38
|
+
const {theme} = useContext(ApplicationContext);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* get size icon button
|
|
42
|
+
*/
|
|
43
|
+
const getSizeStyle = () => {
|
|
44
|
+
if (size === 'small') {
|
|
45
|
+
return styles.small;
|
|
46
|
+
}
|
|
47
|
+
return styles.large;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* get style for icon button by type
|
|
52
|
+
*/
|
|
53
|
+
const getTypeStyle = () => {
|
|
54
|
+
switch (type) {
|
|
55
|
+
case 'disabled':
|
|
56
|
+
return {
|
|
57
|
+
backgroundColor: theme.colors.background.disable,
|
|
58
|
+
};
|
|
59
|
+
case 'primary':
|
|
60
|
+
return {backgroundColor: theme.colors.primary};
|
|
61
|
+
case 'secondary':
|
|
62
|
+
return {
|
|
63
|
+
backgroundColor: theme.colors.background.surface,
|
|
64
|
+
borderWidth: 1,
|
|
65
|
+
borderColor: theme.colors.border.default,
|
|
66
|
+
};
|
|
67
|
+
case 'outline':
|
|
68
|
+
return {
|
|
69
|
+
backgroundColor: theme.colors.background.surface,
|
|
70
|
+
borderWidth: 1,
|
|
71
|
+
borderColor: theme.colors.primary,
|
|
72
|
+
};
|
|
73
|
+
case 'tonal':
|
|
74
|
+
return {
|
|
75
|
+
backgroundColor: theme.colors.background.tonal,
|
|
76
|
+
};
|
|
77
|
+
case 'danger':
|
|
78
|
+
return {
|
|
79
|
+
backgroundColor: theme.colors.error.primary,
|
|
80
|
+
};
|
|
81
|
+
default:
|
|
82
|
+
return {backgroundColor: theme.colors.primary};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* get color for icon
|
|
88
|
+
*/
|
|
89
|
+
const getIconColor = () => {
|
|
90
|
+
switch (type) {
|
|
91
|
+
case 'disabled':
|
|
92
|
+
return theme.colors.text.disable;
|
|
93
|
+
case 'primary':
|
|
94
|
+
return Colors.black_01;
|
|
95
|
+
case 'danger':
|
|
96
|
+
return Colors.black_01;
|
|
97
|
+
case 'tonal':
|
|
98
|
+
return theme.colors.primary;
|
|
99
|
+
case 'secondary':
|
|
100
|
+
return theme.colors.text.default;
|
|
101
|
+
case 'outline':
|
|
102
|
+
return theme.colors.primary;
|
|
103
|
+
default:
|
|
104
|
+
return Colors.black_01;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* handle press
|
|
110
|
+
* @param e
|
|
111
|
+
*/
|
|
112
|
+
const onPressButton = (e: GestureResponderEvent) => {
|
|
113
|
+
if (type === 'disabled') {
|
|
114
|
+
return () => {};
|
|
115
|
+
}
|
|
116
|
+
onPress?.(e);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const activeOpacity = type === 'disabled' ? 0.75 : 0.5;
|
|
120
|
+
const buttonStyle = StyleSheet.flatten([getTypeStyle(), getSizeStyle()]);
|
|
121
|
+
const iconSize = size === 'small' ? 16 : 24;
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<TouchableOpacity
|
|
125
|
+
{...rest}
|
|
126
|
+
activeOpacity={activeOpacity}
|
|
127
|
+
onPress={onPressButton}
|
|
128
|
+
style={buttonStyle}>
|
|
129
|
+
<Icon size={iconSize} source={icon} color={getIconColor()} />
|
|
130
|
+
</TouchableOpacity>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
IconButton.defaultProps = {
|
|
135
|
+
type: 'primary',
|
|
136
|
+
disabled: false,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
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
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, {useContext, useRef, useState} from 'react';
|
|
2
|
+
import {StyleSheet, View} from 'react-native';
|
|
3
|
+
|
|
4
|
+
import styles from './styles';
|
|
5
|
+
import {ApplicationContext} from '../Application';
|
|
6
|
+
import {Skeleton} from '../Skeleton';
|
|
7
|
+
import {Icon} from '../Icon';
|
|
8
|
+
import {Styles} from '../Consts';
|
|
9
|
+
import {ImageProps} from './types';
|
|
10
|
+
import FastImage from 'react-native-fast-image';
|
|
11
|
+
|
|
12
|
+
type Status = 'success' | 'loading' | 'error';
|
|
13
|
+
|
|
14
|
+
const Image: React.FC<ImageProps> = ({
|
|
15
|
+
style,
|
|
16
|
+
source,
|
|
17
|
+
loading,
|
|
18
|
+
children,
|
|
19
|
+
...rest
|
|
20
|
+
}) => {
|
|
21
|
+
const {theme} = useContext(ApplicationContext);
|
|
22
|
+
const error = useRef(false);
|
|
23
|
+
const [status, setStatus] = useState<Status>('success');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* render content loading | fail | rendered
|
|
27
|
+
*/
|
|
28
|
+
const renderContent = () => {
|
|
29
|
+
if (status === 'loading' || status === 'error') {
|
|
30
|
+
let content = <Skeleton />;
|
|
31
|
+
if (status === 'error') {
|
|
32
|
+
content = (
|
|
33
|
+
<Icon source="media_fail" color={theme.colors.text.disable} />
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return (
|
|
37
|
+
<View style={[StyleSheet.absoluteFill, Styles.flexCenter]}>
|
|
38
|
+
{content}
|
|
39
|
+
</View>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return children;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<FastImage
|
|
47
|
+
{...rest}
|
|
48
|
+
source={source}
|
|
49
|
+
style={[styles.container, style]}
|
|
50
|
+
onProgress={() => {
|
|
51
|
+
error.current = false;
|
|
52
|
+
if (status !== 'loading' && loading) {
|
|
53
|
+
setStatus('loading');
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
onLoadStart={() => {
|
|
57
|
+
error.current = false;
|
|
58
|
+
}}
|
|
59
|
+
onLoad={() => {
|
|
60
|
+
error.current = false;
|
|
61
|
+
}}
|
|
62
|
+
onLoadEnd={() => {
|
|
63
|
+
let current: Status = 'success';
|
|
64
|
+
if (error.current) {
|
|
65
|
+
current = 'error';
|
|
66
|
+
}
|
|
67
|
+
if (status !== current) {
|
|
68
|
+
setStatus(current);
|
|
69
|
+
}
|
|
70
|
+
}}
|
|
71
|
+
onError={() => {
|
|
72
|
+
error.current = true;
|
|
73
|
+
}}>
|
|
74
|
+
{renderContent()}
|
|
75
|
+
</FastImage>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
Image.defaultProps = {
|
|
80
|
+
loading: true,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export {Image};
|
package/Image/styles.ts
ADDED