@momo-kits/foundation 1.0.3 → 1.0.5

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.
@@ -1,36 +1,23 @@
1
1
  import {KeyboardAvoidingView, Platform, ScrollView, View} from 'react-native';
2
2
  import {useHeaderHeight} from '@react-navigation/stack';
3
3
  import {SafeAreaView} from 'react-native-safe-area-context';
4
- import React, {useContext, useLayoutEffect} from 'react';
4
+ import React from 'react';
5
5
  import {ScreenContainerProps} from '../Navigation/types';
6
- import {ApplicationContext} from '../Navigation';
7
6
  import {Spacing, Styles} from '../Consts';
8
7
  import {ScreenSection, validateChildren} from './index';
9
8
 
10
9
  const ScreenContainer: React.FC<ScreenContainerProps> = ({
11
10
  children,
12
- navigation,
13
- options,
14
11
  edges,
15
12
  enableKeyboardAvoidingView,
16
13
  scrollable,
17
14
  }) => {
18
15
  let Component: any = View;
19
- const {theme} = useContext(ApplicationContext);
20
16
  const headerHeight = useHeaderHeight();
21
17
  if (scrollable) {
22
18
  Component = ScrollView;
23
19
  }
24
20
 
25
- /**
26
- * handle set options for navigation if have props options
27
- */
28
- useLayoutEffect(() => {
29
- if (options) {
30
- navigation.setOptions(options);
31
- }
32
- }, [navigation, options]);
33
-
34
21
  /**
35
22
  * build content for screen
36
23
  */
@@ -54,14 +41,7 @@ const ScreenContainer: React.FC<ScreenContainerProps> = ({
54
41
  };
55
42
 
56
43
  return (
57
- <SafeAreaView
58
- style={[
59
- Styles.flex,
60
- {
61
- backgroundColor: theme.colors.background.default,
62
- },
63
- ]}
64
- edges={edges}>
44
+ <SafeAreaView style={Styles.flex} edges={edges}>
65
45
  <KeyboardAvoidingView
66
46
  style={Styles.flex}
67
47
  keyboardVerticalOffset={headerHeight}
@@ -1,10 +1,11 @@
1
1
  import React, {useContext} from 'react';
2
- import {StatusBar, StyleSheet, View} from 'react-native';
2
+ import {DeviceEventEmitter, StatusBar, StyleSheet, View} from 'react-native';
3
3
  import {ApplicationContext, NavigationButton} from './index';
4
- import {Styles} from '../Consts';
4
+ import {Colors, Styles} from '../Consts';
5
5
  import {Image} from '../Image';
6
6
  import {HeaderBackgroundProps, TitleCustomProps} from './types';
7
7
  import {useGridSystem} from '../Layout';
8
+ import {Text} from '../Text';
8
9
 
9
10
  const styles = StyleSheet.create({
10
11
  headerBackground: {
@@ -17,14 +18,36 @@ const styles = StyleSheet.create({
17
18
  alignItems: 'center',
18
19
  justifyContent: 'center',
19
20
  },
21
+ avatar: {width: 36, height: 36, borderRadius: 18},
22
+ dotAvatar: {
23
+ position: 'absolute',
24
+ width: 12,
25
+ height: 12,
26
+ borderRadius: 6,
27
+ bottom: 0,
28
+ right: 0,
29
+ borderWidth: 1,
30
+ borderColor: Colors.black_01,
31
+ },
20
32
  });
21
33
 
22
- const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
23
- surface = false,
24
- image,
25
- }) => {
34
+ const HeaderLeft = (props: any) => {
35
+ const {navigator} = useContext(ApplicationContext);
36
+ const goBack = () => {
37
+ const canGoBack = navigator?.ref.current?.canGoBack();
38
+ if (canGoBack) {
39
+ navigator?.ref.current?.goBack();
40
+ } else {
41
+ const currentTime = new Date().getTime();
42
+ const requestId = `navigationBackPress#${currentTime}`;
43
+ DeviceEventEmitter.emit('dismiss', {requestId});
44
+ }
45
+ };
46
+ return <NavigationButton icon="ic_back" {...props} onPress={goBack} />;
47
+ };
48
+
49
+ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({image}) => {
26
50
  const {theme} = useContext(ApplicationContext);
27
- const allowImage = !surface && theme.assets?.headerBackground;
28
51
  return (
29
52
  <View
30
53
  style={[
@@ -32,14 +55,9 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
32
55
  {backgroundColor: theme.colors.background.surface, overflow: 'hidden'},
33
56
  ]}>
34
57
  <StatusBar
35
- barStyle={allowImage || theme.dark ? 'light-content' : 'dark-content'}
58
+ barStyle={image || theme.dark ? 'light-content' : 'dark-content'}
36
59
  />
37
- {allowImage && (
38
- <Image
39
- style={styles.headerBackground}
40
- source={{uri: image ?? theme.assets?.headerBackground}}
41
- />
42
- )}
60
+ {image && <Image style={styles.headerBackground} source={{uri: image}} />}
43
61
  </View>
44
62
  );
45
63
  };
@@ -49,12 +67,32 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
49
67
  subTitle,
50
68
  image,
51
69
  content,
70
+ tintColor,
71
+ dotColor,
52
72
  }) => {
53
73
  const {getSizeSpan} = useGridSystem();
54
74
 
75
+ const header = (
76
+ <View style={Styles.row}>
77
+ <View>
78
+ <Image source={{uri: image}} style={styles.avatar} />
79
+ {dotColor && (
80
+ <View style={[styles.dotAvatar, {backgroundColor: dotColor}]} />
81
+ )}
82
+ </View>
83
+ <View style={[Styles.flex, Styles.paddingHorizontal8]}>
84
+ <Text typography="title_xs" color={tintColor}>
85
+ {title}
86
+ </Text>
87
+ <Text typography="description_s" color={tintColor}>
88
+ {subTitle}
89
+ </Text>
90
+ </View>
91
+ </View>
92
+ );
55
93
  return (
56
94
  <View style={[styles.headerTitleContainer, {width: getSizeSpan(10)}]}>
57
- {content ?? <View />}
95
+ {content ?? header}
58
96
  </View>
59
97
  );
60
98
  };
@@ -85,4 +123,4 @@ const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
85
123
  return <View style={Styles.headerRightButton}>{renderAction()}</View>;
86
124
  };
87
125
 
88
- export {HeaderBackground, HeaderRightAction, HeaderCustom};
126
+ export {HeaderLeft, HeaderBackground, HeaderRightAction, HeaderCustom};
@@ -26,11 +26,12 @@ const ModalScreen: React.FC<ModalParams> = ({navigation, route, ...rest}) => {
26
26
  backgroundColor,
27
27
  screen,
28
28
  barrierDismissible,
29
+ options,
29
30
  }: ModalParams = route.params;
30
31
  const Component = useRef(screen).current;
31
32
  const params = {
32
33
  ...route.params,
33
- navigation: new Navigation(navigation),
34
+ navigation: new Navigation(navigation, theme),
34
35
  };
35
36
  delete params.screen;
36
37
 
@@ -54,6 +55,7 @@ const ModalScreen: React.FC<ModalParams> = ({navigation, route, ...rest}) => {
54
55
  */
55
56
  if (isBottomSheet) {
56
57
  const bgColor = backgroundColor ?? theme.colors.background.surface;
58
+ const sheetTheme = {...theme, assets: undefined};
57
59
  return (
58
60
  <BottomSheet {...rest} onDismiss={onDismiss}>
59
61
  <View style={[styles.sheetContainer, {backgroundColor: bgColor}]}>
@@ -66,26 +68,22 @@ const ModalScreen: React.FC<ModalParams> = ({navigation, route, ...rest}) => {
66
68
  />
67
69
  </View>
68
70
  <NavigationContainer
69
- theme={{...theme, assets: undefined}}
70
- screen={props => <Component {...params} {...props} />}
71
- onDismiss={() => {
72
- navigation.pop();
73
- }}
74
- screenOptions={{
71
+ theme={sheetTheme}
72
+ screen={props => <Component {...props} {...params} />}
73
+ options={{
74
+ ...options,
75
+ hiddenBack: true,
76
+ headerTitleAlign: 'center',
75
77
  headerRight: (props: any) => (
76
78
  <HeaderRightAction>
77
79
  <NavigationButton
78
80
  icon="24_navigation_close"
79
81
  {...props}
80
- onPress={onDismiss}
82
+ onPress={() => navigation.pop()}
81
83
  />
82
84
  </HeaderRightAction>
83
85
  ),
84
- headerBackground: () => (
85
- <View style={[Styles.flex, {backgroundColor: bgColor}]} />
86
- ),
87
86
  }}
88
- hideBackFirst={true}
89
87
  />
90
88
  </View>
91
89
  </BottomSheet>
@@ -1,13 +1,15 @@
1
1
  import {NavigationProp} from '@react-navigation/native';
2
- import {NavigationOptions} from './types';
2
+ import {NavigationOptions, Theme} from './types';
3
3
  import {HeaderRightAction} from './index';
4
4
  import {getOptions} from './utils';
5
5
 
6
6
  class Navigation {
7
7
  instance: NavigationProp<any>;
8
+ theme: Theme;
8
9
 
9
- constructor(instance: any) {
10
+ constructor(instance: any, theme: Theme) {
10
11
  this.instance = instance;
12
+ this.theme = theme;
11
13
  }
12
14
 
13
15
  verifyParams = (params: NavigationOptions) => {
@@ -17,9 +19,9 @@ class Navigation {
17
19
  console.warn(
18
20
  'headerRight not return element type of HeaderRightAction, Please migrate to use HeaderRightAction of kits.',
19
21
  );
20
- return;
21
22
  }
22
23
  }
24
+ return params;
23
25
  };
24
26
 
25
27
  filterParams = (params: NavigationOptions) => {
@@ -27,9 +29,8 @@ class Navigation {
27
29
  };
28
30
 
29
31
  setOptions = (params: NavigationOptions) => {
30
- this.verifyParams(params);
31
- params = this.filterParams(params);
32
- params = getOptions(params);
32
+ params = this.verifyParams(params);
33
+ params = getOptions(params, this.theme);
33
34
  this.instance.setOptions(params);
34
35
  };
35
36
  }
@@ -1,4 +1,4 @@
1
- import React, {createContext, useRef} from 'react';
1
+ import React, {createContext, useEffect, useRef} from 'react';
2
2
  import {BottomSheetModalProvider} from '@gorhom/bottom-sheet';
3
3
  import {SafeAreaProvider} from 'react-native-safe-area-context';
4
4
  import {
@@ -11,7 +11,7 @@ import ModalScreen from './ModalScreen';
11
11
  import Navigator from './Navigator';
12
12
  import {getDialogOptions, getModalOptions, getStackOptions} from './utils';
13
13
  import {GridSystem, useGridSystem} from '../Layout';
14
- import {defaultContext, defaultTheme} from '../Consts';
14
+ import {defaultContext} from '../Consts';
15
15
  import {NavigationContainerProps} from './types';
16
16
 
17
17
  const Stack = createStackNavigator();
@@ -20,23 +20,17 @@ const ApplicationContext = createContext(defaultContext);
20
20
  const NavigationContainer: React.FC<NavigationContainerProps> = ({
21
21
  screen,
22
22
  theme,
23
- onDismiss,
24
- screenOptions,
25
- hideBackFirst,
23
+ options,
26
24
  }) => {
27
25
  const grid = useGridSystem();
28
26
  const navigationRef = React.useRef<NavigationContainerRef>(null);
29
27
  const navigator = useRef(new Navigator(navigationRef));
30
- const themed: any = theme;
31
28
 
32
- const goBack = () => {
33
- const canGoBack = navigationRef?.current?.canGoBack();
34
- if (canGoBack) {
35
- navigationRef?.current?.goBack();
36
- } else {
37
- onDismiss?.();
29
+ useEffect(() => {
30
+ if (options) {
31
+ navigationRef.current?.setParams({options: options});
38
32
  }
39
- };
33
+ }, []);
40
34
 
41
35
  return (
42
36
  <SafeAreaProvider>
@@ -44,20 +38,31 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
44
38
  <ApplicationContext.Provider
45
39
  value={{theme, navigator: navigator.current}}>
46
40
  <ReactNavigationContainer
47
- theme={themed}
41
+ theme={{
42
+ ...theme,
43
+ dark: false,
44
+ colors: {
45
+ ...theme.colors,
46
+ background: theme.colors.background.default,
47
+ card: theme.colors.background.surface,
48
+ text: theme.colors.text.default,
49
+ border: theme.colors.border.default,
50
+ notification: theme.colors.error.primary,
51
+ },
52
+ }}
48
53
  ref={navigator.current?.ref}
49
54
  independent={true}>
50
55
  <Stack.Navigator initialRouteName="Stack" headerMode="screen">
51
56
  <Stack.Screen
52
57
  name="Stack"
53
58
  component={StackScreen}
54
- initialParams={{screen, hideBackFirst}}
55
- options={{...getStackOptions(theme, goBack), ...screenOptions}}
59
+ initialParams={{screen}}
60
+ options={getStackOptions(theme)}
56
61
  />
57
62
  <Stack.Screen
58
63
  name="Dialog"
59
64
  component={StackScreen}
60
- options={getDialogOptions(theme, goBack)}
65
+ options={getDialogOptions(theme)}
61
66
  initialParams={{screen}}
62
67
  />
63
68
  <Stack.Screen
@@ -1,23 +1,28 @@
1
- import React, {useLayoutEffect} from 'react';
1
+ import React, {useContext, useEffect} from 'react';
2
2
  import {ScreenParams} from './types';
3
3
  import Navigation from './Navigation';
4
+ import {ApplicationContext} from './NavigationContainer';
4
5
 
5
6
  const StackScreen: React.FC<any> = props => {
6
7
  const {route, navigation} = props;
7
- const {screen: Component, hideBackFirst}: ScreenParams = route.params;
8
+ const {screen: Component, options}: ScreenParams = route.params;
9
+ const {theme} = useContext(ApplicationContext);
8
10
 
9
11
  const params = {
10
12
  ...route.params,
11
- navigation: new Navigation(navigation),
13
+ navigation: new Navigation(navigation, theme),
12
14
  };
13
15
 
14
16
  delete params.screen;
15
17
 
16
- useLayoutEffect(() => {
17
- if (hideBackFirst && !navigation?.canGoBack()) {
18
- navigation?.setOptions({headerLeft: null});
18
+ /**
19
+ * handle set options for navigation if have props options
20
+ */
21
+ useEffect(() => {
22
+ if (options) {
23
+ params.navigation.setOptions(options);
19
24
  }
20
- }, []);
25
+ }, [navigation, options]);
21
26
 
22
27
  return <Component {...params} />;
23
28
  };
@@ -1,8 +1,6 @@
1
1
  import {ViewProps} from 'react-native';
2
2
  import React from 'react';
3
3
  import Navigator from './Navigator';
4
- import Navigation from './Navigation';
5
- import {StackNavigationOptions} from '@react-navigation/stack';
6
4
 
7
5
  export type Theme = {
8
6
  dark: boolean;
@@ -66,15 +64,11 @@ export type Context = {
66
64
 
67
65
  export type NavigationContainerProps = {
68
66
  screen: React.ElementType;
69
- screenOptions?: StackNavigationOptions;
70
- hideBackFirst?: boolean;
67
+ options?: NavigationOptions;
71
68
  theme: Theme;
72
- onDismiss?: () => void;
73
69
  };
74
70
 
75
71
  export interface ScreenContainerProps extends ViewProps {
76
- navigation: Navigation;
77
- options?: NavigationOptions;
78
72
  edges?: any[];
79
73
  enableKeyboardAvoidingView?: boolean;
80
74
  scrollable: boolean;
@@ -102,6 +96,7 @@ export type NavigationButtonProps = {
102
96
 
103
97
  export type NavigationOptions = {
104
98
  surface?: boolean;
99
+ hiddenBack?: boolean;
105
100
  title?: string;
106
101
  headerTitleAlign?: 'left' | 'center';
107
102
  customTitle?: TitleCustomProps;
@@ -109,7 +104,6 @@ export type NavigationOptions = {
109
104
  };
110
105
 
111
106
  export type HeaderBackgroundProps = {
112
- surface?: boolean;
113
107
  image?: string;
114
108
  };
115
109
 
@@ -117,5 +111,7 @@ export type TitleCustomProps = {
117
111
  title?: string;
118
112
  subTitle?: string;
119
113
  image?: string;
114
+ dotColor?: string;
115
+ tintColor?: string;
120
116
  content?: React.ReactNode;
121
117
  };
@@ -3,13 +3,12 @@ import {
3
3
  StackNavigationOptions,
4
4
  TransitionPresets,
5
5
  } from '@react-navigation/stack';
6
- import {HeaderBackground, HeaderCustom} from './Components';
6
+ import {HeaderBackground, HeaderCustom, HeaderLeft} from './Components';
7
7
  import {NavigationOptions, Theme} from './types';
8
8
  import {Colors} from '../Consts';
9
9
  import {Text} from '../Text';
10
- import {NavigationButton} from './index';
11
10
 
12
- const renderTitle = (props: any) => {
11
+ const HeaderTitle = (props: any) => {
13
12
  return (
14
13
  <Text
15
14
  {...props}
@@ -31,32 +30,26 @@ const getTintColor = (theme: Theme): any => {
31
30
  };
32
31
  };
33
32
 
34
- const getStackOptions = (
35
- theme: Theme,
36
- goBack: () => void,
37
- ): StackNavigationOptions => {
33
+ const getStackOptions = (theme: Theme): StackNavigationOptions => {
38
34
  return {
39
35
  headerTitleAlign: 'center',
40
- headerTitle: renderTitle,
41
- headerBackground: HeaderBackground,
42
- headerLeft: (props: any) => (
43
- <NavigationButton icon="ic_back" {...props} onPress={goBack} />
36
+ headerTitle: HeaderTitle,
37
+ headerBackground: () => (
38
+ <HeaderBackground image={theme.assets?.headerBackground} />
44
39
  ),
40
+ headerLeft: (props: any) => <HeaderLeft {...props} />,
45
41
  ...getTintColor(theme),
46
42
  };
47
43
  };
48
44
 
49
- const getDialogOptions = (
50
- theme: Theme,
51
- goBack: () => void,
52
- ): StackNavigationOptions => {
45
+ const getDialogOptions = (theme: Theme): StackNavigationOptions => {
53
46
  return {
54
47
  headerTitleAlign: 'center',
55
- headerTitle: renderTitle,
56
- headerLeft: (props: any) => (
57
- <NavigationButton icon="ic_back" {...props} onPress={goBack} />
48
+ headerTitle: HeaderTitle,
49
+ headerLeft: (props: any) => <HeaderLeft {...props} />,
50
+ headerBackground: () => (
51
+ <HeaderBackground image={theme.assets?.headerBackground} />
58
52
  ),
59
- headerBackground: HeaderBackground,
60
53
  ...getTintColor(theme),
61
54
  ...TransitionPresets.ModalTransition,
62
55
  };
@@ -87,21 +80,52 @@ const getModalOptions = (): StackNavigationOptions => {
87
80
  };
88
81
  };
89
82
 
90
- const getOptions = (params: NavigationOptions) => {
91
- let surfaceTheme = {};
92
- let titleTheme = {};
93
- if (params.surface) {
83
+ const getOptions = (params: NavigationOptions, theme: Theme) => {
84
+ let backTheme: {};
85
+ let surfaceTheme: {};
86
+ let titleTheme: {};
87
+
88
+ if (params.hiddenBack == true) {
89
+ backTheme = {
90
+ headerLeft: null,
91
+ };
92
+ } else {
93
+ backTheme = {
94
+ headerLeft: (props: any) => <HeaderLeft {...props} />,
95
+ };
96
+ }
97
+
98
+ if (params.surface == true) {
99
+ surfaceTheme = {
100
+ headerBackground: () => <HeaderBackground />,
101
+ ...getTintColor({
102
+ ...theme,
103
+ assets: {...theme.assets, headerBackground: undefined},
104
+ }),
105
+ };
106
+ } else {
94
107
  surfaceTheme = {
95
- headerBackground: () => <HeaderBackground surface={true} />,
96
- headerTintColor: undefined,
108
+ headerBackground: () => (
109
+ <HeaderBackground image={theme.assets?.headerBackground} />
110
+ ),
111
+ ...getTintColor(theme),
97
112
  };
98
113
  }
114
+
99
115
  if (params.customTitle) {
100
116
  titleTheme = {
101
- headerTitle: () => <HeaderCustom {...params.customTitle} />,
117
+ headerTitleAlign: 'left',
118
+ headerTitle: (props: any) => {
119
+ return <HeaderCustom {...params.customTitle} {...props} />;
120
+ },
121
+ };
122
+ } else {
123
+ titleTheme = {
124
+ headerTitle: HeaderTitle,
102
125
  };
103
126
  }
104
- return {...params, ...surfaceTheme, ...titleTheme};
127
+
128
+ return {...params, ...backTheme, ...surfaceTheme, ...titleTheme};
105
129
  };
106
130
 
107
131
  export {getStackOptions, getDialogOptions, getModalOptions, getOptions};