@momo-kits/foundation 0.81.51-beta.0 → 0.81.52

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 (71) hide show
  1. package/Application/{BottomTab/index.tsx → BottomTab.tsx} +18 -42
  2. package/Application/Components.tsx +91 -356
  3. package/Application/ModalScreen.tsx +185 -14
  4. package/Application/Navigation.ts +19 -5
  5. package/Application/NavigationContainer.tsx +72 -62
  6. package/Application/Navigator.ts +48 -154
  7. package/Application/StackScreen.tsx +32 -289
  8. package/Application/index.ts +15 -13
  9. package/Application/types.ts +35 -81
  10. package/Application/utils.tsx +30 -76
  11. package/Assets/icon_bank.json +1 -1
  12. package/Badge/Badge.tsx +1 -1
  13. package/Badge/index.tsx +1 -2
  14. package/Badge/styles.ts +6 -50
  15. package/Button/index.tsx +26 -44
  16. package/CheckBox/index.tsx +26 -37
  17. package/CheckBox/types.ts +3 -5
  18. package/Consts/colors+spacing+radius.ts +0 -14
  19. package/Icon/index.tsx +8 -9
  20. package/Icon/types.ts +2 -2
  21. package/IconButton/index.tsx +14 -24
  22. package/Image/index.tsx +8 -23
  23. package/Image/types.ts +0 -2
  24. package/Input/Input.tsx +26 -116
  25. package/Input/InputDropDown.tsx +51 -66
  26. package/Input/InputMoney.tsx +15 -106
  27. package/Input/InputOTP.tsx +26 -49
  28. package/Input/InputSearch.tsx +99 -252
  29. package/Input/InputTextArea.tsx +6 -26
  30. package/Input/common.tsx +22 -48
  31. package/Input/index.tsx +77 -112
  32. package/Input/styles.ts +11 -48
  33. package/Input/utils.ts +1 -1
  34. package/Layout/Card.tsx +3 -4
  35. package/Layout/GridSystem.tsx +1 -1
  36. package/Layout/ItemList.tsx +2 -3
  37. package/Layout/Screen.tsx +121 -242
  38. package/Layout/Section.tsx +3 -4
  39. package/Layout/styles.ts +13 -11
  40. package/Layout/types.ts +1 -1
  41. package/Layout/utils.ts +4 -4
  42. package/Loader/index.tsx +0 -2
  43. package/Pagination/PaginationScroll.tsx +2 -2
  44. package/Pagination/styles.ts +1 -1
  45. package/Popup/PopupNotify.tsx +46 -57
  46. package/Popup/PopupPromotion.tsx +8 -6
  47. package/Popup/types.ts +7 -1
  48. package/Radio/index.tsx +20 -35
  49. package/Radio/types.ts +3 -5
  50. package/Switch/index.tsx +18 -32
  51. package/Switch/types.ts +3 -5
  52. package/Tag/index.tsx +2 -9
  53. package/Tag/styles.ts +2 -3
  54. package/Tag/types.ts +0 -12
  55. package/Text/index.tsx +5 -24
  56. package/Text/styles.ts +8 -8
  57. package/Title/index.tsx +103 -170
  58. package/Title/styles.ts +9 -36
  59. package/Title/types.ts +48 -16
  60. package/code-scanner.js +16 -125
  61. package/index.ts +0 -7
  62. package/package.json +7 -8
  63. package/publish.sh +21 -16
  64. package/tsconfig.json +1 -1
  65. package/Application/BottomSheet.tsx +0 -216
  66. package/Application/BottomTab/BottomTabBar.tsx +0 -278
  67. package/Application/Localize.ts +0 -44
  68. package/Application/constants.ts +0 -9
  69. package/Badge/BadgeDotAnimation.tsx +0 -95
  70. package/Input/TextTyping.tsx +0 -113
  71. package/Layout/FloatingButton.tsx +0 -168
@@ -1,19 +1,40 @@
1
- import React, {useContext, useEffect, useRef} from 'react';
1
+ import React, {useContext, useEffect, useMemo, useRef, useState} from 'react';
2
2
  import {
3
+ Dimensions,
4
+ Keyboard,
3
5
  KeyboardAvoidingView,
4
6
  Platform,
5
7
  Pressable,
6
8
  StyleSheet,
7
9
  View,
8
10
  } from 'react-native';
9
-
10
- import {ApplicationContext} from './index';
11
- import {Styles} from '../Consts';
11
+ import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
12
+ import {
13
+ BottomSheetBackdrop,
14
+ BottomSheetModal,
15
+ BottomSheetView,
16
+ } from '@gorhom/bottom-sheet';
12
17
  import Navigation from './Navigation';
13
- import {ModalParams} from './types';
14
- import BottomSheet from './BottomSheet';
18
+ import {BottomSheetParams, ModalParams} from './types';
19
+ import {Radius, Spacing, Styles} from '../Consts';
20
+ import {
21
+ ApplicationContext,
22
+ HeaderRightAction,
23
+ NavigationButton,
24
+ NavigationContainer,
25
+ } from './index';
26
+ import {HeaderBackground, HeaderLeft, HeaderTitle} from './Components';
15
27
 
16
28
  const ModalScreen: React.FC<any> = props => {
29
+ /**
30
+ * dismiss callback when modal | bottom sheet dismissed
31
+ */
32
+ useEffect(() => {
33
+ return () => {
34
+ props.route.params?.onDismiss?.();
35
+ };
36
+ }, []);
37
+
17
38
  if (props.route?.params?.isBottomSheet) {
18
39
  return <BottomSheet {...props} />;
19
40
  }
@@ -21,21 +42,16 @@ const ModalScreen: React.FC<any> = props => {
21
42
  };
22
43
 
23
44
  const Modal: React.FC<ModalParams> = props => {
24
- const {navigator} = useContext(ApplicationContext);
45
+ const {theme, navigator} = useContext(ApplicationContext);
25
46
  const {screen, barrierDismissible} = props.route.params;
26
47
  const Component = useRef(screen).current;
27
- const navigation = new Navigation(props.navigation);
48
+ const navigation = new Navigation(props.navigation, theme);
28
49
  const params = {
29
50
  ...props.route.params,
30
51
  navigation,
31
52
  };
32
- delete params.screen;
33
53
 
34
- useEffect(() => {
35
- return () => {
36
- props.route.params?.onDismiss?.();
37
- };
38
- }, []);
54
+ delete params.screen;
39
55
 
40
56
  const onDismiss = () => {
41
57
  if (barrierDismissible) {
@@ -75,6 +91,142 @@ const Modal: React.FC<ModalParams> = props => {
75
91
  );
76
92
  };
77
93
 
94
+ const BottomSheet: React.FC<BottomSheetParams> = props => {
95
+ const insets = useSafeAreaInsets();
96
+ const {theme, navigator} = useContext(ApplicationContext);
97
+ const [keyboardSnap, setKeyboardSnap] = useState(0);
98
+ const {screen, options, snapPoint, indicatorColor}: BottomSheetParams =
99
+ props.route.params;
100
+ const Component = useRef(screen).current;
101
+ const navigation = new Navigation(props.navigation, theme);
102
+ const params = {
103
+ ...props.route.params,
104
+ navigation,
105
+ };
106
+
107
+ delete params.screen;
108
+
109
+ const snapPoints = useMemo(() => {
110
+ if (snapPoint) {
111
+ const current = parseInt(snapPoint) + keyboardSnap * 100;
112
+ return [`${current}%`];
113
+ }
114
+ return ['50%', '70%', '90%'];
115
+ }, [keyboardSnap]);
116
+
117
+ const bottomSheetRef = useRef<any>(null);
118
+ const mountedRef = useRef(true);
119
+
120
+ let backgroundColor = theme.colors.background.default;
121
+ let bottomInset = keyboardSnap * Dimensions.get('window').height;
122
+ if (bottomInset > 16) {
123
+ bottomInset -= 16;
124
+ }
125
+ if (options?.surface) {
126
+ backgroundColor = theme.colors.background.surface;
127
+ }
128
+
129
+ useEffect(() => {
130
+ const keyboardDidShow = Keyboard.addListener('keyboardDidShow', e => {
131
+ setKeyboardSnap(
132
+ e.endCoordinates.height / Dimensions.get('window').height,
133
+ );
134
+ bottomSheetRef.current?.expand();
135
+ });
136
+
137
+ const keyboardDidHide = Keyboard.addListener('keyboardDidHide', () => {
138
+ setKeyboardSnap(0);
139
+ });
140
+
141
+ bottomSheetRef.current?.present();
142
+ return () => {
143
+ mountedRef.current = false;
144
+ keyboardDidShow.remove();
145
+ keyboardDidHide.remove();
146
+ };
147
+ }, []);
148
+
149
+ const backdropComponent = (backdropProps: any) => (
150
+ <BottomSheetBackdrop
151
+ disappearsOnIndex={-1}
152
+ appearsOnIndex={0}
153
+ {...backdropProps}
154
+ />
155
+ );
156
+
157
+ return (
158
+ <SafeAreaView style={Styles.flex}>
159
+ <BottomSheetModal
160
+ ref={bottomSheetRef}
161
+ snapPoints={snapPoints}
162
+ onDismiss={() => {
163
+ if (mountedRef.current) {
164
+ navigator?.pop();
165
+ }
166
+ }}
167
+ bottomInset={Platform.OS === 'ios' ? bottomInset : 0}
168
+ handleComponent={null}
169
+ backdropComponent={backdropComponent}>
170
+ <BottomSheetView>
171
+ <View style={[styles.sheetContainer, {backgroundColor}]}>
172
+ <View style={styles.indicatorContainer}>
173
+ <View
174
+ style={[
175
+ styles.indicator,
176
+ {backgroundColor: theme.colors.text.disable},
177
+ ]}
178
+ />
179
+ </View>
180
+ <NavigationContainer
181
+ maxApi={navigator?.maxApi}
182
+ theme={{
183
+ ...theme,
184
+ assets: {...theme.assets, headerBackground: undefined},
185
+ }}
186
+ screen={props => <Component {...props} {...params} />}
187
+ options={{
188
+ ...options,
189
+ ...{
190
+ headerLeft: (props: any) => {
191
+ if (props.canGoBack) {
192
+ return <HeaderLeft {...props} useBorder={false} />;
193
+ }
194
+ },
195
+ headerTitle: (props: any) => {
196
+ return <HeaderTitle {...props} />;
197
+ },
198
+ headerTintColor: theme.colors.text.default,
199
+ headerBackground: () => (
200
+ <HeaderBackground backgroundColor={backgroundColor} />
201
+ ),
202
+ headerTransparent: false,
203
+ },
204
+ headerTitleAlign: 'center',
205
+ headerRight: props => (
206
+ <HeaderRightAction>
207
+ <NavigationButton
208
+ {...props}
209
+ icon="24_navigation_close"
210
+ useBorder={false}
211
+ onPress={() => bottomSheetRef.current?.dismiss()}
212
+ />
213
+ </HeaderRightAction>
214
+ ),
215
+ }}
216
+ />
217
+ <View
218
+ style={{
219
+ height: Math.min(insets.bottom, 20),
220
+ backgroundColor: indicatorColor ?? backgroundColor,
221
+ }}
222
+ />
223
+ </View>
224
+ </BottomSheetView>
225
+ </BottomSheetModal>
226
+ </SafeAreaView>
227
+ );
228
+ };
229
+
78
230
  const styles = StyleSheet.create({
79
231
  container: {flex: 1, flexDirection: 'row'},
80
232
  modalContent: {
@@ -92,6 +244,25 @@ const styles = StyleSheet.create({
92
244
  flex: 1,
93
245
  minHeight: '10%',
94
246
  },
247
+ sheetContainer: {
248
+ height: '100%',
249
+ width: '100%',
250
+ borderTopLeftRadius: Radius.M,
251
+ borderTopRightRadius: Radius.M,
252
+ overflow: 'hidden',
253
+ },
254
+ indicatorContainer: {
255
+ width: '100%',
256
+ paddingTop: Spacing.S,
257
+ paddingBottom: Spacing.XS,
258
+ alignItems: 'center',
259
+ justifyContent: 'center',
260
+ },
261
+ indicator: {
262
+ width: 40,
263
+ height: 4,
264
+ borderRadius: Radius.S,
265
+ },
95
266
  });
96
267
 
97
268
  export default ModalScreen;
@@ -1,13 +1,17 @@
1
+ import React from 'react';
1
2
  import {NavigationProp} from '@react-navigation/native';
2
- import {NavigationOptions} from './types';
3
+ import {NavigationOptions, Theme} from './types';
4
+ import {HeaderRightAction} from './index';
3
5
  import {getOptions} from './utils';
6
+ import {HeaderToolkitAction} from './Components';
4
7
 
5
8
  class Navigation {
6
- instance: NavigationProp<any>;
7
- onHeaderRightChange?: (width: number) => void;
9
+ private instance: NavigationProp<any>;
10
+ private readonly theme: Theme;
8
11
 
9
- constructor(instance: any) {
12
+ constructor(instance: any, theme: Theme) {
10
13
  this.instance = instance;
14
+ this.theme = theme;
11
15
  }
12
16
 
13
17
  /**
@@ -15,6 +19,16 @@ class Navigation {
15
19
  * @param params
16
20
  */
17
21
  private filterParams = (params: NavigationOptions) => {
22
+ if (params.headerRight) {
23
+ const headerRight = params.headerRight?.({}) as React.ReactElement;
24
+ const type = headerRight?.type;
25
+ if (type !== HeaderRightAction && type !== HeaderToolkitAction) {
26
+ console.warn(
27
+ 'headerRight contains element type of HeaderRightAction | HeaderToolkitAction, Please check again.',
28
+ );
29
+ }
30
+ }
31
+
18
32
  if (params.headerLeft) {
19
33
  console.warn('headerLeft not allow override render by design system!');
20
34
  delete params.headerLeft;
@@ -28,7 +42,7 @@ class Navigation {
28
42
  * @param params
29
43
  */
30
44
  setOptions = (params: NavigationOptions) => {
31
- params = getOptions(this.filterParams(params), this);
45
+ params = getOptions(this.filterParams(params), this.theme);
32
46
  this.instance.setOptions(params);
33
47
  };
34
48
  }
@@ -1,4 +1,5 @@
1
- import React, {useEffect, useRef, useState} from 'react';
1
+ import React, {useContext, useEffect, useRef, useState} from 'react';
2
+ import {BottomSheetModalProvider} from '@gorhom/bottom-sheet';
2
3
  import {SafeAreaProvider} from 'react-native-safe-area-context';
3
4
  import {
4
5
  NavigationContainer as ReactNavigationContainer,
@@ -10,9 +11,9 @@ import StackScreen from './StackScreen';
10
11
  import ModalScreen from './ModalScreen';
11
12
  import Navigator from './Navigator';
12
13
  import {getDialogOptions, getModalOptions, getStackOptions} from './utils';
13
- import {NavigationContainerProps} from './types';
14
- import {ApplicationContext} from './index';
15
- import Localize from './Localize';
14
+ import {LocalizationObject, NavigationContainerProps} from './types';
15
+ import {ApplicationContext, MiniAppContext} from './index';
16
+ import kitLocalization from '../Assets/language.json';
16
17
 
17
18
  const Stack = createStackNavigator();
18
19
 
@@ -21,18 +22,14 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
21
22
  theme,
22
23
  options,
23
24
  maxApi,
24
- initialParams,
25
- localize = new Localize({vi: {}, en: {}}),
25
+ localization,
26
26
  }) => {
27
- const navigationRef = useRef<NavigationContainerRef>(null);
28
- const isReady = useRef(false);
29
- const navigator = useRef(new Navigator(navigationRef, isReady));
27
+ const context = useContext(MiniAppContext);
28
+ const navigationRef = React.useRef<NavigationContainerRef>(null);
29
+ const navigator = useRef(new Navigator(navigationRef));
30
30
  const [showGrid, setShowGrid] = useState(false);
31
31
  const config = useRef<any>();
32
- /**
33
- * inject data for navigator
34
- */
35
- navigator.current.maxApi = maxApi;
32
+
36
33
  /**
37
34
  * handle mini language & listen change
38
35
  * engine only shake to enable grid view
@@ -42,7 +39,7 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
42
39
  'onChangeGrid',
43
40
  enable => {
44
41
  setShowGrid(!!enable);
45
- }
42
+ },
46
43
  );
47
44
 
48
45
  return () => {
@@ -50,59 +47,72 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
50
47
  };
51
48
  }, []);
52
49
 
50
+ /**
51
+ * inject data for navigator
52
+ */
53
+ navigator.current.maxApi = maxApi;
54
+
55
+ /**
56
+ * translate
57
+ * @param key
58
+ */
53
59
  const translate = (key: string) => {
54
- return localize?.translate(key);
60
+ const language: 'vi' | 'en' = context?.language ?? 'vi';
61
+ const mergedLocalization: LocalizationObject = {
62
+ vi: {...localization?.vi, ...kitLocalization.vi},
63
+ en: {...localization?.en, ...kitLocalization.en},
64
+ };
65
+ return mergedLocalization?.[language]?.[key] || key;
55
66
  };
56
67
 
57
68
  return (
58
69
  <SafeAreaProvider>
59
- <ApplicationContext.Provider
60
- value={{
61
- navigator: navigator.current,
62
- config: config.current,
63
- theme,
64
- showGrid,
65
- translate,
66
- }}>
67
- <ReactNavigationContainer
68
- theme={{
69
- ...theme,
70
- colors: {
71
- ...theme.colors,
72
- background: theme.colors.background.default,
73
- card: theme.colors.background.surface,
74
- text: theme.colors.text.default,
75
- border: theme.colors.border.default,
76
- notification: theme.colors.error.primary,
77
- },
78
- }}
79
- ref={navigationRef}
80
- onReady={() => {
81
- isReady.current = true;
82
- }}
83
- independent={true}>
84
- <Stack.Navigator initialRouteName="Stack" headerMode="screen">
85
- <Stack.Screen
86
- name="Stack"
87
- component={StackScreen}
88
- initialParams={{screen, initialParams}}
89
- options={{...getStackOptions(), ...options}}
90
- />
91
- <Stack.Screen
92
- name="Dialog"
93
- component={StackScreen}
94
- options={getDialogOptions()}
95
- initialParams={{screen}}
96
- />
97
- <Stack.Screen
98
- name="Modal"
99
- component={ModalScreen}
100
- options={getModalOptions()}
101
- initialParams={{screen}}
102
- />
103
- </Stack.Navigator>
104
- </ReactNavigationContainer>
105
- </ApplicationContext.Provider>
70
+ <BottomSheetModalProvider>
71
+ <ApplicationContext.Provider
72
+ value={{
73
+ navigator: navigator.current,
74
+ config: config.current,
75
+ theme,
76
+ showGrid,
77
+ translate,
78
+ }}>
79
+ <ReactNavigationContainer
80
+ theme={{
81
+ ...theme,
82
+ colors: {
83
+ ...theme.colors,
84
+ background: theme.colors.background.default,
85
+ card: theme.colors.background.surface,
86
+ text: theme.colors.text.default,
87
+ border: theme.colors.border.default,
88
+ notification: theme.colors.error.primary,
89
+ },
90
+ }}
91
+ ref={navigator.current?.ref}
92
+ independent={true}>
93
+ <Stack.Navigator initialRouteName="Stack" headerMode="screen">
94
+ <Stack.Screen
95
+ name="Stack"
96
+ component={StackScreen}
97
+ initialParams={{screen}}
98
+ options={{...getStackOptions(), ...options}}
99
+ />
100
+ <Stack.Screen
101
+ name="Dialog"
102
+ component={StackScreen}
103
+ options={getDialogOptions()}
104
+ initialParams={{screen}}
105
+ />
106
+ <Stack.Screen
107
+ name="Modal"
108
+ component={ModalScreen}
109
+ options={getModalOptions()}
110
+ initialParams={{screen}}
111
+ />
112
+ </Stack.Navigator>
113
+ </ReactNavigationContainer>
114
+ </ApplicationContext.Provider>
115
+ </BottomSheetModalProvider>
106
116
  </SafeAreaProvider>
107
117
  );
108
118
  };