@momo-kits/foundation 0.81.1-optimize.22 → 0.81.1-rc.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.
@@ -1,8 +1,9 @@
1
- import {StackNavigationOptions} from '@react-navigation/stack';
2
1
  import {EventArg} from '@react-navigation/core';
2
+ import {StackNavigationOptions} from '@react-navigation/stack';
3
3
  import React from 'react';
4
4
  import {Animated, ViewProps} from 'react-native';
5
5
  import {PopupNotifyProps} from '../Popup/types';
6
+ import Localize from './Localize';
6
7
  import Navigation from './Navigation';
7
8
  import Navigator from './Navigator';
8
9
 
@@ -85,8 +86,8 @@ export type NavigationContainerProps = {
85
86
  options?: NavigationOptions;
86
87
  theme: Theme;
87
88
  maxApi: any;
88
- localization?: LocalizationObject;
89
89
  initialParams?: any;
90
+ localize: Localize;
90
91
  };
91
92
 
92
93
  export type NavigationScreenProps = {
@@ -115,6 +116,7 @@ export type BottomSheetParams = {
115
116
  title: string;
116
117
  surface?: boolean;
117
118
  };
119
+ useNativeModal?: boolean;
118
120
  surface?: boolean;
119
121
  onDismiss?: (type?: string) => void;
120
122
  barrierDismissible?: boolean;
@@ -128,7 +130,7 @@ export type NavigationButtonProps = {
128
130
  tintColor?: string;
129
131
  useBorder?: boolean;
130
132
  onPress: () => void;
131
- badgeType?: 'dot' | 'number';
133
+ badgeType?: 'dot' | 'number' | 'dot-animation';
132
134
  badgeValue?: number;
133
135
  accessibilityLabel?: string;
134
136
  };
@@ -136,7 +138,7 @@ export type NavigationButtonProps = {
136
138
  export type PinnedToolType = {
137
139
  key: string;
138
140
  badgeValue?: number;
139
- badgeType?: 'number' | 'dot';
141
+ badgeType?: 'number' | 'dot' | 'dot-animation';
140
142
  };
141
143
 
142
144
  export type RuntimeToolType = {
@@ -145,19 +147,29 @@ export type RuntimeToolType = {
145
147
  key: string;
146
148
  };
147
149
 
148
- export interface NavigationOptions extends StackNavigationOptions {
150
+ export interface NavigationOptions
151
+ extends Omit<StackNavigationOptions, 'headerRight'> {
149
152
  preventBack?: PopupNotifyProps;
150
153
  onPressLeftHeader?: () => void;
151
154
  surface?: boolean;
152
155
  hiddenBack?: boolean;
153
156
  customTitle?: TitleCustomProps;
154
- toolkitParams?: {
155
- pinnedTool?: PinnedToolType;
156
- runtimeTools?: RuntimeToolType[];
157
- preventClose?: PopupNotifyProps;
158
- };
157
+ headerRight?: HeaderRightToolkit | HeaderRightActions | any;
159
158
  }
160
159
 
160
+ export type HeaderRightToolkit = {
161
+ type?: 'toolkit';
162
+ pinnedTool?: PinnedToolType;
163
+ runtimeTools?: RuntimeToolType[];
164
+ preventClose?: PopupNotifyProps;
165
+ useMore?: boolean;
166
+ };
167
+
168
+ export type HeaderRightActions = {
169
+ type?: 'icon';
170
+ buttons: NavigationButtonProps[];
171
+ };
172
+
161
173
  export interface HeaderBackProps extends NavigationButtonProps {
162
174
  preventBack?: PopupNotifyProps;
163
175
  onPressLeftHeader?: () => void;
@@ -7,11 +7,13 @@ import {
7
7
  HeaderBackground,
8
8
  HeaderCustom,
9
9
  HeaderLeft,
10
+ HeaderRight,
10
11
  HeaderTitle,
11
- HeaderToolkitAction,
12
12
  } from './Components';
13
- import {NavigationOptions, Theme} from './types';
13
+ import {NavigationOptions} from './types';
14
14
  import {Colors} from '../Consts';
15
+ import Navigation from './Navigation';
16
+ import {LayoutChangeEvent} from 'react-native';
15
17
 
16
18
  /**
17
19
  * default options for stack screen
@@ -22,7 +24,7 @@ const getStackOptions = (): StackNavigationOptions => {
22
24
  headerTitle: HeaderTitle,
23
25
  headerBackground: (props: any) => <HeaderBackground {...props} />,
24
26
  headerLeft: (props: any) => <HeaderLeft {...props} />,
25
- headerRight: (props: any) => <HeaderToolkitAction {...props} />,
27
+ headerRight: (props: any) => <HeaderRight {...props} />,
26
28
  headerTintColor: Colors.black_01,
27
29
  };
28
30
  };
@@ -72,33 +74,42 @@ const getModalOptions = (): StackNavigationOptions => {
72
74
  /**
73
75
  * build react-navigation options
74
76
  */
75
- const getOptions = (params: NavigationOptions, theme: Theme) => {
77
+ const getOptions = (params: NavigationOptions, navigation?: Navigation) => {
76
78
  let options = {};
77
79
 
80
+ /**
81
+ * left header factory
82
+ */
78
83
  if (
79
84
  typeof params.onPressLeftHeader === 'function' ||
80
85
  params.preventBack !== undefined ||
81
86
  typeof params.hiddenBack === 'boolean'
82
87
  ) {
88
+ let headerLeft: any = (props: any) => (
89
+ <HeaderLeft
90
+ {...props}
91
+ preventBack={params.preventBack}
92
+ onPressLeftHeader={params.onPressLeftHeader}
93
+ />
94
+ );
95
+ if (params.hiddenBack) {
96
+ headerLeft = null;
97
+ }
83
98
  options = {
84
99
  ...options,
85
- headerLeft: params.hiddenBack
86
- ? null
87
- : (props: any) => (
88
- <HeaderLeft
89
- {...props}
90
- preventBack={params.preventBack}
91
- onPressLeftHeader={params.onPressLeftHeader}
92
- />
93
- ),
100
+ headerLeft,
94
101
  };
95
102
  }
96
103
 
104
+ /**
105
+ * surface header style
106
+ */
97
107
  if (typeof params.surface === 'boolean') {
108
+ console.warn('surface options is deprecated, headerType instead in screen');
98
109
  if (params.surface) {
99
110
  options = {
100
111
  ...options,
101
- headerTintColor: theme.colors.text.default,
112
+ headerTintColor: Colors.black_17,
102
113
  headerBackground: () => (
103
114
  <HeaderBackground image={null} useSurface={true} />
104
115
  ),
@@ -112,7 +123,13 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
112
123
  }
113
124
  }
114
125
 
126
+ /**
127
+ * custom title
128
+ */
115
129
  if (params.customTitle) {
130
+ console.warn(
131
+ 'customTitle options is deprecated, please used headerTitle instead'
132
+ );
116
133
  options = {
117
134
  ...options,
118
135
  headerTitleAlign: 'left',
@@ -122,12 +139,37 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
122
139
  };
123
140
  }
124
141
 
125
- if (params.toolkitParams) {
142
+ /**
143
+ * header right
144
+ */
145
+ if (params.headerRight) {
146
+ let headerRight: any = (props: any) => {
147
+ return (
148
+ <HeaderRight
149
+ {...props}
150
+ {...params.headerRight}
151
+ onLayout={(e: LayoutChangeEvent) => {
152
+ navigation?.onHeaderRightChange?.(e.nativeEvent.layout.width);
153
+ }}
154
+ />
155
+ );
156
+ };
157
+ if (typeof params.headerRight === 'function') {
158
+ headerRight = (props: any) => {
159
+ return (
160
+ <HeaderRight
161
+ {...props}
162
+ onLayout={(e: LayoutChangeEvent) => {
163
+ navigation?.onHeaderRightChange?.(e.nativeEvent.layout.width);
164
+ }}>
165
+ {params.headerRight(props)}
166
+ </HeaderRight>
167
+ );
168
+ };
169
+ }
126
170
  options = {
127
171
  ...options,
128
- headerRight: (props: any) => {
129
- return <HeaderToolkitAction {...params.toolkitParams} {...props} />;
130
- },
172
+ headerRight,
131
173
  };
132
174
  }
133
175
 
@@ -0,0 +1,95 @@
1
+ import React, {useEffect, useRef} from 'react';
2
+ import {Animated, View} from 'react-native';
3
+ import {BadgeDotProps} from './types';
4
+ import styles from './styles';
5
+
6
+ const DURATION = 500;
7
+
8
+ const BadgeDotAnimation = ({size, style}: BadgeDotProps) => {
9
+ // Refs for animated values
10
+ const scaleAnim = useRef(new Animated.Value(1)).current;
11
+ const waveScaleAnim = useRef(new Animated.Value(1)).current;
12
+ const waveOpacityAnim = useRef(new Animated.Value(0)).current;
13
+
14
+ const dotStyle =
15
+ size === 'small' ? styles.dotAnimationSmall : styles.dotAnimation;
16
+ const waveStyle = size === 'small' ? styles.waveSmall : styles.wave;
17
+
18
+ useEffect(() => {
19
+ // Infinite loop animation for the scale and wave effect
20
+ const animation = Animated.loop(
21
+ Animated.parallel([
22
+ // Dot pulse animation
23
+ Animated.sequence([
24
+ Animated.spring(scaleAnim, {
25
+ toValue: 1, // Scale up slightly
26
+ friction: 5, // Controls the "bounciness" of the spring
27
+ tension: 30, // Controls the "stiffness" of the spring
28
+ useNativeDriver: true,
29
+ }),
30
+ Animated.spring(scaleAnim, {
31
+ toValue: 1.1,
32
+ friction: 5,
33
+ tension: 30,
34
+ useNativeDriver: true,
35
+ }),
36
+ ]), // Wave animation
37
+ Animated.sequence([
38
+ Animated.timing(waveScaleAnim, {
39
+ toValue: 2.5,
40
+ duration: DURATION * 3,
41
+ useNativeDriver: true,
42
+ }),
43
+ Animated.timing(waveScaleAnim, {
44
+ toValue: 1, // Reset wave size
45
+ duration: 0,
46
+ useNativeDriver: true,
47
+ }),
48
+ ]), // Wave opacity animation
49
+ Animated.sequence([
50
+ Animated.timing(waveOpacityAnim, {
51
+ toValue: 0.3, // Wave becomes visible
52
+ duration: DURATION * 2,
53
+ useNativeDriver: true,
54
+ }),
55
+ Animated.timing(waveOpacityAnim, {
56
+ toValue: 0, // Wave fades out
57
+ duration: DURATION,
58
+ useNativeDriver: true,
59
+ }),
60
+ ]),
61
+ ])
62
+ );
63
+ animation.start();
64
+
65
+ return () => {
66
+ animation.stop();
67
+ };
68
+ }, []);
69
+
70
+ return (
71
+ <View style={[styles.dotAnimationContainer, style]}>
72
+ {/* Wave Animation */}
73
+ <Animated.View
74
+ style={[
75
+ waveStyle,
76
+ {
77
+ transform: [{scale: waveScaleAnim}],
78
+ opacity: waveOpacityAnim,
79
+ },
80
+ ]}
81
+ />
82
+ {/* Dot Animation */}
83
+ <Animated.View
84
+ style={[
85
+ dotStyle,
86
+ {
87
+ transform: [{scale: scaleAnim}],
88
+ },
89
+ ]}
90
+ />
91
+ </View>
92
+ );
93
+ };
94
+
95
+ export default BadgeDotAnimation;
package/Badge/index.tsx CHANGED
@@ -1,7 +1,8 @@
1
1
  import Badge from './Badge';
2
2
  import BadgeDot from './BadgeDot';
3
3
  import BadgeRibbon from './BadgeRibbon';
4
+ import BadgeDotAnimation from './BadgeDotAnimation';
4
5
  import {BadgeProps, BadgeDotProps, BadgeRibbonProps} from './types';
5
6
 
6
- export {Badge, BadgeRibbon, BadgeDot};
7
+ export {Badge, BadgeRibbon, BadgeDot, BadgeDotAnimation};
7
8
  export type {BadgeProps, BadgeDotProps, BadgeRibbonProps};
package/Badge/styles.ts CHANGED
@@ -2,6 +2,10 @@ import {StyleSheet} from 'react-native';
2
2
  import {Colors, Radius, Spacing} from '../Consts';
3
3
  import {scaleSize} from '../Text';
4
4
 
5
+ const DOT_SIZE = 16;
6
+
7
+ const DOT_SMALL_SIZE = 10;
8
+
5
9
  export default StyleSheet.create({
6
10
  badge: {
7
11
  paddingHorizontal: Spacing.XS,
@@ -15,21 +19,61 @@ export default StyleSheet.create({
15
19
  borderColor: Colors.black_01,
16
20
  alignSelf: 'baseline',
17
21
  },
22
+ dotAnimationContainer: {
23
+ position: 'relative',
24
+ alignItems: 'center',
25
+ justifyContent: 'center',
26
+ },
18
27
  dot: {
19
- width: 16,
20
- height: 16,
28
+ width: DOT_SIZE,
29
+ height: DOT_SIZE,
21
30
  borderWidth: 2,
22
31
  borderColor: Colors.black_01,
23
32
  backgroundColor: Colors.red_03,
24
- borderRadius: 8,
33
+ borderRadius: DOT_SIZE / 2,
25
34
  },
26
35
  dotSmall: {
27
- width: 10,
28
- height: 10,
36
+ width: DOT_SMALL_SIZE,
37
+ height: DOT_SMALL_SIZE,
29
38
  borderWidth: 1,
30
39
  borderColor: Colors.black_01,
31
40
  backgroundColor: Colors.red_03,
32
- borderRadius: 5,
41
+ borderRadius: DOT_SMALL_SIZE / 2,
42
+ },
43
+ dotAnimation: {
44
+ width: DOT_SIZE,
45
+ height: DOT_SIZE,
46
+ borderColor: Colors.black_01,
47
+ backgroundColor: Colors.red_03,
48
+ borderRadius: DOT_SIZE / 2,
49
+ borderWidth: 0,
50
+ },
51
+ dotAnimationSmall: {
52
+ width: DOT_SMALL_SIZE,
53
+ height: DOT_SMALL_SIZE,
54
+ borderColor: Colors.black_01,
55
+ backgroundColor: Colors.red_03,
56
+ borderRadius: DOT_SMALL_SIZE / 2,
57
+ borderWidth: 0,
58
+ },
59
+ wave: {
60
+ width: DOT_SIZE,
61
+ height: DOT_SIZE,
62
+ borderColor: Colors.black_01,
63
+ backgroundColor: Colors.red_03,
64
+ borderRadius: DOT_SIZE / 2,
65
+ opacity: 0,
66
+ position: 'absolute',
67
+ borderWidth: 0,
68
+ },
69
+ waveSmall: {
70
+ width: DOT_SMALL_SIZE,
71
+ height: DOT_SMALL_SIZE,
72
+ borderColor: Colors.black_01,
73
+ backgroundColor: Colors.red_03,
74
+ borderRadius: DOT_SMALL_SIZE / 2,
75
+ borderWidth: 0,
76
+ position: 'absolute',
33
77
  },
34
78
  ribbon: {
35
79
  alignSelf: 'baseline',
package/Button/index.tsx CHANGED
@@ -297,7 +297,6 @@ const Button: FC<ButtonProps> = ({
297
297
  };
298
298
 
299
299
  const activeOpacity = type === 'disabled' ? 0.75 : 0.5;
300
-
301
300
  return (
302
301
  <ComponentContext.Provider
303
302
  value={{
package/Input/Input.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import React, {
2
2
  forwardRef,
3
+ ReactNode,
3
4
  useContext,
4
5
  useImperativeHandle,
5
6
  useRef,
@@ -16,11 +17,15 @@ import {
16
17
  import {ApplicationContext, ComponentContext} from '../Application';
17
18
  import {Icon} from '../Icon';
18
19
  import {Loader} from '../Loader';
20
+ import {Text} from '../Text';
19
21
  import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
20
22
  import {InputProps} from './index';
21
23
  import styles from './styles';
22
24
  import {checkTyping} from './utils';
23
25
 
26
+ /**
27
+ * Input default component
28
+ */
24
29
  const Input = forwardRef(
25
30
  (
26
31
  {
@@ -34,16 +39,19 @@ const Input = forwardRef(
34
39
  onFocus,
35
40
  errorMessage,
36
41
  icon,
42
+ iconColor,
43
+ onPressIcon,
44
+ trailing,
45
+ trailingColor,
46
+ onPressTrailing,
37
47
  disabled = false,
38
48
  floatingIconColor,
39
- iconColor,
40
49
  required = false,
41
50
  errorSpacing,
42
51
  loading = false,
43
52
  leadingIcon,
44
53
  leadingIconColor,
45
54
  fontWeight = 'Regular',
46
- onPressIcon,
47
55
  secureTextEntry,
48
56
  keyboardType,
49
57
  style,
@@ -51,9 +59,10 @@ const Input = forwardRef(
51
59
  hintText,
52
60
  accessibilityLabel,
53
61
  editable = true,
62
+ onPressFloatingIcon,
54
63
  ...props
55
64
  }: InputProps,
56
- ref,
65
+ ref
57
66
  ) => {
58
67
  const {theme} = useContext(ApplicationContext);
59
68
  const [focused, setFocused] = useState(false);
@@ -90,7 +99,11 @@ const Input = forwardRef(
90
99
  };
91
100
  });
92
101
 
93
- const renderIcon = (color: string | undefined) => {
102
+ /**
103
+ * Render trailing icon or text
104
+ * @param color
105
+ */
106
+ const renderTrailing = (color?: string) => {
94
107
  if (loading) {
95
108
  return <Loader type={'spinner'} color={color} style={styles.icon} />;
96
109
  }
@@ -109,24 +122,46 @@ const Input = forwardRef(
109
122
  </TouchableOpacity>
110
123
  );
111
124
  }
112
- if (icon) {
113
- return (
114
- <TouchableOpacity
115
- onPress={onPressIcon}
116
- disabled={!onPressIcon}
117
- style={styles.icon}>
118
- <Icon color={color} source={icon} size={24} />
119
- </TouchableOpacity>
125
+ if (icon || trailing) {
126
+ const renderIconTouchable = (icon: ReactNode) => {
127
+ return (
128
+ <TouchableOpacity
129
+ onPress={onPressTrailing ?? onPressIcon}
130
+ style={styles.icon}>
131
+ {icon}
132
+ </TouchableOpacity>
133
+ );
134
+ };
135
+ const trailingValue = icon || trailing;
136
+ if (trailingValue?.includes('_') || trailingValue?.includes('http')) {
137
+ return renderIconTouchable(
138
+ <Icon
139
+ color={color}
140
+ source={(icon || trailing) as string}
141
+ size={24}
142
+ />
143
+ );
144
+ }
145
+ return renderIconTouchable(
146
+ <Text
147
+ typography="action_xs_bold"
148
+ color={color ?? theme.colors.primary}
149
+ numberOfLines={1}>
150
+ {trailingValue!.substring(0, 15)}
151
+ </Text>
120
152
  );
121
153
  }
122
154
  };
123
155
 
156
+ /**
157
+ * Render input view
158
+ */
124
159
  const renderInputView = () => {
125
160
  const disabledColor = theme.colors.text.disable;
126
161
  const secure = secureTextInput && secureTextEntry;
127
162
  let textColor = theme.colors.text.default;
128
163
  let placeholderColor = theme.colors.text.hint;
129
- let iconTintColor = iconColor;
164
+ let iconTintColor = trailingColor ?? iconColor;
130
165
 
131
166
  if (disabled) {
132
167
  textColor = disabledColor;
@@ -155,6 +190,7 @@ const Input = forwardRef(
155
190
  disabled={disabled}
156
191
  required={required}
157
192
  floatingIcon={floatingIcon}
193
+ onPress={onPressFloatingIcon}
158
194
  />
159
195
  <View style={styles.inputView}>
160
196
  {!!leadingIcon && (
@@ -204,21 +240,19 @@ const Input = forwardRef(
204
240
  />
205
241
  </TouchableOpacity>
206
242
  )}
207
- {renderIcon(iconTintColor)}
243
+ {renderTrailing(iconTintColor)}
208
244
  </View>
209
245
  </View>
210
246
  );
211
247
  };
212
248
 
213
249
  let inputState = 'active';
214
-
215
250
  if (value && value?.length > 0) {
216
251
  inputState = 'filled';
217
252
  }
218
253
  if (errorMessage && errorMessage?.length > 0) {
219
254
  inputState = 'error';
220
255
  }
221
-
222
256
  if (disabled) {
223
257
  inputState = 'disabled';
224
258
  }
@@ -242,7 +276,7 @@ const Input = forwardRef(
242
276
  </View>
243
277
  </ComponentContext.Provider>
244
278
  );
245
- },
279
+ }
246
280
  );
247
281
 
248
282
  export default Input;