@momo-kits/foundation 0.81.52 → 0.81.53-beta.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 (60) hide show
  1. package/Application/BottomSheet.tsx +216 -0
  2. package/Application/BottomTab/BottomTabBar.tsx +278 -0
  3. package/Application/{BottomTab.tsx → BottomTab/index.tsx} +43 -18
  4. package/Application/Components.tsx +71 -53
  5. package/Application/ModalScreen.tsx +12 -183
  6. package/Application/Navigation.ts +2 -2
  7. package/Application/NavigationContainer.tsx +54 -53
  8. package/Application/Navigator.ts +53 -35
  9. package/Application/StackScreen.tsx +225 -30
  10. package/Application/index.ts +8 -12
  11. package/Application/types.ts +45 -27
  12. package/Application/utils.tsx +19 -15
  13. package/Assets/icon_bank.json +1 -1
  14. package/Badge/Badge.tsx +1 -1
  15. package/Button/index.tsx +44 -25
  16. package/CheckBox/index.tsx +37 -26
  17. package/CheckBox/types.ts +5 -3
  18. package/Consts/colors+spacing+radius.ts +14 -0
  19. package/Icon/index.tsx +9 -8
  20. package/Icon/types.ts +2 -2
  21. package/IconButton/index.tsx +24 -14
  22. package/Image/index.tsx +23 -8
  23. package/Image/types.ts +2 -0
  24. package/Input/Input.tsx +116 -26
  25. package/Input/InputDropDown.tsx +66 -51
  26. package/Input/InputMoney.tsx +106 -15
  27. package/Input/InputOTP.tsx +45 -22
  28. package/Input/InputSearch.tsx +92 -44
  29. package/Input/InputTextArea.tsx +26 -6
  30. package/Input/common.tsx +48 -22
  31. package/Input/index.tsx +94 -77
  32. package/Input/styles.ts +35 -5
  33. package/Input/utils.ts +1 -1
  34. package/Layout/Card.tsx +4 -3
  35. package/Layout/FloatingButton.tsx +168 -0
  36. package/Layout/GridSystem.tsx +1 -1
  37. package/Layout/ItemList.tsx +3 -2
  38. package/Layout/Screen.tsx +87 -49
  39. package/Layout/Section.tsx +4 -3
  40. package/Layout/styles.ts +11 -7
  41. package/Layout/utils.ts +4 -4
  42. package/Loader/index.tsx +2 -0
  43. package/Popup/PopupNotify.tsx +55 -44
  44. package/Popup/PopupPromotion.tsx +5 -7
  45. package/Popup/types.ts +1 -7
  46. package/Radio/index.tsx +35 -20
  47. package/Radio/types.ts +5 -3
  48. package/Switch/index.tsx +32 -18
  49. package/Switch/types.ts +5 -3
  50. package/Tag/styles.ts +3 -2
  51. package/Text/index.tsx +24 -5
  52. package/Text/styles.ts +8 -8
  53. package/Title/index.tsx +170 -103
  54. package/Title/styles.ts +36 -9
  55. package/Title/types.ts +16 -48
  56. package/code-scanner.js +125 -16
  57. package/index.ts +7 -0
  58. package/package.json +6 -6
  59. package/publish.sh +16 -21
  60. package/tsconfig.json +1 -1
@@ -37,10 +37,11 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
37
37
  useBorder = true,
38
38
  badgeType,
39
39
  badgeValue,
