@momo-kits/foundation 1.0.0

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 (51) hide show
  1. package/Assets/header-background.png +0 -0
  2. package/Button/index.tsx +307 -0
  3. package/Button/styles.ts +41 -0
  4. package/Button/types.ts +18 -0
  5. package/CheckBox/index.js +74 -0
  6. package/CheckBox/styles.js +3 -0
  7. package/Consts/colors+spacing+radius.ts +162 -0
  8. package/Consts/index.ts +76 -0
  9. package/Consts/styles.ts +137 -0
  10. package/ContentLoader/index.tsx +71 -0
  11. package/ContentLoader/styles.ts +5 -0
  12. package/ContentLoader/types.ts +3 -0
  13. package/Divider/index.js +43 -0
  14. package/Divider/styles.js +10 -0
  15. package/Icon/icon.json +3935 -0
  16. package/Icon/index.tsx +30 -0
  17. package/Icon/types.ts +8 -0
  18. package/IconButton/index.tsx +127 -0
  19. package/IconButton/types.ts +17 -0
  20. package/Image/index.tsx +67 -0
  21. package/Image/styles.ts +13 -0
  22. package/Image/types.ts +6 -0
  23. package/Layout/Row.tsx +42 -0
  24. package/Layout/Screen.tsx +68 -0
  25. package/Layout/Section.tsx +30 -0
  26. package/Layout/View.tsx +84 -0
  27. package/Layout/index.ts +6 -0
  28. package/Layout/styles.ts +24 -0
  29. package/Layout/types.ts +39 -0
  30. package/Layout/utils.ts +37 -0
  31. package/Navigation/Components.tsx +55 -0
  32. package/Navigation/ModalScreen.tsx +177 -0
  33. package/Navigation/Navigation.ts +34 -0
  34. package/Navigation/NavigationButton.tsx +25 -0
  35. package/Navigation/NavigationContainer.tsx +74 -0
  36. package/Navigation/Navigator.ts +45 -0
  37. package/Navigation/ScreenContainer.tsx +38 -0
  38. package/Navigation/StackScreen.tsx +19 -0
  39. package/Navigation/index.ts +10 -0
  40. package/Navigation/types.ts +87 -0
  41. package/Navigation/utils.tsx +84 -0
  42. package/SizedBox/index.js +23 -0
  43. package/SizedBox/styles.js +7 -0
  44. package/Text/index.tsx +164 -0
  45. package/Text/styles.ts +34 -0
  46. package/Text/types.ts +47 -0
  47. package/TextInput/index.js +225 -0
  48. package/TextInput/styles.js +55 -0
  49. package/index.ts +18 -0
  50. package/package.json +41 -0
  51. package/publish.sh +26 -0
