@momo-kits/foundation 0.75.1-beta.8 → 0.75.2-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 (112) hide show
  1. package/Application/BottomTab/BottomTabBar.tsx +478 -0
  2. package/Application/BottomTab/index.tsx +134 -0
  3. package/Application/Components.tsx +552 -0
  4. package/Application/ModalScreen.tsx +244 -0
  5. package/Application/Navigation.ts +50 -0
  6. package/Application/NavigationContainer.tsx +128 -0
  7. package/Application/Navigator.ts +151 -0
  8. package/Application/StackScreen.tsx +146 -0
  9. package/Application/index.ts +37 -0
  10. package/{Navigation → Application}/types.ts +96 -27
  11. package/Application/utils.tsx +175 -0
  12. package/Assets/DotAnimation.json +256 -0
  13. package/Assets/SpinnerAnimation.json +1027 -0
  14. package/Assets/icon.json +89 -5
  15. package/Assets/icon_bank.json +506 -0
  16. package/Assets/language.json +202 -0
  17. package/Badge/Badge.tsx +56 -0
  18. package/Badge/BadgeDot.tsx +11 -0
  19. package/Badge/BadgeRibbon.tsx +125 -0
  20. package/Badge/Shape.tsx +29 -0
  21. package/Badge/index.tsx +7 -0
  22. package/Badge/styles.ts +58 -0
  23. package/Badge/types.ts +63 -0
  24. package/Button/index.tsx +90 -39
  25. package/Button/styles.ts +19 -0
  26. package/CheckBox/index.tsx +58 -43
  27. package/CheckBox/styles.ts +1 -1
  28. package/CheckBox/types.ts +6 -4
  29. package/Consts/colors+spacing+radius.ts +18 -0
  30. package/Consts/styles.ts +0 -26
  31. package/Consts/theme.ts +5 -1
  32. package/Divider/index.tsx +3 -1
  33. package/Icon/index.tsx +14 -10
  34. package/Icon/types.ts +4 -3
  35. package/IconButton/index.tsx +24 -15
  36. package/Image/index.tsx +63 -45
  37. package/Image/styles.ts +1 -9
  38. package/Image/types.ts +5 -1
  39. package/Input/Input.tsx +227 -115
  40. package/Input/InputDropDown.tsx +132 -0
  41. package/Input/InputMoney.tsx +174 -110
  42. package/Input/InputOTP.tsx +257 -0
  43. package/Input/InputSearch.tsx +160 -130
  44. package/Input/InputTextArea.tsx +149 -121
  45. package/Input/common.tsx +43 -18
  46. package/Input/index.tsx +205 -17
  47. package/Input/styles.ts +44 -7
  48. package/Input/utils.ts +2 -2
  49. package/Layout/Card.tsx +154 -62
  50. package/Layout/FloatingButton.tsx +128 -0
  51. package/Layout/GridSystem.tsx +35 -23
  52. package/Layout/Item.tsx +56 -0
  53. package/Layout/ItemList.tsx +55 -0
  54. package/Layout/ItemSectionList.tsx +39 -0
  55. package/Layout/Screen.tsx +378 -0
  56. package/Layout/Section.tsx +74 -39
  57. package/Layout/index.ts +14 -3
  58. package/Layout/styles.ts +101 -0
  59. package/Layout/types.ts +1 -39
  60. package/Layout/utils.ts +42 -37
  61. package/Loader/DotLoader.tsx +37 -0
  62. package/Loader/Loader.tsx +13 -0
  63. package/Loader/ProgressBar.tsx +44 -0
  64. package/Loader/Spinner.tsx +29 -0
  65. package/Loader/index.tsx +4 -0
  66. package/Loader/styles.ts +10 -0
  67. package/Loader/types.ts +35 -0
  68. package/Loader/utils.ts +25 -0
  69. package/Pagination/Dot.tsx +1 -1
  70. package/Pagination/PaginationDot.tsx +8 -3
  71. package/Pagination/PaginationNumber.tsx +8 -7
  72. package/Pagination/PaginationScroll.tsx +1 -1
  73. package/Pagination/PaginationWhiteDot.tsx +8 -5
  74. package/Pagination/styles.ts +3 -0
  75. package/Pagination/types.ts +2 -0
  76. package/Popup/PopupNotify.tsx +102 -44
  77. package/Popup/PopupPromotion.tsx +63 -22
  78. package/Popup/types.ts +15 -4
  79. package/Radio/index.tsx +35 -21
  80. package/Radio/types.ts +7 -5
  81. package/Skeleton/index.tsx +1 -1
  82. package/Skeleton/types.ts +3 -0
  83. package/Switch/index.tsx +35 -24
  84. package/Switch/types.ts +5 -3
  85. package/Tag/index.tsx +1 -1
  86. package/Text/index.tsx +32 -26
  87. package/Text/styles.ts +38 -0
  88. package/Text/types.ts +25 -13
  89. package/Text/utils.ts +14 -0
  90. package/Title/index.tsx +186 -0
  91. package/Title/styles.ts +79 -0
  92. package/Title/types.ts +20 -0
  93. package/code-scanner.js +143 -0
  94. package/index.ts +17 -3
  95. package/migrate_typo.js +56 -0
  96. package/package.json +44 -39
  97. package/publish.sh +2 -5
  98. package/tsconfig.json +24 -0
  99. package/verify.js +47 -0
  100. package/Assets/header-background.png +0 -0
  101. package/Layout/ScreenContainer.tsx +0 -122
  102. package/Navigation/BottomTab.tsx +0 -39
  103. package/Navigation/Components.tsx +0 -365
  104. package/Navigation/ModalScreen.tsx +0 -226
  105. package/Navigation/Navigation.ts +0 -44
  106. package/Navigation/NavigationButton.tsx +0 -54
  107. package/Navigation/NavigationContainer.tsx +0 -77
  108. package/Navigation/Navigator.ts +0 -82
  109. package/Navigation/Observer.ts +0 -79
  110. package/Navigation/StackScreen.tsx +0 -27
  111. package/Navigation/index.ts +0 -14
  112. package/Navigation/utils.tsx +0 -137