40
+ accessibilityLabel = '',
40
41
  }) => {
41
42
  const {theme} = useContext(ApplicationContext);
42
43
  let buttonStyle: ViewStyle = {backgroundColor: '#00000066'};
43
- if (tintColor != Colors.black_01) {
44
+ if (tintColor !== Colors.black_01) {
44
45
  buttonStyle = {
45
46
  backgroundColor: 'transparent',
46
47
  borderColor: '#00000066',
@@ -64,8 +65,9 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
64
65
  }, [badgeType, badgeValue]);
65
66
 
66
67
  return (
67
- <View style={{width: scaleSize(28)}}>
68
+ <View style={{width: 28}}>
68
69
  <TouchableOpacity
70
+ accessibilityLabel={accessibilityLabel}
69
71
  style={[styles.navigationButton, buttonStyle]}
70
72
  onPress={onPress}>
71
73
  <Icon
@@ -90,6 +92,7 @@ const HeaderTitle: React.FC<any> = props => {
90
92
  return (
91
93
  <Animated.Text
92
94
  {...props}
95
+ accessibilityLabel={'title_navigation_header'}
93
96
  style={[
94
97
  styles.title,
95
98
  {fontFamily: `${theme.font}-Bold`, opacity, color: props.tintColor},
@@ -104,13 +107,13 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
104
107
  onPressLeftHeader,
105
108
  useBorder,
106
109
  }) => {
107
- const context = useContext(MiniAppContext);
110
+ const context = useContext<any>(MiniAppContext);
108
111
  const {navigator} = useContext(ApplicationContext);
109
112
 
110
113
  useEffect(() => {
111
114
  const backHandler = BackHandler.addEventListener(
112
115
  'hardwareBackPress',
113
- goBackSafe,
116
+ goBackSafe
114
117
  );
115
118
 
116
119
  return () => backHandler.remove();
@@ -118,20 +121,25 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
118
121
 
119
122
  const goBackSafe = () => {
120
123
  const goBack = () => {
121
- const canGoBack = navigator?.ref.current?.canGoBack();
122
- if (!canGoBack) {
123
- const currentRoute = navigator?.ref?.current.getCurrentRoute();
124
- navigator?.maxApi?.trackEvent?.('service_icon_clicked', {
125
- ...context,
126
- screen_name: currentRoute?.params?.screen?.name ?? currentRoute?.name,
127
- trigger_id: '111154000000000000',
128
- icon_name: 'back',
129
- });
130
- }
124
+ const canGoBack = navigator?.ref?.current?.canGoBack?.();
125
+ const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
126
+ const params = {
127
+ appId: context.appId,
128
+ code: context.code,
129
+ screen_name: currentRoute?.params?.screen?.name ?? currentRoute?.name,
130
+ trigger_id: '111154000000000000',
131
+ icon_name: 'back',
132
+ };
133
+ navigator?.maxApi?.showToastDebug?.({
134
+ appId: `auto - ${context.appId}`,
135
+ message: 'service_icon_clicked',
136
+ params,
137
+ });
138
+ navigator?.maxApi?.trackEvent?.('service_icon_clicked', params);
131
139
  if (canGoBack) {
132
- navigator?.ref.current?.goBack();
140
+ navigator?.ref?.current?.goBack?.();
133
141
  } else if (navigator?.maxApi) {
134
- navigator?.maxApi?.dismiss(navigator.dismissData);
142
+ navigator?.maxApi?.dismiss?.(navigator?.dismissData);
135
143
  } else {
136
144
  const currentTime = new Date().getTime();
137
145
  const requestId = `navigationBackPress#${currentTime}`;
@@ -160,10 +168,10 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
160
168
  }
161
169
  return true;
162
170
  };
163
-
164
171
  return (
165
172
  <View style={styles.headerLeft}>
166
173
  <NavigationButton
174
+ accessibilityLabel="btn_navigation_back"
167
175
  icon="ic_back"
168
176
  tintColor={tintColor}
169
177
  onPress={goBackSafe}
@@ -175,15 +183,18 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
175
183
 
176
184
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
177
185
  image,
178
- backgroundColor,
179
186
  animatedValue,
180
- useBorder = false,
187
+ useSurface = false,
181
188
  }) => {
182
189
  const {theme} = useContext(ApplicationContext);
183
190
  let headerImage = theme.assets?.headerBackground;
191
+ let backgroundColor;
184
192
  if (image === null) {
185
193
  headerImage = undefined;
186
194
  }
195
+ if (useSurface) {
196
+ backgroundColor = theme.colors.background.surface;
197
+ }
187
198
  const opacity = animatedValue?.interpolate({
188
199
  inputRange: [0, 114],
189
200
  outputRange: [0, 1],
@@ -195,9 +206,9 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
195
206
  style={[
196
207
  Styles.flex,
197
208
  {
198
- backgroundColor: backgroundColor ?? theme.colors.background.default,
209
+ backgroundColor,
199
210
  overflow: 'hidden',
200
- borderBottomWidth: useBorder ? 1 : 0,
211
+ borderBottomWidth: useSurface ? 1 : 0,
201
212
  borderColor: theme.colors.border.default,
202
213
  opacity,
203
214
  },
@@ -227,7 +238,7 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
227
238
  tintColor,
228
239
  dotColor,
229
240
  }) => {
230
- const avatarStyle = imageShape == 'circle' ? styles.circle : styles.square;
241
+ const avatarStyle = imageShape === 'circle' ? styles.circle : styles.square;
231
242
  const header = (
232
243
  <View style={Styles.row}>
233
244
  <View>
@@ -237,7 +248,10 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
237
248
  )}
238
249
  </View>
239
250
  <View style={[Styles.flex, {paddingHorizontal: 8}]}>
240
- <Text typography="header_default_bold" color={tintColor}>
251
+ <Text
252
+ accessibilityLabel={'title_navigation_header'}
253
+ typography="header_default_bold"
254
+ color={tintColor}>
241
255
  {title}
242
256
  </Text>
243
257
  <Text typography="description_default_regular" color={tintColor}>
@@ -251,10 +265,10 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
251
265
 
252
266
  const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
253
267
  const validateType = (child: React.ReactElement) => {
254
- const valid = child?.type === NavigationButton || child?.type == Fragment;
268
+ const valid = child?.type === NavigationButton || child?.type === Fragment;
255
269
  if (__DEV__ && !valid) {
256
270
  console.warn(
257
- 'HeaderRightAction contains element type of NavigationButton, Please check again.',
271
+ 'HeaderRightAction contains element type of NavigationButton, Please check again.'
258
272
  );
259
273
  }
260
274
  return child;
@@ -284,16 +298,16 @@ const HeaderToolkitAction: React.FC<any> = ({
284
298
  tintColor,
285
299
  pinnedTool,
286
300
  runtimeTools = [],
287
- preventBack,
301
+ preventClose,
288
302
  }) => {
289
- const {theme, navigator} = useContext(ApplicationContext);
290
- const miniContext = useContext(MiniAppContext);
303
+ const {navigator} = useContext(ApplicationContext);
304
+ const miniContext = useContext<any>(MiniAppContext);
291
305
  const [toolConfig, setToolConfig] = useState<any>();
292
306
  const {onAction} = toolConfig ?? {};
293
307
 
294
308
  let backgroundColor;
295
- if (tintColor != Colors.black_01) {
296
- backgroundColor = theme.colors.background.surface;
309
+ if (tintColor !== Colors.black_01) {
310
+ backgroundColor = 'transparent';
297
311
  }
298
312
 
299
313
  useEffect(() => {
@@ -313,7 +327,7 @@ const HeaderToolkitAction: React.FC<any> = ({
313
327
  (config: any) => {
314
328
  navigator.toolkitConfig = config;
315
329
  setToolConfig(navigator?.toolkitConfig);
316
- },
330
+ }
317
331
  );
318
332
  };
319
333
 
@@ -324,26 +338,26 @@ const HeaderToolkitAction: React.FC<any> = ({
324
338
  {runtimeTools},
325
339
  (res: {item: {action?: string; key: string}}) => {
326
340
  const {item} = res;
327
- navigator?.toolkitCallback?.(item);
341
+ navigator?.toolkitCallback?.(item.key);
328
342
  getToolkitConfig();
329
- },
343
+ }
330
344
  );
331
345
  };
332
346
 
333
347
  const onClose = () => {
334
- if (preventBack) {
348
+ if (preventClose) {
335
349
  navigator?.showModal({
336
350
  screen: () => (
337
351
  <PopupNotify
338
- {...preventBack}
352
+ {...preventClose}
339
353
  primary={{
340
- title: preventBack.primary.title,
354
+ title: preventClose.primary.title,
341
355
  onPress: () => {
342
- preventBack?.primary.onPress();
356
+ preventClose?.primary.onPress();
343
357
  navigator?.maxApi?.dispatchFunction?.(
344
358
  'dismiss',
345
359
  undefined,
346
- undefined,
360
+ undefined
347
361
  );
348
362
  },
349
363
  }}
@@ -363,6 +377,7 @@ const HeaderToolkitAction: React.FC<any> = ({
363
377
  if (pinTool)
364
378
  return (
365
379
  <NavigationButton
380
+ accessibilityLabel="btn_navigation_pin"
366
381
  icon={pinTool.icon as string}
367
382
  tintColor={tintColor}
368
383
  badgeValue={badgeValue}
@@ -373,14 +388,13 @@ const HeaderToolkitAction: React.FC<any> = ({
373
388
  [pinTool?.key],
374
389
  () => {
375
390
  getToolkitConfig();
376
- },
391
+ }
377
392
  );
378
393
  navigator?.toolkitCallback?.(pinTool.key);
379
394
  }}
380
395
  />
381
396
  );
382
397
  };
383
-
384
398
  if (toolConfig) {
385
399
  return (
386
400
  <View style={styles.headerRightButton}>
@@ -393,11 +407,15 @@ const HeaderToolkitAction: React.FC<any> = ({
393
407
  borderWidth: backgroundColor ? 0.5 : 0,
394
408
  },
395
409
  ]}>
396
- <TouchableOpacity onPress={onMore}>
410
+ <TouchableOpacity
411
+ accessibilityLabel={'btn_navigation_more'}
412
+ onPress={onMore}>
397
413
  <Icon color={tintColor} source="navigation_more_horiz" size={20} />
398
414
  </TouchableOpacity>
399
415
  <View style={[styles.divider, {backgroundColor: tintColor}]} />
400
- <TouchableOpacity onPress={onClose}>
416
+ <TouchableOpacity
417
+ accessibilityLabel={'btn_navigation_close'}
418
+ onPress={onClose}>
401
419
  <Icon
402
420
  color={tintColor}
403
421
  source="16_navigation_close_circle"
@@ -415,7 +433,7 @@ const HeaderToolkitAction: React.FC<any> = ({
415
433
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
416
434
  animatedValue,
417
435
  image,
418
- onPress,
436
+ children,
419
437
  }) => {
420
438
  const scale = animatedValue.interpolate({
421
439
  inputRange: [-300, 0, 300],
@@ -434,21 +452,21 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
434
452
  });
435
453
 
436
454
  return (
437
- <TouchableOpacity onPress={onPress}>
438
- <Animated.View
439
- style={{
440
- height: 210,
441
- opacity: opacity,
442
- transform: [{scale}, {translateY}],
443
- }}>
455
+ <Animated.View
456
+ style={{
457
+ height: 210,
458
+ opacity: opacity,
459
+ transform: [{scale}, {translateY}],
460
+ }}>
461
+ {children ?? (
444
462
  <Image
445
463
  source={{
446
464
  uri: image,
447
465
  }}
448
466
  style={{width: '100%', height: '100%'}}
449
467
  />
450
- </Animated.View>
451
- </TouchableOpacity>
468
+ )}
469
+ </Animated.View>
452
470
  );
453
471
  };
454
472
 
@@ -488,7 +506,7 @@ const styles = StyleSheet.create({
488
506
  flexDirection: 'row',
489
507
  justifyContent: 'center',
490
508
  alignItems: 'center',
491
- paddingRight: Spacing.S,
509
+ paddingRight: Spacing.M,
492
510
  },
493
511
  toolkitContainer: {
494
512
  marginLeft: Spacing.S,
@@ -1,40 +1,19 @@
1
- import React, {useContext, useEffect, useMemo, useRef, useState} from 'react';
1
+ import React, {useContext, useEffect, useRef} from 'react';
2
2
  import {
3
- Dimensions,
4
- Keyboard,
5
3
  KeyboardAvoidingView,
6
4
  Platform,
7
5
  Pressable,
8
6
  StyleSheet,
9
7
  View,
10
8
  } from 'react-native';
11
- import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
12
- import {
13
- BottomSheetBackdrop,
14
- BottomSheetModal,
15
- BottomSheetView,
16
- } from '@gorhom/bottom-sheet';
9
+
10
+ import {ApplicationContext} from './index';
11
+ import {Styles} from '../Consts';
17
12
  import Navigation from './Navigation';
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';
13
+ import {ModalParams} from './types';
14
+ import BottomSheet from './BottomSheet';
27
15
 
28
16
  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
-
38
17
  if (props.route?.params?.isBottomSheet) {
39
18
  return <BottomSheet {...props} />;
40
19
  }
@@ -50,9 +29,14 @@ const Modal: React.FC<ModalParams> = props => {
50
29
  ...props.route.params,
51
30
  navigation,
52
31
  };
53
-
54
32
  delete params.screen;
55
33
 
34
+ useEffect(() => {
35
+ return () => {
36
+ props.route.params?.onDismiss?.();
37
+ };
38
+ }, []);
39
+
56
40
  const onDismiss = () => {
57
41
  if (barrierDismissible) {
58
42
  return;
@@ -91,142 +75,6 @@ const Modal: React.FC<ModalParams> = props => {
91
75
  );
92
76
  };
93
77
 
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
-
230
78
  const styles = StyleSheet.create({
231
79
  container: {flex: 1, flexDirection: 'row'},
232
80
  modalContent: {
@@ -244,25 +92,6 @@ const styles = StyleSheet.create({
244
92
  flex: 1,
245
93
  minHeight: '10%',
246
94
  },
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
- },
266
95
  });
267
96
 
268
97
  export default ModalScreen;
@@ -6,8 +6,8 @@ import {getOptions} from './utils';
6
6
  import {HeaderToolkitAction} from './Components';
7
7
 
8
8
  class Navigation {
9
- private instance: NavigationProp<any>;
10
- private readonly theme: Theme;
9
+ instance: NavigationProp<any>;
10
+ readonly theme: Theme;
11
11
 
12
12
  constructor(instance: any, theme: Theme) {
13
13
  this.instance = instance;