@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.
Files changed (76) hide show
  1. package/Assets/header-background.png +0 -0
  2. package/Assets/icon.json +3947 -0
  3. package/Button/index.tsx +247 -0
  4. package/Button/styles.ts +41 -0
  5. package/CheckBox/index.tsx +61 -0
  6. package/CheckBox/styles.ts +15 -0
  7. package/CheckBox/types.ts +10 -0
  8. package/Consts/colors+spacing+radius.ts +214 -0
  9. package/Consts/index.ts +3 -0
  10. package/Consts/styles.ts +78 -0
  11. package/Consts/theme.ts +121 -0
  12. package/Divider/index.tsx +19 -0
  13. package/Icon/index.tsx +37 -0
  14. package/Icon/types.ts +8 -0
  15. package/IconButton/index.tsx +128 -0
  16. package/IconButton/styles.ts +19 -0
  17. package/Image/index.tsx +80 -0
  18. package/Image/styles.ts +13 -0
  19. package/Image/types.ts +3 -0
  20. package/Input/Input.tsx +142 -0
  21. package/Input/InputMoney.tsx +132 -0
  22. package/Input/InputSearch.tsx +161 -0
  23. package/Input/InputTextArea.tsx +152 -0
  24. package/Input/common.tsx +125 -0
  25. package/Input/index.tsx +41 -0
  26. package/Input/styles.ts +122 -0
  27. package/Input/utils.ts +63 -0
  28. package/Layout/Card.tsx +87 -0
  29. package/Layout/GridSystem.tsx +124 -0
  30. package/Layout/ScreenContainer.tsx +122 -0
  31. package/Layout/Section.tsx +143 -0
  32. package/Layout/index.ts +16 -0
  33. package/Layout/types.ts +21 -0
  34. package/Layout/utils.ts +50 -0
  35. package/Navigation/BottomTab.tsx +39 -0
  36. package/Navigation/Components.tsx +329 -0
  37. package/Navigation/ModalScreen.tsx +207 -0
  38. package/Navigation/Navigation.ts +38 -0
  39. package/Navigation/NavigationButton.tsx +54 -0
  40. package/Navigation/NavigationContainer.tsx +82 -0
  41. package/Navigation/Navigator.ts +66 -0
  42. package/Navigation/Observer.ts +79 -0
  43. package/Navigation/StackScreen.tsx +40 -0
  44. package/Navigation/index.ts +14 -0
  45. package/Navigation/types.ts +149 -0
  46. package/Navigation/utils.tsx +167 -0
  47. package/Pagination/Dot.tsx +16 -0
  48. package/Pagination/PaginationDot.tsx +33 -0
  49. package/Pagination/PaginationNumber.tsx +23 -0
  50. package/Pagination/PaginationScroll.tsx +88 -0
  51. package/Pagination/PaginationWhiteDot.tsx +32 -0
  52. package/Pagination/index.tsx +25 -0
  53. package/Pagination/styles.ts +47 -0
  54. package/Pagination/types.ts +19 -0
  55. package/Popup/PopupNotify.tsx +215 -0
  56. package/Popup/PopupPromotion.tsx +77 -0
  57. package/Popup/index.tsx +4 -0
  58. package/Popup/types.ts +22 -0
  59. package/Radio/index.tsx +65 -0
  60. package/Radio/styles.ts +11 -0
  61. package/Radio/types.ts +10 -0
  62. package/Skeleton/index.tsx +72 -0
  63. package/Skeleton/styles.ts +5 -0
  64. package/Skeleton/types.ts +3 -0
  65. package/Switch/index.tsx +40 -0
  66. package/Switch/styles.ts +23 -0
  67. package/Switch/types.ts +8 -0
  68. package/Tag/index.tsx +79 -0
  69. package/Tag/styles.ts +24 -0
  70. package/Tag/types.ts +10 -0
  71. package/Text/index.tsx +110 -0
  72. package/Text/styles.ts +35 -0
  73. package/Text/types.ts +40 -0
  74. package/index.ts +38 -0
  75. package/package.json +41 -0
  76. package/publish.sh +26 -0
@@ -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;
@@ -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};
@@ -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
+ }
@@ -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;