package/Icon/index.tsx ADDED
@@ -0,0 +1,30 @@
1
+ import React, {useContext} from 'react';
2
+ import {IconProps, Image, NavigationContext} from '../index';
3
+ import SourceSets from './icon.json';
4
+
5
+ const Icon: React.FC<IconProps> = ({name, source, size, color}) => {
6
+ const {theme} = useContext(NavigationContext);
7
+
8
+ const getIconSource = (): any => {
9
+ if (name) {
10
+ let icon: {[key: string]: object} = SourceSets;
11
+ return icon[name] ?? icon.ic_warning;
12
+ }
13
+ return source;
14
+ };
15
+
16
+ return (
17
+ <Image
18
+ source={getIconSource()}
19
+ style={{width: size, height: size}}
20
+ tintColor={color ?? theme.colors.text}
21
+ />
22
+ );
23
+ };
24
+
25
+ Icon.defaultProps = {
26
+ name: 'ic_back',
27
+ size: 24,
28
+ };
29
+
30
+ export {Icon};
package/Icon/types.ts ADDED
@@ -0,0 +1,8 @@
1
+ import {Source} from 'react-native-fast-image';
2
+
3
+ export type IconProps = {
4
+ name?: string;
5
+ source?: Source | number | undefined;
6
+ size?: number;
7
+ color?: string;
8
+ };
@@ -0,0 +1,127 @@
1
+ import React, {useContext} from 'react';
2
+ import {StyleSheet, TouchableOpacity, View} from 'react-native';
3
+ import {NavigationContext, Colors, Icon, IconButtonProps} from '../index';
4
+
5
+ const IconButton: React.FC<IconButtonProps> = ({
6
+ style,
7
+ type,
8
+ icon,
9
+ size,
10
+ shape,
11
+ ...rest
12
+ }) => {
13
+ const {theme} = useContext(NavigationContext);
14
+
15
+ /**
16
+ * export size style
17
+ */
18
+ const getSizeStyle = () => {
19
+ switch (size) {
20
+ case 'large':
21
+ return styles.large;
22
+ case 'small':
23
+ return styles.small;
24
+
25
+ default:
26
+ return styles.small;
27
+ }
28
+ };
29
+
30
+ /**
31
+ * export size style
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
47
+ */
48
+ const getTypeStyle = () => {
49
+ switch (type) {
50
+ case 'primary':
51
+ return {backgroundColor: theme.colors.primary};
52
+ case 'tonal':
53
+ return {backgroundColor: theme.colors.primary + '33'};
54
+ case 'secondary':
55
+ return {borderColor: theme.colors.border, borderWidth: 1};
56
+ case 'outline':
57
+ return {
58
+ borderWidth: 1,
59
+ borderColor: theme.colors.primary,
60
+ };
61
+ case 'disabled':
62
+ return {backgroundColor: theme.colors.border};
63
+ default:
64
+ return {backgroundColor: theme.colors.primary};
65
+ }
66
+ };
67
+
68
+ const getIconColor = () => {
69
+ switch (type) {
70
+ case 'primary':
71
+ return Colors.black_01;
72
+ case 'tonal':
73
+ return theme.colors.primary;
74
+ case 'secondary':
75
+ return theme.colors.text;
76
+ case 'outline':
77
+ return theme.colors.primary;
78
+ case 'disabled':
79
+ return theme.colors.text + '4D';
80
+ default:
81
+ return Colors.black_01;
82
+ }
83
+ };
84
+
85
+ const buttonStyle = StyleSheet.flatten([
86
+ getSizeStyle(),
87
+ getTypeStyle(),
88
+ shape === 'rectangle' && {borderRadius: 8},
89
+ style,
90
+ ]);
91
+
92
+ return (
93
+ <TouchableOpacity
94
+ {...rest}
95
+ disabled={type === 'disabled'}
96
+ style={buttonStyle}>
97
+ <Icon name={icon} size={getIconSize()} color={getIconColor()} />
98
+ </TouchableOpacity>
99
+ );
100
+ };
101
+
102
+ IconButton.defaultProps = {
103
+ icon: 'close',
104
+ type: 'primary',
105
+ size: 'small',
106
+ shape: 'circle',
107
+ children: <View />,
108
+ };
109
+
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
+ export {IconButton};
@@ -0,0 +1,17 @@
1
+ import {TouchableOpacityProps} from 'react-native';
2
+
3
+ export type IconButtonType =
4
+ | 'primary'
5
+ | 'tonal'
6
+ | 'secondary'
7
+ | 'outline'
8
+ | 'disabled';
9
+ export type IconButtonSize = 'large' | 'small';
10
+ export type IconButtonShape = 'circle' | 'rectangle';
11
+
12
+ export interface IconButtonProps extends TouchableOpacityProps {
13
+ icon: string;
14
+ type?: IconButtonType;
15
+ size?: IconButtonSize;
16
+ shape?: IconButtonShape;
17
+ }
@@ -0,0 +1,67 @@
1
+ import React, {useContext, useState} from 'react';
2
+ import {StyleSheet, View} from 'react-native';
3
+ import FastImage from 'react-native-fast-image';
4
+ import {
5
+ ContentLoader,
6
+ Icon,
7
+ ImageProps,
8
+ NavigationContext,
9
+ Styles,
10
+ Text,
11
+ } from '../index';
12
+ import styles from './styles';
13
+
14
+ const Image: React.FC<ImageProps> = props => {
15
+ const {theme} = useContext(NavigationContext);
16
+ const {style, placeholder, error, source} = props;
17
+ const [loading, setLoading] = useState(typeof source === 'object');
18
+ const [fail, setFail] = useState(false);
19
+
20
+ /**
21
+ * render content
22
+ * @returns {JSX.Element}
23
+ */
24
+ const renderContent = () => {
25
+ if (loading || fail) {
26
+ let content = placeholder ?? (
27
+ <ContentLoader style={[StyleSheet.absoluteFill, styles.image]} />
28
+ );
29
+ if (fail) {
30
+ content = error ?? (
31
+ <View
32
+ style={[Styles.flexCenter, {backgroundColor: theme.colors.border}]}>
33
+ <Icon name="error-outline" />
34
+ <Text typography="subtitle" style={Styles.textCenter}>
35
+ Can't load image
36
+ </Text>
37
+ </View>
38
+ );
39
+ }
40
+ return (
41
+ <View style={[StyleSheet.absoluteFill, styles.image]}>{content}</View>
42
+ );
43
+ }
44
+ };
45
+
46
+ return (
47
+ <View style={[styles.container, style]}>
48
+ <FastImage
49
+ {...props}
50
+ style={styles.image}
51
+ onLoad={() => {
52
+ setFail(false);
53
+ setLoading(true);
54
+ }}
55
+ onLoadEnd={() => setLoading(false)}
56
+ onError={() => setFail(true)}
57
+ />
58
+ {renderContent()}
59
+ </View>
60
+ );
61
+ };
62
+
63
+ Image.defaultProps = {
64
+ style: {},
65
+ };
66
+
67
+ export {Image};
@@ -0,0 +1,13 @@
1
+ import {StyleSheet} from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ overflow: 'hidden',
6
+ alignItems: 'center',
7
+ justifyContent: 'center',
8
+ },
9
+ image: {
10
+ width: '100%',
11
+ height: '100%',
12
+ },
13
+ });
package/Image/types.ts ADDED
@@ -0,0 +1,6 @@
1
+ import {FastImageProps} from 'react-native-fast-image';
2
+
3
+ export interface ImageProps extends FastImageProps {
4
+ placeholder?: boolean;
5
+ error?: boolean;
6
+ }
package/Layout/Row.tsx ADDED
@@ -0,0 +1,42 @@
1
+ import React, {useContext} from 'react';
2
+ import {View} from 'react-native';
3
+ import styles from './styles';
4
+ import {MMSectionContext} from './Section';
5
+ import {DEFAULT_SCREEN_PADDING, screenWidth} from './utils';
6
+
7
+ const MMRow: React.FC<any> = props => {
8
+ const haveMargin = useContext(MMSectionContext);
9
+ const rowWidth = haveMargin
10
+ ? screenWidth - DEFAULT_SCREEN_PADDING * 4
11
+ : screenWidth - DEFAULT_SCREEN_PADDING * 2;
12
+
13
+ const {children} = props;
14
+ let totalGap = 0;
15
+ return (
16
+ <View
17
+ style={[
18
+ styles.row,
19
+ {
20
+ width: rowWidth,
21
+ },
22
+ ]}>
23
+ {React.Children.map(children, (child, index) => {
24
+ let isEndLine = false;
25
+ totalGap += child.props.span;
26
+ if (totalGap >= 12) {
27
+ totalGap = 0;
28
+ isEndLine = true;
29
+ }
30
+ if (totalGap + children[index + 1]?.props?.span > 12) {
31
+ totalGap = 0;
32
+ }
33
+ return React.cloneElement(child, {
34
+ isEndLine: isEndLine,
35
+ isRowChild: true,
36
+ });
37
+ })}
38
+ </View>
39
+ );
40
+ };
41
+
42
+ export default MMRow;
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import {ScrollView, View} from 'react-native';
3
+ import styles from './styles';
4
+ import {MMScreenProps} from './types';
5
+ import {MMSection} from './index';
6
+ import {
7
+ DEFAULT_GUTTER,
8
+ DEFAULT_SCREEN_PADDING,
9
+ screenWidth,
10
+ validateChildren,
11
+ } from './utils';
12
+
13
+ const MMScreen = (props: MMScreenProps) => {
14
+ const {children, scrollable} = props;
15
+
16
+ const parentWidth = screenWidth - DEFAULT_SCREEN_PADDING * 2;
17
+
18
+ const colWidth = (parentWidth - DEFAULT_GUTTER * 11) / 12;
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
+ flexDirection: 'row',
30
+ marginHorizontal: 12,
31
+ }}>
32
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((i, ii) => {
33
+ return (
34
+ <View
35
+ pointerEvents="none"
36
+ style={{
37
+ width: colWidth,
38
+ height: '100%',
39
+ opacity: 0.2,
40
+ backgroundColor: '#bae7ff',
41
+ marginRight: ii !== 12 ? DEFAULT_GUTTER : 0,
42
+ }}
43
+ />
44
+ );
45
+ })}
46
+ </View>
47
+ );
48
+ };
49
+
50
+ if (scrollable) {
51
+ return (
52
+ <ScrollView
53
+ showsVerticalScrollIndicator={false}
54
+ style={[styles.screen, {width: screenWidth}]}>
55
+ {validateChildren(children, MMSection)}
56
+ {renderOverlay()}
57
+ </ScrollView>
58
+ );
59
+ }
60
+ return (
61
+ <View style={[styles.screen, {width: screenWidth}]}>
62
+ {validateChildren(children, MMSection)}
63
+ {renderOverlay()}
64
+ </View>
65
+ );
66
+ };
67
+
68
+ export default MMScreen;
@@ -0,0 +1,30 @@
1
+ import React, {createContext} from 'react';
2
+ import {View} from 'react-native';
3
+ import styles from './styles';
4
+ import {MMSectionProps} from './types';
5
+ import {MMRow} from './index';
6
+ import {DEFAULT_SCREEN_PADDING, screenWidth, validateChildren} from './utils';
7
+
8
+ export const MMSectionContext = createContext({});
9
+
10
+ const MMSection: React.FC<MMSectionProps> = props => {
11
+ const {haveMargin = false, children} = props;
12
+ return (
13
+ <MMSectionContext.Provider value={haveMargin}>
14
+ <View
15
+ style={[
16
+ styles.section,
17
+ {
18
+ width: haveMargin
19
+ ? screenWidth - DEFAULT_SCREEN_PADDING * 2
20
+ : screenWidth,
21
+ marginHorizontal: haveMargin ? DEFAULT_SCREEN_PADDING : 0,
22
+ },
23
+ ]}>
24
+ {validateChildren(children, MMRow)}
25
+ </View>
26
+ </MMSectionContext.Provider>
27
+ );
28
+ };
29
+
30
+ export default MMSection;
@@ -0,0 +1,84 @@
1
+ import React, {useContext} from 'react';
2
+ import {View} from 'react-native';
3
+ import {MMViewProps} from './types';
4
+ import styles from './styles';
5
+ import {MMSectionContext} from './Section';
6
+ import {screenWidth} from './utils';
7
+ import {Colors, Shadow} from '@momo-kits/core';
8
+
9
+ const MMView: React.FC<MMViewProps> = props => {
10
+ const {
11
+ span = 12,
12
+ children,
13
+ isEndLine,
14
+ alignItems,
15
+ justifyContent,
16
+ padding,
17
+ backgroundColor,
18
+ borderRadius,
19
+ direction,
20
+ shadow = 'none',
21
+ isRowChild = false,
22
+ } = props;
23
+ const NUM_OF_SPAN = 12;
24
+ const DEFAULT_SCREEN_PADDING = 12;
25
+ const GUTTER = 12;
26
+ const haveMargin = useContext(MMSectionContext);
27
+ const shadowStyle =
28
+ shadow !== 'none' ? (shadow === 'light' ? Shadow.Light : Shadow.Dark) : {};
29
+
30
+ const parentWidth = haveMargin
31
+ ? screenWidth - DEFAULT_SCREEN_PADDING * 4
32
+ : screenWidth - DEFAULT_SCREEN_PADDING * 2;
33
+
34
+ const paddingStyle = {
35
+ paddingTop: padding?.[0] || 0,
36
+ paddingRight: padding?.[1] || 0,
37
+ paddingBottom: padding?.[2] || 0,
38
+ paddingLeft: padding?.[3] || 0,
39
+ };
40
+ const colWidth =
41
+ (span * (parentWidth - GUTTER * (NUM_OF_SPAN - 1))) / NUM_OF_SPAN +
42
+ GUTTER * (span - 1);
43
+
44
+ const renderOverlay = () => {
45
+ return (
46
+ <View
47
+ pointerEvents={'none'}
48
+ style={{
49
+ position: 'absolute',
50
+ top: 0,
51
+ bottom: 0,
52
+ left: 0,
53
+ right: 0,
54
+ borderColor: Colors.pink_08,
55
+ borderWidth: 1,
56
+ }}
57
+ />
58
+ );
59
+ };
60
+
61
+ return (
62
+ <View
63
+ style={[
64
+ styles.col,
65
+ {
66
+ width: colWidth,
67
+ marginRight: isEndLine ? 0 : GUTTER,
68
+ marginBottom: isRowChild ? GUTTER : 0,
69
+ alignItems,
70
+ backgroundColor,
71
+ borderRadius,
72
+ justifyContent,
73
+ flexDirection: direction,
74
+ },
75
+ paddingStyle,
76
+ shadowStyle,
77
+ ]}>
78
+ {children}
79
+ {renderOverlay()}
80
+ </View>
81
+ );
82
+ };
83
+
84
+ export default MMView;
@@ -0,0 +1,6 @@
1
+ import MMRow from './Row';
2
+ import MMView from './View';
3
+ import MMScreen from './Screen';
4
+ import MMSection from './Section';
5
+
6
+ export {MMRow, MMView, MMScreen, MMSection};
@@ -0,0 +1,24 @@
1
+ import {StyleSheet} from 'react-native';
2
+ import {Colors} from '@momo-kits/core';
3
+
4
+ const styles = StyleSheet.create({
5
+ col: {
6
+ minHeight: 1,
7
+ },
8
+ row: {
9
+ minHeight: 1,
10
+ flexDirection: 'row',
11
+ flexWrap: 'wrap',
12
+ },
13
+ section: {
14
+ paddingHorizontal: 12,
15
+ marginBottom: 12,
16
+ },
17
+ screen: {
18
+ flex: 1,
19
+ backgroundColor: Colors.black_01,
20
+ paddingVertical: 12,
21
+ },
22
+ });
23
+
24
+ export default styles;
@@ -0,0 +1,39 @@
1
+ import {ReactElement} from 'react';
2
+
3
+ type SpanNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
4
+ type ViewAlignItems = 'flex-start' | 'flex-end' | 'center';
5
+
6
+ type ViewJustifyContent =
7
+ | 'flex-start'
8
+ | 'flex-end'
9
+ | 'center'
10
+ | 'space-between'
11
+ | 'space-around'
12
+ | 'space-evenly';
13
+
14
+ type ViewDirection = 'row' | 'column';
15
+
16
+ type ViewShadow = 'none' | 'light' | 'dark';
17
+ export interface MMScreenProps {
18
+ children: ReactElement;
19
+ scrollable: boolean;
20
+ }
21
+
22
+ export interface MMSectionProps {
23
+ haveMargin?: boolean;
24
+ children: ReactElement;
25
+ }
26
+
27
+ export interface MMViewProps {
28
+ span: SpanNumber;
29
+ children: ReactElement;
30
+ isEndLine?: boolean;
31
+ isRowChild?: boolean;
32
+ alignItems?: ViewAlignItems;
33
+ justifyContent: ViewJustifyContent;
34
+ padding: Array<number>;
35
+ backgroundColor: string;
36
+ borderRadius: number;
37
+ direction: ViewDirection;
38
+ shadow: ViewShadow;
39
+ }
@@ -0,0 +1,37 @@
1
+ import {Dimensions} from 'react-native';
2
+ import {FC, JSXElementConstructor, ReactElement, ReactNode} from 'react';
3
+ import {MMSectionProps} from './types';
4
+
5
+ export const screenWidth = Dimensions.get('window').width;
6
+
7
+ export const DEFAULT_SCREEN_PADDING = 12;
8
+
9
+ export const DEFAULT_GUTTER = 12;
10
+ export const validateChildren = (
11
+ children: ReactElement<any, string | JSXElementConstructor<any>> & ReactNode,
12
+ childrenType:
13
+ | string
14
+ | FC<MMSectionProps>
15
+ | JSXElementConstructor<any>
16
+ | FC<any>,
17
+ ) => {
18
+ if (Array.isArray(children)) {
19
+ return children?.map((child: ReactElement) => {
20
+ if (child?.type === childrenType) {
21
+ return child;
22
+ }
23
+ console.error(
24
+ 'Wrong children type for this container, please check document.',
25
+ );
26
+ return null;
27
+ });
28
+ } else {
29
+ if (children?.type === childrenType) {
30
+ return children;
31
+ }
32
+ console.error(
33
+ 'Wrong children type for this container, please check document.',
34
+ );
35
+ return null;
36
+ }
37
+ };
@@ -0,0 +1,55 @@
1
+ import React, {useContext} from 'react';
2
+ import {StatusBar, StyleSheet, View} from 'react-native';
3
+ import {Image, NavigationButton, NavigationContext, Styles} from '../index';
4
+
5
+ const styles = StyleSheet.create({
6
+ headerBackground: {
7
+ width: '100%',
8
+ height: undefined,
9
+ position: 'absolute',
10
+ aspectRatio: 375 / 154,
11
+ },
12
+ });
13
+
14
+ const HeaderBackground: React.FC<any> = () => {
15
+ const {theme} = useContext(NavigationContext);
16
+ const source: any = theme.assets?.headerBackground;
17
+ return (
18
+ <View
19
+ style={[
20
+ Styles.flex,
21
+ {backgroundColor: theme.colors.background, overflow: 'hidden'},
22
+ ]}>
23
+ <StatusBar barStyle="light-content" />
24
+ <Image style={styles.headerBackground} source={source} />
25
+ </View>
26
+ );
27
+ };
28
+
29
+ const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
30
+ const validType = (item: any) => {
31
+ if (item.type !== NavigationButton) {
32
+ console.error(
33
+ 'element type of NavigationButton, Please migrate to use NavigationButton of kits.',
34
+ );
35
+ }
36
+ };
37
+ const renderAction = () => {
38
+ if (Array.isArray(children)) {
39
+ return children.map((child, index) => {
40
+ validType(child);
41
+ return (
42
+ <React.Fragment key={index}>
43
+ {React.cloneElement(child, {...restProps})}
44
+ </React.Fragment>
45
+ );
46
+ });
47
+ }
48
+
49
+ validType(children);
50
+ return React.cloneElement(children, {...restProps});
51
+ };
52
+ return <View style={Styles.headerRightButton}>{renderAction()}</View>;
53
+ };
54
+
55
+ export {HeaderBackground, HeaderRightAction};