@@ -0,0 +1,552 @@
1
+ import React, {
2
+ Fragment,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useState,
7
+ } from 'react';
8
+ import {
9
+ Animated,
10
+ BackHandler,
11
+ DeviceEventEmitter,
12
+ StatusBar,
13
+ StyleSheet,
14
+ TouchableOpacity,
15
+ View,
16
+ ViewStyle,
17
+ } from 'react-native';
18
+ import {ApplicationContext, MiniAppContext} from './index';
19
+ import {Colors, Radius, Spacing, Styles} from '../Consts';
20
+ import {Image} from '../Image';
21
+ import {
22
+ HeaderAnimatedProps,
23
+ HeaderBackgroundProps,
24
+ HeaderBackProps,
25
+ NavigationButtonProps,
26
+ TitleCustomProps,
27
+ } from './types';
28
+ import {scaleSize, Text} from '../Text';
29
+ import {Icon} from '../Icon';
30
+ import {PopupNotify} from '../Popup';
31
+ import {Badge, BadgeDot} from '../Badge';
32
+
33
+ const NavigationButton: React.FC<NavigationButtonProps> = ({
34
+ icon,
35
+ tintColor,
36
+ onPress,
37
+ useBorder = true,
38
+ badgeType,
39
+ badgeValue,
40
+ accessibilityLabel = '',
41
+ }) => {
42
+ const {theme} = useContext(ApplicationContext);
43
+ let buttonStyle: ViewStyle = {backgroundColor: '#00000066'};
44
+ if (tintColor != Colors.black_01) {
45
+ buttonStyle = {
46
+ backgroundColor: 'transparent',
47
+ borderColor: '#00000066',
48
+ borderWidth: useBorder ? 0.5 : 0,
49
+ };
50
+ }
51
+
52
+ const isNumber = (label: any) => {
53
+ const numberRegex = /^\d+$/;
54
+ return numberRegex.test(String(label));
55
+ };
56
+
57
+ const renderBadge = useCallback(() => {
58
+ if (badgeType === 'dot') {
59
+ return <BadgeDot size="small" style={styles.badgeDot} />;
60
+ }
61
+
62
+ if (isNumber(badgeValue)) {
63
+ return <Badge label={badgeValue} style={styles.badge} />;
64
+ }
65
+ }, [badgeType, badgeValue]);
66
+
67
+ return (
68
+ <View style={{width: scaleSize(28)}}>
69
+ <TouchableOpacity
70
+ accessibilityLabel={accessibilityLabel}
71
+ style={[styles.navigationButton, buttonStyle]}
72
+ onPress={onPress}>
73
+ <Icon
74
+ source={icon}
75
+ color={tintColor ?? theme.colors.text.default}
76
+ size={20}
77
+ />
78
+ </TouchableOpacity>
79
+ {renderBadge()}
80
+ </View>
81
+ );
82
+ };
83
+
84
+ const HeaderTitle: React.FC<any> = props => {
85
+ const {theme} = useContext(ApplicationContext);
86
+ const opacity = props.animatedValue?.interpolate({
87
+ inputRange: [0, 200],
88
+ outputRange: [0, 1],
89
+ extrapolate: 'clamp',
90
+ });
91
+
92
+ return (
93
+ <Animated.Text
94
+ {...props}
95
+ accessibilityLabel={'title_navigation_header'}
96
+ style={[
97
+ styles.title,
98
+ {fontFamily: `${theme.font}-Bold`, opacity, color: props.tintColor},
99
+ ]}
100
+ />
101
+ );
102
+ };
103
+
104
+ const HeaderLeft: React.FC<HeaderBackProps> = ({
105
+ tintColor,
106
+ preventBack,
107
+ onPressLeftHeader,
108
+ useBorder,
109
+ }) => {
110
+ const context = useContext<any>(MiniAppContext);
111
+ const {navigator} = useContext(ApplicationContext);
112
+
113
+ useEffect(() => {
114
+ const backHandler = BackHandler.addEventListener(
115
+ 'hardwareBackPress',
116
+ goBackSafe,
117
+ );
118
+
119
+ return () => backHandler.remove();
120
+ }, []);
121
+
122
+ const goBackSafe = () => {
123
+ const goBack = () => {
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);
139
+ if (canGoBack) {
140
+ navigator?.ref?.current?.goBack?.();
141
+ } else if (navigator?.maxApi) {
142
+ navigator?.maxApi?.dismiss?.(navigator?.dismissData);
143
+ } else {
144
+ const currentTime = new Date().getTime();
145
+ const requestId = `navigationBackPress#${currentTime}`;
146
+ DeviceEventEmitter.emit('dismiss', {requestId});
147
+ }
148
+ onPressLeftHeader?.();
149
+ };
150
+
151
+ if (preventBack) {
152
+ navigator?.showModal({
153
+ screen: () => (
154
+ <PopupNotify
155
+ {...preventBack}
156
+ primary={{
157
+ title: preventBack.primary.title,
158
+ onPress: () => {
159
+ preventBack?.primary.onPress();
160
+ goBack();
161
+ },
162
+ }}
163
+ />
164
+ ),
165
+ });
166
+ } else {
167
+ goBack();
168
+ }
169
+ return true;
170
+ };
171
+ return (
172
+ <View style={styles.headerLeft}>
173
+ <NavigationButton
174
+ accessibilityLabel="btn_navigation_back"
175
+ icon="ic_back"
176
+ tintColor={tintColor}
177
+ onPress={goBackSafe}
178
+ useBorder={useBorder}
179
+ />
180
+ </View>
181
+ );
182
+ };
183
+
184
+ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
185
+ image,
186
+ backgroundColor,
187
+ animatedValue,
188
+ useBorder = false,
189
+ }) => {
190
+ const {theme} = useContext(ApplicationContext);
191
+ let headerImage = theme.assets?.headerBackground;
192
+ if (image === null) {
193
+ headerImage = undefined;
194
+ }
195
+ const opacity = animatedValue?.interpolate({
196
+ inputRange: [0, 114],
197
+ outputRange: [0, 1],
198
+ extrapolate: 'clamp',
199
+ });
200
+
201
+ return (
202
+ <Animated.View
203
+ style={[
204
+ Styles.flex,
205
+ {
206
+ backgroundColor: backgroundColor ?? theme.colors.background.default,
207
+ overflow: 'hidden',
208
+ borderBottomWidth: useBorder ? 1 : 0,
209
+ borderColor: theme.colors.border.default,
210
+ opacity,
211
+ },
212
+ ]}>
213
+ <StatusBar
214
+ barStyle={headerImage || theme.dark ? 'light-content' : 'dark-content'}
215
+ translucent={true}
216
+ backgroundColor={'transparent'}
217
+ />
218
+ {!!headerImage && (
219
+ <Image
220
+ style={styles.headerBackground}
221
+ source={{uri: headerImage}}
222
+ loading={false}
223
+ />
224
+ )}
225
+ </Animated.View>
226
+ );
227
+ };
228
+
229
+ const HeaderCustom: React.FC<TitleCustomProps> = ({
230
+ title,
231
+ subTitle,
232
+ image,
233
+ imageShape = 'circle',
234
+ content,
235
+ tintColor,
236
+ dotColor,
237
+ }) => {
238
+ const avatarStyle = imageShape == 'circle' ? styles.circle : styles.square;
239
+ const header = (
240
+ <View style={Styles.row}>
241
+ <View>
242
+ <Image source={{uri: image}} style={avatarStyle} />
243
+ {!!dotColor && (
244
+ <View style={[styles.dotAvatar, {backgroundColor: dotColor}]} />
245
+ )}
246
+ </View>
247
+ <View style={[Styles.flex, {paddingHorizontal: 8}]}>
248
+ <Text
249
+ accessibilityLabel={'title_navigation_header'}
250
+ typography="header_default_bold"
251
+ color={tintColor}>
252
+ {title}
253
+ </Text>
254
+ <Text typography="description_default_regular" color={tintColor}>
255
+ {subTitle}
256
+ </Text>
257
+ </View>
258
+ </View>
259
+ );
260
+ return <View style={styles.headerTitleContainer}>{content ?? header}</View>;
261
+ };
262
+
263
+ const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
264
+ const validateType = (child: React.ReactElement) => {
265
+ const valid = child?.type === NavigationButton || child?.type == Fragment;
266
+ if (__DEV__ && !valid) {
267
+ console.warn(
268
+ 'HeaderRightAction contains element type of NavigationButton, Please check again.',
269
+ );
270
+ }
271
+ return child;
272
+ };
273
+
274
+ const renderAction = () => {
275
+ if (Array.isArray(children)) {
276
+ return children.map((child, index) => {
277
+ return (
278
+ <View key={`HeaderRightAction ${index}`} style={styles.headerButton}>
279
+ {React.cloneElement(validateType(child), {...restProps})}
280
+ </View>
281
+ );
282
+ });
283
+ }
284
+
285
+ return (
286
+ <View style={styles.headerButton}>
287
+ {React.cloneElement(validateType(children), {...restProps})}
288
+ </View>
289
+ );
290
+ };
291
+ return <View style={styles.headerRightButton}>{renderAction()}</View>;
292
+ };
293
+
294
+ const HeaderToolkitAction: React.FC<any> = ({
295
+ tintColor,
296
+ pinnedTool,
297
+ runtimeTools = [],
298
+ preventClose,
299
+ }) => {
300
+ const {theme, navigator} = useContext(ApplicationContext);
301
+ const miniContext = useContext<any>(MiniAppContext);
302
+ const [toolConfig, setToolConfig] = useState<any>();
303
+ const {onAction} = toolConfig ?? {};
304
+
305
+ let backgroundColor;
306
+ if (tintColor != Colors.black_01) {
307
+ backgroundColor = theme.colors.background.surface;
308
+ }
309
+
310
+ useEffect(() => {
311
+ if (miniContext?.toolkitConfig?.hidden === false) {
312
+ if (navigator?.toolkitConfig) {
313
+ setToolConfig(navigator?.toolkitConfig);
314
+ } else {
315
+ getToolkitConfig();
316
+ }
317
+ }
318
+ }, []);
319
+
320
+ const getToolkitConfig = () => {
321
+ navigator?.maxApi?.dispatchFunction?.(
322
+ 'getToolsConfig',
323
+ [undefined],
324
+ (config: any) => {
325
+ navigator.toolkitConfig = config;
326
+ setToolConfig(navigator?.toolkitConfig);
327
+ },
328
+ );
329
+ };
330
+
331
+ const onMore = () => {
332
+ onAction?.('onMore');
333
+ navigator?.maxApi?.dispatchFunction?.(
334
+ 'showTools',
335
+ {runtimeTools},
336
+ (res: {item: {action?: string; key: string}}) => {
337
+ const {item} = res;
338
+ navigator?.toolkitCallback?.(item);
339
+ getToolkitConfig();
340
+ },
341
+ );
342
+ };
343
+
344
+ const onClose = () => {
345
+ if (preventClose) {
346
+ navigator?.showModal({
347
+ screen: () => (
348
+ <PopupNotify
349
+ {...preventClose}
350
+ primary={{
351
+ title: preventClose.primary.title,
352
+ onPress: () => {
353
+ preventClose?.primary.onPress();
354
+ navigator?.maxApi?.dispatchFunction?.(
355
+ 'dismiss',
356
+ undefined,
357
+ undefined,
358
+ );
359
+ },
360
+ }}
361
+ />
362
+ ),
363
+ });
364
+ } else {
365
+ navigator?.maxApi?.dispatchFunction?.('dismiss', undefined, undefined);
366
+ }
367
+ };
368
+
369
+ const renderPinnedTool = () => {
370
+ const pinKey = pinnedTool?.key || toolConfig?.pinnedTool?.key;
371
+ const pinTool = toolConfig?.tools?.find((i: any) => i.key === pinKey);
372
+ const {badgeValue, badgeType} = pinnedTool ?? {};
373
+
374
+ if (pinTool)
375
+ return (
376
+ <NavigationButton
377
+ accessibilityLabel="btn_navigation_pin"
378
+ icon={pinTool.icon as string}
379
+ tintColor={tintColor}
380
+ badgeValue={badgeValue}
381
+ badgeType={badgeType}
382
+ onPress={() => {
383
+ navigator?.maxApi?.dispatchFunction?.(
384
+ 'onToolAction',
385
+ [pinTool?.key],
386
+ () => {
387
+ getToolkitConfig();
388
+ },
389
+ );
390
+ navigator?.toolkitCallback?.(pinTool.key);
391
+ }}
392
+ />
393
+ );
394
+ };
395
+ if (toolConfig) {
396
+ return (
397
+ <View style={styles.headerRightButton}>
398
+ {renderPinnedTool()}
399
+ <View
400
+ style={[
401
+ styles.toolkitContainer,
402
+ {
403
+ backgroundColor: backgroundColor ?? '#00000066',
404
+ borderWidth: backgroundColor ? 0.5 : 0,
405
+ },
406
+ ]}>
407
+ <TouchableOpacity
408
+ accessibilityLabel={'btn_navigation_more'}
409
+ onPress={onMore}>
410
+ <Icon color={tintColor} source="navigation_more_horiz" size={20} />
411
+ </TouchableOpacity>
412
+ <View style={[styles.divider, {backgroundColor: tintColor}]} />
413
+ <TouchableOpacity
414
+ accessibilityLabel={'btn_navigation_close'}
415
+ onPress={onClose}>
416
+ <Icon
417
+ color={tintColor}
418
+ source="16_navigation_close_circle"
419
+ size={20}
420
+ />
421
+ </TouchableOpacity>
422
+ </View>
423
+ </View>
424
+ );
425
+ }
426
+
427
+ return <View />;
428
+ };
429
+
430
+ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
431
+ animatedValue,
432
+ image,
433
+ children,
434
+ }) => {
435
+ const scale = animatedValue.interpolate({
436
+ inputRange: [-300, 0, 300],
437
+ outputRange: [4, 1, 1],
438
+ extrapolate: 'clamp',
439
+ });
440
+ const opacity = animatedValue.interpolate({
441
+ inputRange: [0, 150, 300],
442
+ outputRange: [1, 0.5, 0],
443
+ extrapolate: 'clamp',
444
+ });
445
+ const translateY = animatedValue.interpolate({
446
+ inputRange: [-300, 0],
447
+ outputRange: [-80, -2],
448
+ extrapolate: 'clamp',
449
+ });
450
+
451
+ return (
452
+ <Animated.View
453
+ style={{
454
+ height: 210,
455
+ opacity: opacity,
456
+ transform: [{scale}, {translateY}],
457
+ }}>
458
+ {children ?? (
459
+ <Image
460
+ source={{
461
+ uri: image,
462
+ }}
463
+ style={{width: '100%', height: '100%'}}
464
+ />
465
+ )}
466
+ </Animated.View>
467
+ );
468
+ };
469
+
470
+ const styles = StyleSheet.create({
471
+ navigationButton: {
472
+ height: 28,
473
+ width: 28,
474
+ borderRadius: 14,
475
+ justifyContent: 'center',
476
+ alignItems: 'center',
477
+ },
478
+ headerBackground: {
479
+ width: '100%',
480
+ height: undefined,
481
+ position: 'absolute',
482
+ aspectRatio: 375 / 154,
483
+ },
484
+ headerTitleContainer: {
485
+ alignItems: 'center',
486
+ justifyContent: 'center',
487
+ width: '100%',
488
+ },
489
+ circle: {width: 36, height: 36, borderRadius: 18},
490
+ square: {width: 36, height: 36, borderRadius: Radius.XS},
491
+ dotAvatar: {
492
+ position: 'absolute',
493
+ width: 12,
494
+ height: 12,
495
+ borderRadius: 6,
496
+ bottom: 0,
497
+ right: 0,
498
+ borderWidth: 1,
499
+ borderColor: Colors.black_01,
500
+ },
501
+ headerButton: {paddingHorizontal: 4},
502
+ headerRightButton: {
503
+ flexDirection: 'row',
504
+ justifyContent: 'center',
505
+ alignItems: 'center',
506
+ paddingRight: Spacing.S,
507
+ },
508
+ toolkitContainer: {
509
+ marginLeft: Spacing.S,
510
+ padding: Spacing.XS,
511
+ height: 28,
512
+ borderRadius: 14,
513
+ borderColor: '#00000066',
514
+ justifyContent: 'center',
515
+ alignItems: 'center',
516
+ flexDirection: 'row',
517
+ },
518
+ divider: {
519
+ width: 1,
520
+ height: 12,
521
+ marginHorizontal: 4,
522
+ },
523
+ headerLeft: {
524
+ marginLeft: 12,
525
+ },
526
+ title: {
527
+ fontSize: scaleSize(15),
528
+ lineHeight: scaleSize(22),
529
+ fontWeight: 'bold',
530
+ },
531
+ badge: {
532
+ position: 'absolute',
533
+ top: -Spacing.S,
534
+ right: -6,
535
+ },
536
+ badgeDot: {
537
+ position: 'absolute',
538
+ top: -Spacing.XS,
539
+ right: -Spacing.XS,
540
+ },
541
+ });
542
+
543
+ export {
544
+ NavigationButton,
545
+ HeaderTitle,
546
+ HeaderLeft,
547
+ HeaderBackground,
548
+ HeaderRightAction,
549
+ HeaderToolkitAction,
550
+ HeaderCustom,
551
+ HeaderAnimated,
552
+ };