@momo-kits/foundation 0.81.23-beta.4 → 0.81.24

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,4 +1,10 @@
1
- import React, {Fragment, useContext, useEffect, useState} from 'react';
1
+ import React, {
2
+ Fragment,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useState,
7
+ } from 'react';
2
8
  import {
3
9
  Animated,
4
10
  BackHandler,
@@ -23,6 +29,7 @@ import {
23
29
  import {scaleSize, Text} from '../Text';
24
30
  import {Icon} from '../Icon';
25
31
  import {PopupNotify} from '../Popup';
32
+ import {Badge, BadgeDot} from '../Badge';
26
33
 
27
34
  const styles = StyleSheet.create({
28
35
  navigationButton: {
@@ -58,7 +65,6 @@ const styles = StyleSheet.create({
58
65
  headerButton: {paddingHorizontal: 4},
59
66
  headerRightButton: {
60
67
  flexDirection: 'row',
61
- overflow: 'hidden',
62
68
  justifyContent: 'center',
63
69
  alignItems: 'center',
64
70
  paddingRight: Spacing.S,
@@ -86,6 +92,11 @@ const styles = StyleSheet.create({
86
92
  lineHeight: scaleSize(22),
87
93
  fontWeight: 'bold',
88
94
  },
95
+ badge: {
96
+ position: 'absolute',
97
+ top: -6,
98
+ right: -6,
99
+ },
89
100
  });
90
101
 
91
102
  const NavigationButton: React.FC<NavigationButtonProps> = ({
@@ -94,6 +105,8 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
94
105
  onPress,
95
106
  useBorder = true,
96
107
  accessibilityLabel,
108
+ badgeType,
109
+ badgeValue,
97
110
  }) => {
98
111
  const {theme} = useContext(ApplicationContext);
99
112
  let buttonStyle: ViewStyle = {backgroundColor: '#00000066'};
@@ -105,6 +118,14 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
105
118
  };
106
119
  }
107
120
 
121
+ const renderBadge = useCallback(() => {
122
+ if (!badgeValue && !badgeType) return null;
123
+ if (badgeType === 'dot') {
124
+ return <BadgeDot size="small" style={styles.badge} />;
125
+ }
126
+ return <Badge label={badgeValue} style={styles.badge} />;
127
+ }, [badgeType, badgeValue]);
128
+
108
129
  return (
109
130
  <TouchableOpacity
110
131
  accessibilityLabel={`NavigationButton/${accessibilityLabel}`}
@@ -115,6 +136,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
115
136
  color={tintColor ?? theme.colors.text.default}
116
137
  size={20}
117
138
  />
139
+ {renderBadge()}
118
140
  </TouchableOpacity>
119
141
  );
120
142
  };
@@ -159,7 +181,18 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
159
181
 
160
182
  const goBack = () => {
161
183
  const canGoBack = navigator?.ref.current?.canGoBack();
162
-
184
+ if (!canGoBack && navigator?.trackingInfo) {
185
+ const currentRoute = navigator?.ref?.current.getCurrentRoute();
186
+ navigator?.maxApi?.trackEvent('service_icon_clicked', {
187
+ feature_code: navigator.trackingInfo.code,
188
+ app_id: navigator.trackingInfo.appId,
189
+ miniapp_version: navigator.trackingInfo.buildNumber,
190
+ flow_partner_id: navigator.trackingInfo.flowPartnerId,
191
+ screen_name: currentRoute?.params?.screen?.name ?? currentRoute?.name,
192
+ trigger_id: '111154000000000000',
193
+ icon_name: 'back',
194
+ });
195
+ }
163
196
  if (canGoBack) {
164
197
  if (preventBack) {
165
198
  navigator?.showModal({
@@ -53,13 +53,13 @@ const StackScreen: React.FC<ScreenParams> = props => {
53
53
  trigger_id: '111378000000000000',
54
54
  });
55
55
 
56
- return () => {
56
+ return props.navigation.addListener('beforeRemove', () => {
57
57
  navigator?.maxApi?.trackEvent('service_icon_clicked', {
58
58
  ...tracking,
59
59
  trigger_id: '111154000000000000',
60
60
  icon_name: 'back',
61
61
  });
62
- };
62
+ });
63
63
  }
64
64
  }, []);
65
65
 
@@ -126,6 +126,8 @@ export type NavigationButtonProps = {
126
126
  useBorder?: boolean;
127
127
  onPress: () => void;
128
128
  accessibilityLabel?: string;
129
+ badgeType?: 'dot' | 'number' | 'string';
130
+ badgeValue?: number | string;
129
131
  };
130
132
 
131
133
  export interface NavigationOptions extends StackNavigationOptions {
@@ -0,0 +1,56 @@
1
+ import React, {FC, useContext} from 'react';
2
+ import {View} from 'react-native';
3
+
4
+ import {ApplicationContext} from '../Application';
5
+ import {BadgeProps} from './types';
6
+ import styles from './styles';
7
+ import {Colors} from '../Consts';
8
+ import {Text} from '../Text';
9
+
10
+ const Badge: FC<BadgeProps> = ({label = 'Label', style, backgroundColor}) => {
11
+ const {theme} = useContext(ApplicationContext);
12
+
13
+ const isValidatedColor = () => {
14
+ const colorKeys = Object.keys(Colors);
15
+ const colorValue = Object.values(Colors);
16
+ if (backgroundColor) {
17
+ const colorIndex = colorValue.indexOf(backgroundColor);
18
+ if (colorIndex !== -1) {
19
+ if (colorKeys[colorIndex]?.includes('_03')) {
20
+ return true;
21
+ }
22
+ }
23
+ }
24
+ return false;
25
+ };
26
+
27
+ const isNumber = () => {
28
+ const numberRegex = /^\d+$/;
29
+ return numberRegex.test(String(label));
30
+ };
31
+
32
+ const formatTitle = () => {
33
+ if (isNumber() && Number(label) > 99) {
34
+ return '99+';
35
+ }
36
+ return label.toString();
37
+ };
38
+
39
+ let badgeColor = isNumber()
40
+ ? theme.colors.error.primary
41
+ : theme.colors.warning.primary;
42
+
43
+ if (backgroundColor && isValidatedColor()) {
44
+ badgeColor = backgroundColor;
45
+ }
46
+
47
+ return (
48
+ <View style={[style, styles.badge, {backgroundColor: badgeColor}]}>
49
+ <Text color={Colors.black_01} typography={'action_xxs'}>
50
+ {formatTitle()}
51
+ </Text>
52
+ </View>
53
+ );
54
+ };
55
+
56
+ export default Badge;
@@ -0,0 +1,11 @@
1
+ import React, {FC} from 'react';
2
+ import {View} from 'react-native';
3
+ import {BadgeDotProps} from './types';
4
+ import styles from './styles';
5
+
6
+ const BadgeDot: FC<BadgeDotProps> = ({size = 'large', style}) => {
7
+ const dotStyle = size === 'small' ? styles.dotSmall : styles.dot;
8
+ return <View style={[style, dotStyle]} />;
9
+ };
10
+
11
+ export default BadgeDot;
@@ -0,0 +1,125 @@
1
+ import React, {FC, useContext} from 'react';
2
+ import {View} from 'react-native';
3
+ import {DownTail, UpTail} from './Shape';
4
+ import styles from './styles';
5
+ import {BadgeRibbonProps} from './types';
6
+ import {ApplicationContext} from '../Application';
7
+ import {Text} from '../Text';
8
+ import {Colors} from '../Consts';
9
+ import {Image} from '../Image';
10
+
11
+ const BadgeRibbon: FC<BadgeRibbonProps> = ({
12
+ position = 'top_right',
13
+ label = 'Label',
14
+ isRound = false,
15
+ style = {},
16
+ }) => {
17
+ const {theme} = useContext(ApplicationContext);
18
+ let ribbonRotate = '0deg';
19
+ let textRotate = '0deg';
20
+ let ribbonTail = <DownTail />;
21
+ let textMargin = 0;
22
+ let skewMargin = 0;
23
+
24
+ if (position === 'top_right' || position === 'bottom_right') {
25
+ ribbonRotate = '180deg';
26
+ textRotate = '180deg';
27
+ }
28
+
29
+ if (position === 'bottom_left' || position === 'top_right') {
30
+ textMargin = 4;
31
+ ribbonTail = <UpTail />;
32
+ skewMargin = 4;
33
+ }
34
+
35
+ const renderRoundContent = () => {
36
+ return (
37
+ <View
38
+ style={[
39
+ styles.ribbonContent,
40
+ {
41
+ backgroundColor: theme.colors.warning.primary,
42
+ marginTop: textMargin,
43
+ },
44
+ ]}>
45
+ <Text
46
+ style={[
47
+ styles.ribbonLabel,
48
+ {
49
+ transform: [
50
+ {
51
+ rotate: textRotate,
52
+ },
53
+ ],
54
+ },
55
+ ]}
56
+ color={Colors.black_01}
57
+ typography={'label_xs'}>
58
+ {label}
59
+ </Text>
60
+ </View>
61
+ );
62
+ };
63
+
64
+ const renderSkewContent = () => {
65
+ return (
66
+ <View
67
+ style={{
68
+ flexDirection: 'row',
69
+ marginTop: skewMargin,
70
+ }}>
71
+ <View
72
+ style={[
73
+ styles.ribbonSkewContent,
74
+ {
75
+ backgroundColor: theme.colors.warning.primary,
76
+ },
77
+ ]}>
78
+ <Text
79
+ style={[
80
+ styles.ribbonLabel,
81
+ {
82
+ transform: [
83
+ {
84
+ rotate: textRotate,
85
+ },
86
+ ],
87
+ },
88
+ ]}
89
+ color={Colors.black_01}
90
+ typography={'label_xs'}>
91
+ {label}
92
+ </Text>
93
+ </View>
94
+ <Image
95
+ source={{
96
+ uri: 'https://static.momocdn.net/app/img/kits/utils/Tail_4x.png',
97
+ }}
98
+ style={styles.skew}
99
+ />
100
+ </View>
101
+ );
102
+ };
103
+
104
+ const content = isRound ? renderRoundContent() : renderSkewContent();
105
+
106
+ return (
107
+ <View
108
+ style={[
109
+ style,
110
+ styles.ribbon,
111
+ {
112
+ transform: [
113
+ {
114
+ rotate: ribbonRotate,
115
+ },
116
+ ],
117
+ },
118
+ ]}>
119
+ {ribbonTail}
120
+ {content}
121
+ </View>
122
+ );
123
+ };
124
+
125
+ export default BadgeRibbon;
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import styles from './styles';
3
+ import {Image} from '../Image';
4
+
5
+ const UpTail = () => {
6
+ return (
7
+ <Image
8
+ resizeMode={'stretch'}
9
+ source={{
10
+ uri: 'https://static.momocdn.net/app/img/kits/utils/Head_down_4x.png',
11
+ }}
12
+ style={[styles.tail, {transform: [{rotate: '180deg'}]}]}
13
+ />
14
+ );
15
+ };
16
+
17
+ const DownTail = () => {
18
+ return (
19
+ <Image
20
+ resizeMode={'stretch'}
21
+ source={{
22
+ uri: 'https://static.momocdn.net/app/img/kits/utils/Head_4x.png',
23
+ }}
24
+ style={styles.tail}
25
+ />
26
+ );
27
+ };
28
+
29
+ export {UpTail, DownTail};
@@ -0,0 +1,7 @@
1
+ import Badge from './Badge';
2
+ import BadgeDot from './BadgeDot';
3
+ import BadgeRibbon from './BadgeRibbon';
4
+ import {BadgeProps, BadgeDotProps, BadgeRibbonProps} from './types';
5
+
6
+ export {Badge, BadgeRibbon, BadgeDot};
7
+ export type {BadgeProps, BadgeDotProps, BadgeRibbonProps};
@@ -0,0 +1,57 @@
1
+ import {StyleSheet} from 'react-native';
2
+ import {Colors, Radius, Spacing} from '../Consts';
3
+ import {scaleSize} from '../Text';
4
+
5
+ export default StyleSheet.create({
6
+ badge: {
7
+ paddingHorizontal: Spacing.XS,
8
+ borderRadius: Radius.M,
9
+ justifyContent: 'center',
10
+ alignItems: 'center',
11
+ height: scaleSize(16),
12
+ flexDirection: 'row',
13
+ borderWidth: 1,
14
+ borderColor: Colors.black_01,
15
+ alignSelf: 'baseline',
16
+ },
17
+ dot: {
18
+ width: 16,
19
+ height: 16,
20
+ borderWidth: 2,
21
+ borderColor: Colors.black_01,
22
+ backgroundColor: Colors.red_03,
23
+ borderRadius: 8,
24
+ },
25
+ dotSmall: {
26
+ width: 10,
27
+ height: 10,
28
+ borderWidth: 1,
29
+ borderColor: Colors.black_01,
30
+ backgroundColor: Colors.red_03,
31
+ borderRadius: 5,
32
+ },
33
+ ribbon: {
34
+ alignSelf: 'baseline',
35
+ flexDirection: 'row',
36
+ height: 20,
37
+ overflow: 'hidden',
38
+ },
39
+ ribbonContent: {
40
+ height: 16,
41
+ justifyContent: 'center',
42
+ alignItems: 'center',
43
+ borderBottomRightRadius: 12,
44
+ borderTopRightRadius: 12,
45
+ paddingRight: 6,
46
+ },
47
+ ribbonSkewContent: {
48
+ height: 16,
49
+ justifyContent: 'center',
50
+ alignItems: 'center',
51
+ },
52
+ ribbonLabel: {
53
+ color: Colors.black_01,
54
+ },
55
+ skew: {width: 8, height: 16},
56
+ tail: {width: 5, height: 20},
57
+ });
package/Badge/types.ts ADDED
@@ -0,0 +1,65 @@
1
+ import {ViewStyle} from 'react-native';
2
+
3
+ /**
4
+ * Defines the types of badges that can be used. This helps in rendering the appropriate badge style.
5
+ */
6
+ export type BadgeType = 'badge' | 'badge_number' | 'dot' | 'dot_small';
7
+
8
+ /**
9
+ * Props for a general Badge component. It can display a wide range of badges including simple labels, numbers, or dots.
10
+ */
11
+ export type BadgeProps = {
12
+ /**
13
+ * Optional. The text to display on the badge. This could be a number or string, depending on the badge's purpose.
14
+ */
15
+ label?: string | number;
16
+
17
+ /**
18
+ * Optional. Custom styles to apply to the badge component. Can be a single style or an array of styles.
19
+ */
20
+ style?: ViewStyle | ViewStyle[];
21
+
22
+ backgroundColor?: string;
23
+ };
24
+
25
+ /**
26
+ * Props for a BadgeDot component. It specifically represents a dot-style badge, typically used for notification indicators.
27
+ */
28
+ export type BadgeDotProps = {
29
+ /**
30
+ * Optional. The size of the dot, either 'small' or 'large'. This allows for better visual alignment with different UI elements.
31
+ */
32
+ size?: 'small' | 'large';
33
+
34
+ /**
35
+ * Optional. Custom styles to apply to the badge dot component. Can be a single style or an array of styles.
36
+ */
37
+ style?: ViewStyle | ViewStyle[];
38
+ };
39
+
40
+ /**
41
+ * Props for a BadgeRibbon component. It represents a more specialized badge in the shape of a ribbon, allowing for placement around containers.
42
+ */
43
+ export type BadgeRibbonProps = {
44
+ /**
45
+ * The text to display on the ribbon-style badge. Unlike other badges, ribbons typically carry more descriptive text.
46
+ */
47
+ label: string;
48
+
49
+ /**
50
+ * Optional. Determines the placement of the ribbon on its parent container. The position can be any corner of the container.
51
+ * Defaults to 'top_right' if not specified.
52
+ */
53
+ position?: 'top_right' | 'top_left' | 'bottom_right' | 'bottom_left';
54
+
55
+ /**
56
+ * Optional. If true, the edges of the ribbon badge will be rounded. This is purely a stylistic choice.
57
+ * Defaults to `false` if not provided, resulting in a flat-edged ribbon.
58
+ */
59
+ isRound?: boolean;
60
+
61
+ /**
62
+ * Optional. Custom styles to apply to the ribbon badge component. Can be a single style or an array of styles.
63
+ */
64
+ style?: ViewStyle | ViewStyle[];
65
+ };
package/Image/styles.ts CHANGED
@@ -1,8 +1,5 @@
1
1
  import {StyleSheet} from 'react-native';
2
2
 
3
3
  export default StyleSheet.create({
4
- container: {
5
- width: '100%',
6
- height: '100%',
7
- },
4
+ container: {},
8
5
  });
package/Layout/Card.tsx CHANGED
@@ -148,6 +148,7 @@ const Card: React.FC<CardProps> = ({
148
148
  {
149
149
  width: widthSection,
150
150
  marginHorizontal: margin,
151
+ borderRadius: Spacing.M,
151
152
  },
152
153
  ]}
153
154
  />
package/Layout/styles.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {StyleSheet} from 'react-native';
2
- import {Radius, Spacing} from '../Consts';
2
+ import {Radius} from '../Consts';
3
3
 
4
4
  export default StyleSheet.create({
5
5
  extendedHeader: {
@@ -63,7 +63,6 @@ export default StyleSheet.create({
63
63
  bottom: 0,
64
64
  left: 0,
65
65
  right: 0,
66
- borderRadius: Spacing.M,
67
66
  },
68
67
  protectedStyle: {
69
68
  marginTop: undefined,
@@ -190,11 +190,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
190
190
  styles.container,
191
191
  {backgroundColor: theme.colors.background.surface},
192
192
  ]}>
193
- {!!image && (
194
- <View style={styles.image}>
195
- <Image source={{uri: image}} />
196
- </View>
197
- )}
193
+ {!!image && <Image source={{uri: image}} style={styles.image} />}
198
194
  <View style={styles.content}>
199
195
  <Text typography={'title_xs'} numberOfLines={1}>
200
196
  {title}
@@ -82,13 +82,12 @@ const PopupPromotion: React.FC<PopupPromotionProps> = ({
82
82
 
83
83
  return (
84
84
  <View accessibilityLabel={'PopupPromotion'}>
85
- <View style={styles.container}>
86
- <Image
87
- source={{
88
- uri: image,
89
- }}
90
- />
91
- </View>
85
+ <Image
86
+ source={{
87
+ uri: image,
88
+ }}
89
+ style={styles.container}
90
+ />
92
91
  {buildCloseIcon()}
93
92
  </View>
94
93
  );
@@ -97,6 +96,7 @@ const PopupPromotion: React.FC<PopupPromotionProps> = ({
97
96
  const styles = StyleSheet.create({
98
97
  container: {
99
98
  aspectRatio: 0.72,
99
+ width: '100%',
100
100
  },
101
101
  iconCloseContainer: {
102
102
  width: '100%',
package/index.ts CHANGED
@@ -47,3 +47,5 @@ export * from './Loader';
47
47
  export * from './Loader/types';
48
48
  export * from './Title';
49
49
  export * from './Title/types';
50
+ export * from './Badge';
51
+ export * from './Badge/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.81.23-beta.4",
3
+ "version": "0.81.24",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {