@momo-kits/foundation 0.75.1-beta.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/Assets/header-background.png +0 -0
- package/Assets/icon.json +3947 -0
- package/Button/index.tsx +247 -0
- package/Button/styles.ts +41 -0
- package/CheckBox/index.tsx +61 -0
- package/CheckBox/styles.ts +15 -0
- package/CheckBox/types.ts +10 -0
- package/Consts/colors+spacing+radius.ts +214 -0
- package/Consts/index.ts +3 -0
- package/Consts/styles.ts +78 -0
- package/Consts/theme.ts +121 -0
- package/Divider/index.tsx +19 -0
- package/Icon/index.tsx +37 -0
- package/Icon/types.ts +8 -0
- package/IconButton/index.tsx +128 -0
- package/IconButton/styles.ts +19 -0
- package/Image/index.tsx +80 -0
- package/Image/styles.ts +13 -0
- package/Image/types.ts +3 -0
- package/Input/Input.tsx +142 -0
- package/Input/InputMoney.tsx +132 -0
- package/Input/InputSearch.tsx +161 -0
- package/Input/InputTextArea.tsx +152 -0
- package/Input/common.tsx +125 -0
- package/Input/index.tsx +41 -0
- package/Input/styles.ts +122 -0
- package/Input/utils.ts +63 -0
- package/Layout/Card.tsx +87 -0
- package/Layout/GridSystem.tsx +124 -0
- package/Layout/ScreenContainer.tsx +122 -0
- package/Layout/Section.tsx +143 -0
- package/Layout/index.ts +16 -0
- package/Layout/types.ts +21 -0
- package/Layout/utils.ts +50 -0
- package/Navigation/BottomTab.tsx +39 -0
- package/Navigation/Components.tsx +329 -0
- package/Navigation/ModalScreen.tsx +207 -0
- package/Navigation/Navigation.ts +38 -0
- package/Navigation/NavigationButton.tsx +54 -0
- package/Navigation/NavigationContainer.tsx +82 -0
- package/Navigation/Navigator.ts +66 -0
- package/Navigation/Observer.ts +79 -0
- package/Navigation/StackScreen.tsx +40 -0
- package/Navigation/index.ts +14 -0
- package/Navigation/types.ts +149 -0
- package/Navigation/utils.tsx +167 -0
- package/Pagination/Dot.tsx +16 -0
- package/Pagination/PaginationDot.tsx +33 -0
- package/Pagination/PaginationNumber.tsx +23 -0
- package/Pagination/PaginationScroll.tsx +88 -0
- package/Pagination/PaginationWhiteDot.tsx +32 -0
- package/Pagination/index.tsx +25 -0
- package/Pagination/styles.ts +47 -0
- package/Pagination/types.ts +19 -0
- package/Popup/PopupNotify.tsx +215 -0
- package/Popup/PopupPromotion.tsx +77 -0
- package/Popup/index.tsx +4 -0
- package/Popup/types.ts +22 -0
- package/Radio/index.tsx +65 -0
- package/Radio/styles.ts +11 -0
- package/Radio/types.ts +10 -0
- package/Skeleton/index.tsx +72 -0
- package/Skeleton/styles.ts +5 -0
- package/Skeleton/types.ts +3 -0
- package/Switch/index.tsx +40 -0
- package/Switch/styles.ts +23 -0
- package/Switch/types.ts +8 -0
- package/Tag/index.tsx +79 -0
- package/Tag/styles.ts +24 -0
- package/Tag/types.ts +10 -0
- package/Text/index.tsx +110 -0
- package/Text/styles.ts +35 -0
- package/Text/types.ts +40 -0
- package/index.ts +38 -0
- package/package.json +41 -0
- package/publish.sh +26 -0
package/Layout/Card.tsx
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
|
|
4
|
+
import {CardProps, GridContextProps} from './types';
|
|
5
|
+
import {GridContext} from './index';
|
|
6
|
+
|
|
7
|
+
const Card: React.FC<CardProps> = ({
|
|
8
|
+
widthSpan = 12,
|
|
9
|
+
heightSpan,
|
|
10
|
+
usePadding,
|
|
11
|
+
children,
|
|
12
|
+
style,
|
|
13
|
+
}) => {
|
|
14
|
+
const grid = useContext(GridContext);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* render overlay view only dev mode
|
|
18
|
+
*/
|
|
19
|
+
const renderOverlay = () => {
|
|
20
|
+
return (
|
|
21
|
+
<View
|
|
22
|
+
pointerEvents={'none'}
|
|
23
|
+
style={{
|
|
24
|
+
position: 'absolute',
|
|
25
|
+
top: 0,
|
|
26
|
+
bottom: 0,
|
|
27
|
+
left: 0,
|
|
28
|
+
right: 0,
|
|
29
|
+
borderColor: 'red',
|
|
30
|
+
borderWidth: 1,
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const styles: any = style ?? {};
|
|
37
|
+
|
|
38
|
+
const numberOfColumns = 12;
|
|
39
|
+
const gutterSize = 8;
|
|
40
|
+
let margin = 0;
|
|
41
|
+
if (usePadding) {
|
|
42
|
+
margin = 12;
|
|
43
|
+
}
|
|
44
|
+
const widthSection = grid.getSizeSpan(widthSpan) - margin * 2;
|
|
45
|
+
const totalGutterSize = gutterSize * (numberOfColumns - 1);
|
|
46
|
+
const sizePerSpan = (widthSection - totalGutterSize) / numberOfColumns;
|
|
47
|
+
const gridContext: GridContextProps = {
|
|
48
|
+
numberOfColumns,
|
|
49
|
+
gutterSize,
|
|
50
|
+
margin,
|
|
51
|
+
sizePerSpan,
|
|
52
|
+
getSizeSpan: span => {
|
|
53
|
+
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<View
|
|
59
|
+
accessibilityLabel={`Card`}
|
|
60
|
+
style={{
|
|
61
|
+
...styles,
|
|
62
|
+
width: grid.getSizeSpan(widthSpan),
|
|
63
|
+
height: heightSpan ? grid.getSizeSpan(heightSpan) : undefined,
|
|
64
|
+
paddingHorizontal: margin,
|
|
65
|
+
overflow: 'hidden',
|
|
66
|
+
margin: undefined,
|
|
67
|
+
marginTop: undefined,
|
|
68
|
+
marginBottom: undefined,
|
|
69
|
+
marginLeft: undefined,
|
|
70
|
+
marginRight: undefined,
|
|
71
|
+
}}>
|
|
72
|
+
<GridContext.Provider value={gridContext}>
|
|
73
|
+
{children}
|
|
74
|
+
</GridContext.Provider>
|
|
75
|
+
{__DEV__ && renderOverlay()}
|
|
76
|
+
</View>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
Card.defaultProps = {
|
|
81
|
+
widthSpan: 12,
|
|
82
|
+
usePadding: false,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
Card.displayName = 'Card';
|
|
86
|
+
|
|
87
|
+
export default Card;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
3
|
+
import {Dimensions, StyleSheet, View} from 'react-native';
|
|
4
|
+
import {ApplicationContext} from '../Navigation';
|
|
5
|
+
import {Colors, Spacing} from '../Consts';
|
|
6
|
+
import {GridContextProps} from './types';
|
|
7
|
+
|
|
8
|
+
const GridSystem: React.FC = () => {
|
|
9
|
+
const {theme} = useContext(ApplicationContext);
|
|
10
|
+
const numberOfColumns = 12;
|
|
11
|
+
const gutterSize = 12;
|
|
12
|
+
const margin = 12;
|
|
13
|
+
const widthSection = Dimensions.get('window').width - margin * 2;
|
|
14
|
+
const totalGutterSize = gutterSize * (numberOfColumns - 1);
|
|
15
|
+
const sizePerSpan = (widthSection - totalGutterSize) / numberOfColumns;
|
|
16
|
+
|
|
17
|
+
const grid: GridContextProps = {
|
|
18
|
+
numberOfColumns,
|
|
19
|
+
gutterSize,
|
|
20
|
+
margin,
|
|
21
|
+
sizePerSpan,
|
|
22
|
+
getSizeSpan: span => {
|
|
23
|
+
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const insets = useSafeAreaInsets();
|
|
27
|
+
|
|
28
|
+
const list = [];
|
|
29
|
+
for (let i = 1; i <= grid.numberOfColumns; i++) {
|
|
30
|
+
list.push(i);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<View
|
|
36
|
+
pointerEvents="none"
|
|
37
|
+
style={[
|
|
38
|
+
styles.gridContainer,
|
|
39
|
+
insets,
|
|
40
|
+
{
|
|
41
|
+
borderColor: theme.colors.error.primary,
|
|
42
|
+
marginHorizontal: grid.margin,
|
|
43
|
+
},
|
|
44
|
+
]}>
|
|
45
|
+
{list.map((i, index) => {
|
|
46
|
+
return (
|
|
47
|
+
<View style={{flexDirection: 'row'}} key={index.toString()}>
|
|
48
|
+
{index != -0 && <View style={{width: grid.gutterSize}} />}
|
|
49
|
+
<View
|
|
50
|
+
key={index.toString()}
|
|
51
|
+
pointerEvents="none"
|
|
52
|
+
style={[
|
|
53
|
+
styles.gridColumn,
|
|
54
|
+
{
|
|
55
|
+
width: grid.sizePerSpan,
|
|
56
|
+
},
|
|
57
|
+
]}
|
|
58
|
+
/>
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
})}
|
|
62
|
+
</View>
|
|
63
|
+
<View pointerEvents="none" style={styles.dangerLeftContainer} />
|
|
64
|
+
<View pointerEvents="none" style={styles.dangerRightContainer} />
|
|
65
|
+
<View
|
|
66
|
+
pointerEvents="none"
|
|
67
|
+
style={[styles.dangerTopContainer, {height: insets.top}]}
|
|
68
|
+
/>
|
|
69
|
+
<View
|
|
70
|
+
pointerEvents="none"
|
|
71
|
+
style={[styles.dangerBottomContainer, {height: insets.bottom}]}
|
|
72
|
+
/>
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const styles = StyleSheet.create({
|
|
78
|
+
gridContainer: {
|
|
79
|
+
position: 'absolute',
|
|
80
|
+
top: 0,
|
|
81
|
+
bottom: 0,
|
|
82
|
+
left: 0,
|
|
83
|
+
right: 0,
|
|
84
|
+
flexDirection: 'row',
|
|
85
|
+
borderWidth: 1,
|
|
86
|
+
},
|
|
87
|
+
gridColumn: {
|
|
88
|
+
height: '100%',
|
|
89
|
+
opacity: 0.2,
|
|
90
|
+
backgroundColor: Colors.blue_07,
|
|
91
|
+
},
|
|
92
|
+
dangerTopContainer: {
|
|
93
|
+
opacity: 0.2,
|
|
94
|
+
backgroundColor: Colors.red_03,
|
|
95
|
+
width: '100%',
|
|
96
|
+
top: 0,
|
|
97
|
+
position: 'absolute',
|
|
98
|
+
},
|
|
99
|
+
dangerBottomContainer: {
|
|
100
|
+
opacity: 0.2,
|
|
101
|
+
backgroundColor: Colors.red_03,
|
|
102
|
+
width: '100%',
|
|
103
|
+
bottom: 0,
|
|
104
|
+
position: 'absolute',
|
|
105
|
+
},
|
|
106
|
+
dangerLeftContainer: {
|
|
107
|
+
opacity: 0.2,
|
|
108
|
+
backgroundColor: Colors.red_03,
|
|
109
|
+
width: Spacing.M,
|
|
110
|
+
height: '100%',
|
|
111
|
+
left: 0,
|
|
112
|
+
position: 'absolute',
|
|
113
|
+
},
|
|
114
|
+
dangerRightContainer: {
|
|
115
|
+
opacity: 0.2,
|
|
116
|
+
backgroundColor: Colors.red_03,
|
|
117
|
+
width: Spacing.M,
|
|
118
|
+
height: '100%',
|
|
119
|
+
right: 0,
|
|
120
|
+
position: 'absolute',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export default GridSystem;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {Animated, KeyboardAvoidingView, Platform, View} from 'react-native';
|
|
2
|
+
import {useHeaderHeight} from '@react-navigation/stack';
|
|
3
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
4
|
+
import React, {useContext} from 'react';
|
|
5
|
+
import {ScreenContainerProps} from '../Navigation/types';
|
|
6
|
+
import {Spacing, Styles} from '../Consts';
|
|
7
|
+
import {Section, validateChildren} from './index';
|
|
8
|
+
|
|
9
|
+
import {ApplicationContext} from '../Navigation';
|
|
10
|
+
|
|
11
|
+
const ScreenContainer: React.FC<ScreenContainerProps> = ({
|
|
12
|
+
children,
|
|
13
|
+
enableKeyboardAvoidingView,
|
|
14
|
+
scrollable,
|
|
15
|
+
scrollY,
|
|
16
|
+
headerBackground,
|
|
17
|
+
scrollViewProps,
|
|
18
|
+
footerComponent: Footer,
|
|
19
|
+
}) => {
|
|
20
|
+
const {theme} = useContext(ApplicationContext);
|
|
21
|
+
const insets = useSafeAreaInsets();
|
|
22
|
+
let marginHeader = 0;
|
|
23
|
+
let handleScroll;
|
|
24
|
+
let imageAnimatedStyle = {};
|
|
25
|
+
let Component: any = View;
|
|
26
|
+
if (scrollable) {
|
|
27
|
+
Component = Animated.ScrollView;
|
|
28
|
+
}
|
|
29
|
+
if (scrollY) {
|
|
30
|
+
marginHeader = useHeaderHeight();
|
|
31
|
+
imageAnimatedStyle = {
|
|
32
|
+
transform: [
|
|
33
|
+
{
|
|
34
|
+
scale: scrollY.interpolate?.({
|
|
35
|
+
inputRange: [-300, 0, 1],
|
|
36
|
+
outputRange: [1.75, 1, 1],
|
|
37
|
+
extrapolate: 'clamp',
|
|
38
|
+
}),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
handleScroll = Animated.event(
|
|
43
|
+
[{nativeEvent: {contentOffset: {y: scrollY}}}],
|
|
44
|
+
{useNativeDriver: true},
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* build content for screen
|
|
50
|
+
*/
|
|
51
|
+
const renderContent = () => {
|
|
52
|
+
const results = validateChildren(children, Section);
|
|
53
|
+
if (Array.isArray(results)) {
|
|
54
|
+
return results.map((item, index) => {
|
|
55
|
+
return (
|
|
56
|
+
<View
|
|
57
|
+
key={`Section${index}`}
|
|
58
|
+
style={{
|
|
59
|
+
marginTop: item.props?.useMargin ? Spacing.M : 0,
|
|
60
|
+
flex: item.props?.expanded ? 1 : undefined,
|
|
61
|
+
}}>
|
|
62
|
+
{item}
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<View style={Styles.flex} accessibilityLabel={`ScreenContainer`}>
|
|
72
|
+
<View
|
|
73
|
+
style={{
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
height: 300,
|
|
76
|
+
width: '100%',
|
|
77
|
+
}}>
|
|
78
|
+
{!!headerBackground && (
|
|
79
|
+
<Animated.Image
|
|
80
|
+
source={{
|
|
81
|
+
uri: headerBackground,
|
|
82
|
+
}}
|
|
83
|
+
style={{
|
|
84
|
+
aspectRatio: 1.3,
|
|
85
|
+
...imageAnimatedStyle,
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
89
|
+
</View>
|
|
90
|
+
<KeyboardAvoidingView
|
|
91
|
+
style={Styles.flex}
|
|
92
|
+
enabled={enableKeyboardAvoidingView}
|
|
93
|
+
behavior={Platform.select({
|
|
94
|
+
ios: 'padding',
|
|
95
|
+
android: undefined,
|
|
96
|
+
})}>
|
|
97
|
+
<Component
|
|
98
|
+
{...scrollViewProps}
|
|
99
|
+
onScroll={handleScroll}
|
|
100
|
+
style={[Styles.flex, {marginTop: marginHeader}]}>
|
|
101
|
+
{renderContent()}
|
|
102
|
+
</Component>
|
|
103
|
+
{Footer && (
|
|
104
|
+
<View
|
|
105
|
+
style={{
|
|
106
|
+
paddingTop: Spacing.S,
|
|
107
|
+
paddingBottom: insets.bottom,
|
|
108
|
+
backgroundColor: theme.colors.background.surface,
|
|
109
|
+
}}>
|
|
110
|
+
<Section>{Footer}</Section>
|
|
111
|
+
</View>
|
|
112
|
+
)}
|
|
113
|
+
</KeyboardAvoidingView>
|
|
114
|
+
</View>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
ScreenContainer.defaultProps = {
|
|
119
|
+
enableKeyboardAvoidingView: false,
|
|
120
|
+
scrollable: false,
|
|
121
|
+
};
|
|
122
|
+
export default ScreenContainer;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {Dimensions, View} from 'react-native';
|
|
3
|
+
import {validateChildren} from './utils';
|
|
4
|
+
import Card from './Card';
|
|
5
|
+
import {GridContextProps, SectionProps} from './types';
|
|
6
|
+
import {GridContext} from './index';
|
|
7
|
+
|
|
8
|
+
const Section: React.FC<SectionProps> = ({children, useMargin = true}) => {
|
|
9
|
+
const numberOfColumns = 12;
|
|
10
|
+
const gutterSize = 12;
|
|
11
|
+
let margin = 0;
|
|
12
|
+
if (useMargin) {
|
|
13
|
+
margin = 12;
|
|
14
|
+
}
|
|
15
|
+
let widthSection = Dimensions.get('window').width;
|
|
16
|
+
if (useMargin) widthSection = widthSection - margin * 2;
|
|
17
|
+
const totalGutterSize = gutterSize * (numberOfColumns - 1);
|
|
18
|
+
const sizePerSpan = (widthSection - totalGutterSize) / numberOfColumns;
|
|
19
|
+
|
|
20
|
+
const gridContext: GridContextProps = {
|
|
21
|
+
numberOfColumns,
|
|
22
|
+
gutterSize,
|
|
23
|
+
margin,
|
|
24
|
+
sizePerSpan,
|
|
25
|
+
getSizeSpan: span => {
|
|
26
|
+
return span * sizePerSpan + (span - 1) * gutterSize;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* render overlay only dev mode
|
|
32
|
+
*/
|
|
33
|
+
const renderOverlay = () => {
|
|
34
|
+
return (
|
|
35
|
+
<View
|
|
36
|
+
pointerEvents={'none'}
|
|
37
|
+
style={{
|
|
38
|
+
position: 'absolute',
|
|
39
|
+
top: 0,
|
|
40
|
+
bottom: 0,
|
|
41
|
+
left: 0,
|
|
42
|
+
right: 0,
|
|
43
|
+
borderColor: 'red',
|
|
44
|
+
borderWidth: 1,
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* build a row view in side session
|
|
52
|
+
* @param rows
|
|
53
|
+
* @param cursor
|
|
54
|
+
* @param endRow
|
|
55
|
+
*/
|
|
56
|
+
const addRowView = (
|
|
57
|
+
rows: any[],
|
|
58
|
+
cursor: number = 0,
|
|
59
|
+
endRow: boolean = false,
|
|
60
|
+
) => {
|
|
61
|
+
return (
|
|
62
|
+
<View
|
|
63
|
+
key={`Card${cursor}`}
|
|
64
|
+
style={{
|
|
65
|
+
flexDirection: 'row',
|
|
66
|
+
marginBottom: endRow ? 0 : gridContext.gutterSize,
|
|
67
|
+
}}>
|
|
68
|
+
{rows.map((i: React.ReactElement, index: number) => {
|
|
69
|
+
return (
|
|
70
|
+
<View key={`Card${cursor}${index}`} style={{flexDirection: 'row'}}>
|
|
71
|
+
{index != 0 && (
|
|
72
|
+
<View style={{width: gridContext.gutterSize}}></View>
|
|
73
|
+
)}
|
|
74
|
+
{i}
|
|
75
|
+
</View>
|
|
76
|
+
);
|
|
77
|
+
})}
|
|
78
|
+
</View>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* render children content for session
|
|
84
|
+
* @param children
|
|
85
|
+
*/
|
|
86
|
+
const renderView = (
|
|
87
|
+
children: any,
|
|
88
|
+
): React.ReactElement | React.ReactElement[] => {
|
|
89
|
+
const element: React.ReactElement | React.ReactElement[] = [];
|
|
90
|
+
if (Array.isArray(children)) {
|
|
91
|
+
let cursor = 0;
|
|
92
|
+
let rows: any[] = [];
|
|
93
|
+
for (let i = 0; i < children.length; ++i) {
|
|
94
|
+
const item = children[i];
|
|
95
|
+
const totalSpan = rows
|
|
96
|
+
.concat([item])
|
|
97
|
+
.map(i => i.props?.widthSpan)
|
|
98
|
+
.reduce((previousValue, currentValue) => {
|
|
99
|
+
return previousValue + currentValue;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (totalSpan <= gridContext.numberOfColumns) {
|
|
103
|
+
rows.push(item);
|
|
104
|
+
} else {
|
|
105
|
+
cursor += 1;
|
|
106
|
+
element.push(addRowView(rows, cursor));
|
|
107
|
+
rows = [item];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (i == children.length - 1) {
|
|
111
|
+
cursor += 1;
|
|
112
|
+
element.push(addRowView(rows, cursor, true));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
element.push(children);
|
|
117
|
+
}
|
|
118
|
+
return element;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<GridContext.Provider value={gridContext}>
|
|
123
|
+
<View
|
|
124
|
+
accessibilityLabel={`Section`}
|
|
125
|
+
style={{
|
|
126
|
+
width: gridContext.getSizeSpan(12),
|
|
127
|
+
marginHorizontal: gridContext.margin,
|
|
128
|
+
flexDirection: 'row',
|
|
129
|
+
flexWrap: 'wrap',
|
|
130
|
+
}}>
|
|
131
|
+
{renderView(validateChildren(children, Card))}
|
|
132
|
+
{__DEV__ && renderOverlay()}
|
|
133
|
+
</View>
|
|
134
|
+
</GridContext.Provider>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
Section.displayName = 'Section';
|
|
139
|
+
|
|
140
|
+
Section.defaultProps = {
|
|
141
|
+
useMargin: true,
|
|
142
|
+
};
|
|
143
|
+
export default Section;
|
package/Layout/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Card from './Card';
|
|
2
|
+
import Section from './Section';
|
|
3
|
+
import GridSystem from './GridSystem';
|
|
4
|
+
import {validateChildren} from './utils';
|
|
5
|
+
import {createContext} from 'react';
|
|
6
|
+
import {GridContextProps} from './types';
|
|
7
|
+
|
|
8
|
+
const GridContext = createContext<GridContextProps>({
|
|
9
|
+
numberOfColumns: 12,
|
|
10
|
+
gutterSize: 12,
|
|
11
|
+
margin: 12,
|
|
12
|
+
sizePerSpan: 0,
|
|
13
|
+
getSizeSpan: span => 0,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export {GridContext, Card, Section, GridSystem, validateChildren};
|
package/Layout/types.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {ViewProps} from 'react-native';
|
|
2
|
+
|
|
3
|
+
type SpanNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
|
4
|
+
|
|
5
|
+
export type GridContextProps = {
|
|
6
|
+
numberOfColumns: number;
|
|
7
|
+
gutterSize: number;
|
|
8
|
+
margin?: number;
|
|
9
|
+
sizePerSpan: number;
|
|
10
|
+
getSizeSpan: (span: number) => number;
|
|
11
|
+
};
|
|
12
|
+
export interface SectionProps extends ViewProps {
|
|
13
|
+
expanded?: boolean;
|
|
14
|
+
useMargin?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CardProps extends ViewProps {
|
|
18
|
+
widthSpan: SpanNumber;
|
|
19
|
+
heightSpan?: SpanNumber;
|
|
20
|
+
usePadding?: boolean;
|
|
21
|
+
}
|
package/Layout/utils.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {Alert} from 'react-native';
|
|
2
|
+
import React, {ReactElement} from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* validate children type
|
|
6
|
+
* @param children
|
|
7
|
+
* @param childrenType
|
|
8
|
+
*/
|
|
9
|
+
const validateChildren = (
|
|
10
|
+
children:
|
|
11
|
+
| React.ReactElement<any, string | React.JSXElementConstructor<any>>
|
|
12
|
+
| string
|
|
13
|
+
| number
|
|
14
|
+
| {}
|
|
15
|
+
| Iterable<React.ReactNode>
|
|
16
|
+
| React.ReactPortal
|
|
17
|
+
| null
|
|
18
|
+
| undefined
|
|
19
|
+
| boolean,
|
|
20
|
+
childrenType: React.FC<any>,
|
|
21
|
+
) => {
|
|
22
|
+
if (!__DEV__) return children;
|
|
23
|
+
let results: React.ReactElement[] = [];
|
|
24
|
+
if (Array.isArray(children)) {
|
|
25
|
+
results = children?.filter((child: ReactElement) => {
|
|
26
|
+
if (child?.type === childrenType) {
|
|
27
|
+
return child;
|
|
28
|
+
} else {
|
|
29
|
+
Alert.alert(
|
|
30
|
+
'Component Kits',
|
|
31
|
+
`Wrong children type for this component, expected is ${childrenType.displayName}.`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
const singleChild: any = children;
|
|
37
|
+
if (singleChild?.type === childrenType) {
|
|
38
|
+
results = [singleChild];
|
|
39
|
+
} else {
|
|
40
|
+
Alert.alert(
|
|
41
|
+
'Component Kits',
|
|
42
|
+
`Wrong children type for this component, expected is ${childrenType.displayName}.`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return results;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export {validateChildren};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, {FC} from 'react';
|
|
2
|
+
import {BottomTabProps} from './types';
|
|
3
|
+
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
|
4
|
+
import {Icon} from '../Icon';
|
|
5
|
+
|
|
6
|
+
const Tab = createBottomTabNavigator();
|
|
7
|
+
|
|
8
|
+
const BottomTab: FC<BottomTabProps> = ({tabs}) => {
|
|
9
|
+
return (
|
|
10
|
+
<Tab.Navigator>
|
|
11
|
+
{tabs.map((item, index) => {
|
|
12
|
+
return (
|
|
13
|
+
<Tab.Screen
|
|
14
|
+
key={`${item.label}-${index}`}
|
|
15
|
+
name={item.label}
|
|
16
|
+
component={item.screen}
|
|
17
|
+
options={{
|
|
18
|
+
tabBarLabel: item.label,
|
|
19
|
+
tabBarBadge: item?.badgeLabel,
|
|
20
|
+
tabBarBadgeStyle: {
|
|
21
|
+
borderColor: 'white',
|
|
22
|
+
borderWidth: 2,
|
|
23
|
+
fontSize: 10,
|
|
24
|
+
lineHeight: 14,
|
|
25
|
+
fontWeight: 'bold',
|
|
26
|
+
alignSelf: 'center',
|
|
27
|
+
},
|
|
28
|
+
tabBarIcon: ({color, size}) => (
|
|
29
|
+
<Icon source={item.icon} color={color} size={size} />
|
|
30
|
+
),
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
35
|
+
</Tab.Navigator>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default BottomTab;
|