@momo-kits/foundation 0.81.1-optimize.21 → 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.
Files changed (45) hide show
  1. package/Application/BottomSheet.tsx +27 -23
  2. package/Application/BottomTab/BottomTabBar.tsx +2 -2
  3. package/Application/BottomTab/index.tsx +2 -3
  4. package/Application/Components.tsx +288 -42
  5. package/Application/Localize.ts +44 -0
  6. package/Application/ModalScreen.tsx +2 -2
  7. package/Application/Navigation.ts +4 -18
  8. package/Application/NavigationContainer.tsx +10 -21
  9. package/Application/Navigator.ts +14 -16
  10. package/Application/StackScreen.tsx +211 -113
  11. package/Application/index.ts +5 -3
  12. package/Application/types.ts +30 -11
  13. package/Application/utils.tsx +60 -18
  14. package/Badge/BadgeDotAnimation.tsx +95 -0
  15. package/Badge/index.tsx +2 -1
  16. package/Badge/styles.ts +50 -6
  17. package/Button/index.tsx +0 -1
  18. package/Icon/types.ts +0 -1
  19. package/Input/Input.tsx +51 -17
  20. package/Input/InputDropDown.tsx +44 -47
  21. package/Input/InputMoney.tsx +56 -6
  22. package/Input/InputOTP.tsx +10 -6
  23. package/Input/InputSearch.tsx +259 -146
  24. package/Input/TextTyping.tsx +113 -0
  25. package/Input/common.tsx +29 -10
  26. package/Input/index.tsx +75 -74
  27. package/Input/styles.ts +43 -7
  28. package/Layout/FloatingButton.tsx +1 -1
  29. package/Layout/GridSystem.tsx +1 -1
  30. package/Layout/ItemList.tsx +3 -2
  31. package/Layout/Screen.tsx +174 -91
  32. package/Layout/styles.ts +0 -6
  33. package/Layout/types.ts +1 -1
  34. package/Pagination/PaginationScroll.tsx +2 -2
  35. package/Pagination/styles.ts +1 -1
  36. package/Popup/PopupNotify.tsx +3 -3
  37. package/Tag/index.tsx +9 -2
  38. package/Tag/styles.ts +3 -2
  39. package/Tag/types.ts +12 -0
  40. package/Title/index.tsx +6 -9
  41. package/Title/styles.ts +4 -4
  42. package/code-scanner.js +4 -4
  43. package/package.json +6 -5
  44. package/publish.sh +12 -22
  45. package/verify.js +36 -42
@@ -1,6 +1,9 @@
1
1
  import React, {
2
+ FC,
2
3
  forwardRef,
4
+ ForwardRefRenderFunction,
3
5
  useContext,
6
+ useEffect,
4
7
  useImperativeHandle,
5
8
  useRef,
6
9
  useState,
@@ -15,177 +18,287 @@ import {
15
18
  import {ApplicationContext, ComponentContext} from '../Application';
16
19
  import {Icon} from '../Icon';
17
20
  import {Text} from '../Text';
18
- import {InputSearchProps} from './index';
21
+ import {InputRef, InputSearchProps} from './index';
19
22
  import styles from './styles';
20
23
  import {checkTyping} from './utils';
24
+ import {Styles} from '../Consts';
21
25
 
22
- const InputSearch = forwardRef(
23
- (
24
- {
25
- placeholder,
26
- onFocus,
27
- onBlur,
28
- iconColor,
29
- value,
30
- onChangeText,
31
- icon,
32
- buttonText = 'Hủy',
33
- showButtonText = true,
34
- style,
35
- defaultValue,
36
- onPressButtonText,
37
- params,
38
- hasColorBG = false,
39
- accessibilityLabel,
40
- ...props
41
- }: InputSearchProps,
42
- ref,
43
- ) => {
44
- const {theme} = useContext(ApplicationContext);
45
- const [focused, setFocused] = useState(false);
46
- const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
47
- const inputRef = useRef<TextInput | null>(null);
48
-
49
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
50
- setFocused(true);
51
- onFocus?.(e);
26
+ const TextTyping: FC<any> = ({
27
+ data = [],
28
+ timeDelayEnd = 2000,
29
+ timeDelayNextChar = 20,
30
+ }) => {
31
+ const textRef = useRef<TextInput>(null);
32
+
33
+ const currentIndex = useRef<number>(0);
34
+ const currentCharIndex = useRef<number>(0);
35
+ const currentText = useRef('');
36
+
37
+ useEffect(() => {
38
+ setTimeout(() => {
39
+ playAnimation();
40
+ }, 1000);
41
+ }, []);
42
+
43
+ const playAnimation = () => {
44
+ const handleResetList = () => {
45
+ currentIndex.current = 0;
46
+ handleAnimation();
52
47
  };
53
48
 
54
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
55
- setFocused(false);
56
- onBlur?.(e);
49
+ const handleAnimation = () => {
50
+ const listChar = data?.[currentIndex.current]?.split('') || [];
51
+ listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
57
52
  };
58
53
 
59
- const onClearText = () => {
60
- inputRef?.current?.clear();
61
- _onChangeText('');
54
+ currentIndex.current >= data?.length
55
+ ? handleResetList()
56
+ : handleAnimation();
57
+ };
58
+
59
+ const showChar = (char?: string) => {
60
+ const handleResetString = () => {
61
+ setTimeout(() => {
62
+ reset();
63
+ }, timeDelayEnd);
62
64
  };
63
65
 
64
- const _onChangeText = (text: string) => {
65
- checkTyping(text, haveValue, setHaveValue);
66
- onChangeText?.(text);
66
+ const handleShowChar = () => {
67
+ setTimeout(() => {
68
+ textRef.current?.setNativeProps?.({
69
+ text: currentText.current + char,
70
+ });
71
+ currentText.current += char;
72
+ currentCharIndex.current++;
73
+ playAnimation();
74
+ }, timeDelayNextChar);
67
75
  };
68
76
 
69
- useImperativeHandle(ref, () => {
70
- return {
71
- clear: onClearText,
72
- focus: () => inputRef.current?.focus(),
73
- blur: () => inputRef.current?.blur(),
74
- setText: (text: string) => _onChangeText(text),
75
- };
76
- });
77
+ currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
78
+ ? handleResetString()
79
+ : handleShowChar();
80
+ };
81
+
82
+ const reset = () => {
83
+ const handleNextString = () => {
84
+ currentIndex.current++;
85
+ playAnimation();
86
+ };
87
+
88
+ const handleReset = () => {
89
+ setTimeout(() => {
90
+ textRef.current?.setNativeProps?.({
91
+ text: currentText.current.slice(0, -1),
92
+ });
93
+ currentText.current = currentText.current.slice(0, -1);
94
+ currentCharIndex.current--;
95
+ reset();
96
+ }, timeDelayNextChar);
97
+ };
98
+
99
+ currentCharIndex.current <= 0 ? handleNextString() : handleReset();
100
+ };
101
+
102
+ return (
103
+ <TextInput
104
+ style={styles.inputStyle}
105
+ ref={textRef}
106
+ editable={false}
107
+ autoCorrect={false}
108
+ pointerEvents={'none'}
109
+ numberOfLines={1}
110
+ />
111
+ );
112
+ };
113
+
114
+ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
115
+ {
116
+ placeholder,
117
+ onFocus,
118
+ onBlur,
119
+ value,
120
+ icon,
121
+ iconColor,
122
+ onPressIcon,
123
+ trailing,
124
+ trailingColor,
125
+ onPressTrailing,
126
+ onChangeText,
127
+ buttonText = 'Hủy',
128
+ showButtonText = true,
129
+ style,
130
+ defaultValue,
131
+ onPressButtonText,
132
+ params,
133
+ hasColorBG = false,
134
+ accessibilityLabel,
135
+ onPress,
136
+ typingData,
137
+ ...props
138
+ },
139
+ ref
140
+ ) => {
141
+ const {theme} = useContext(ApplicationContext);
142
+ const [focused, setFocused] = useState(false);
143
+ const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
144
+ const inputRef = useRef<TextInput | null>(null);
145
+
146
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
147
+ setFocused(true);
148
+ onFocus?.(e);
149
+ };
150
+
151
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
152
+ setFocused(false);
153
+ onBlur?.(e);
154
+ };
77
155
 
78
- const renderInputView = () => {
156
+ const onClearText = () => {
157
+ inputRef?.current?.clear();
158
+ _onChangeText('');
159
+ };
160
+
161
+ const _onChangeText = (text: string) => {
162
+ checkTyping(text, haveValue, setHaveValue);
163
+ onChangeText?.(text);
164
+ };
165
+
166
+ useImperativeHandle(ref, () => {
167
+ return {
168
+ clear: onClearText,
169
+ focus: () => inputRef.current?.focus(),
170
+ blur: () => inputRef.current?.blur(),
171
+ setText: (text: string) => _onChangeText(text),
172
+ };
173
+ });
174
+
175
+ /**
176
+ * Render the input view
177
+ */
178
+ const renderInputView = () => {
179
+ if (typingData && typingData?.length > 0) {
79
180
  return (
80
- <TextInput
81
- {...props}
82
- accessibilityLabel={accessibilityLabel}
83
- textAlignVertical="center"
84
- ref={inputRef}
181
+ <TouchableOpacity
85
182
  style={[
86
183
  styles.searchInput,
87
184
  {
88
- color: theme.colors.text.default,
185
+ justifyContent: 'center',
89
186
  },
90
187
  ]}
91
- value={value}
92
- onChangeText={_onChangeText}
93
- onFocus={_onFocus}
94
- onBlur={_onBlur}
95
- placeholder={placeholder}
96
- selectionColor={theme.colors.primary}
97
- placeholderTextColor={theme.colors.text.hint}
98
- />
99
- );
100
- };
101
-
102
- const renderIconView = () => {
103
- return (
104
- <View
105
- style={{
106
- flexDirection: 'row',
107
- }}>
108
- {focused && haveValue && (
109
- <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
110
- <Icon
111
- source="24_navigation_close_circle_full"
112
- size={16}
113
- color={theme.colors.text.hint}
114
- />
115
- </TouchableOpacity>
116
- )}
117
- {!!icon && (
118
- <View style={{flexDirection: 'row'}}>
119
- <View
120
- style={[
121
- styles.divider,
122
- {
123
- backgroundColor: theme.colors.primary,
124
- },
125
- ]}
126
- />
127
- <Icon
128
- color={iconColor}
129
- source={icon}
130
- style={styles.iconSearchInput}
131
- />
132
- </View>
133
- )}
134
- </View>
188
+ onPress={onPress}
189
+ activeOpacity={onPress ? 0.6 : 1}>
190
+ <TextTyping data={typingData} />
191
+ </TouchableOpacity>
135
192
  );
136
- };
137
-
138
- let inputState = 'empty';
139
-
140
- if (focused) {
141
- inputState = 'focus';
142
193
  }
143
194
 
144
- if (value && value?.length > 0) {
145
- inputState = 'filled';
146
- }
147
-
148
- const backgroundColor = hasColorBG
149
- ? theme.colors.background.default
150
- : theme.colors.background.surface;
195
+ return (
196
+ <TextInput
197
+ {...props}
198
+ accessibilityLabel={accessibilityLabel}
199
+ textAlignVertical="center"
200
+ ref={inputRef}
201
+ style={[
202
+ styles.searchInput,
203
+ {
204
+ color: theme.colors.text.default,
205
+ },
206
+ ]}
207
+ value={value}
208
+ onChangeText={_onChangeText}
209
+ onFocus={_onFocus}
210
+ onBlur={_onBlur}
211
+ placeholder={placeholder}
212
+ selectionColor={theme.colors.primary}
213
+ placeholderTextColor={theme.colors.text.hint}
214
+ />
215
+ );
216
+ };
151
217
 
218
+ /**
219
+ * Render trailing icon or text
220
+ */
221
+ const renderTrailing = () => {
152
222
  return (
153
- <ComponentContext.Provider
154
- value={{
155
- componentName: 'InputSearch',
156
- params,
157
- state: inputState,
158
- componentId: accessibilityLabel,
159
- }}>
160
- <View style={[style, styles.searchInputContainer]}>
161
- <View
162
- style={[
163
- styles.searchInputWrapper,
164
- {
165
- backgroundColor,
166
- },
167
- ]}>
223
+ <View style={Styles.row}>
224
+ {focused && haveValue && (
225
+ <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
168
226
  <Icon
169
- source={'navigation_search'}
170
- size={24}
227
+ source="24_navigation_close_circle_full"
228
+ size={16}
171
229
  color={theme.colors.text.hint}
172
230
  />
173
- {renderInputView()}
174
- {renderIconView()}
175
- </View>
176
- {showButtonText && (
177
- <TouchableOpacity onPress={onPressButtonText}>
178
- <Text
179
- typography={'action_default_bold'}
180
- style={styles.textButton}>
181
- {buttonText}
182
- </Text>
183
- </TouchableOpacity>
184
- )}
185
- </View>
186
- </ComponentContext.Provider>
231
+ </TouchableOpacity>
232
+ )}
233
+ {!!(icon || trailing) && (
234
+ <TouchableOpacity
235
+ style={Styles.row}
236
+ onPress={onPressTrailing ?? onPressIcon}>
237
+ <View
238
+ style={[
239
+ styles.divider,
240
+ {
241
+ backgroundColor: theme.colors.primary,
242
+ },
243
+ ]}
244
+ />
245
+ <Icon
246
+ color={iconColor || trailingColor}
247
+ source={(icon || trailing) as string}
248
+ style={styles.iconSearchInput}
249
+ />
250
+ </TouchableOpacity>
251
+ )}
252
+ </View>
187
253
  );
188
- },
189
- );
254
+ };
255
+
256
+ let inputState = 'empty';
257
+ if (focused) {
258
+ inputState = 'focus';
259
+ }
260
+ if (value && value?.length > 0) {
261
+ inputState = 'filled';
262
+ }
263
+
264
+ const backgroundColor = hasColorBG
265
+ ? theme.colors.background.default
266
+ : theme.colors.background.surface;
267
+
268
+ return (
269
+ <ComponentContext.Provider
270
+ value={{
271
+ componentName: 'InputSearch',
272
+ params,
273
+ state: inputState,
274
+ componentId: accessibilityLabel,
275
+ }}>
276
+ <View style={[style, styles.searchInputContainer]}>
277
+ <View
278
+ style={[
279
+ styles.searchInputWrapper,
280
+ {
281
+ backgroundColor,
282
+ },
283
+ ]}>
284
+ <Icon
285
+ source={'navigation_search'}
286
+ size={24}
287
+ color={theme.colors.text.hint}
288
+ />
289
+ {renderInputView()}
290
+ {renderTrailing()}
291
+ </View>
292
+ {showButtonText && (
293
+ <TouchableOpacity onPress={onPressButtonText}>
294
+ <Text typography={'action_default_bold'} style={styles.textButton}>
295
+ {buttonText}
296
+ </Text>
297
+ </TouchableOpacity>
298
+ )}
299
+ </View>
300
+ </ComponentContext.Provider>
301
+ );
302
+ };
190
303
 
191
- export default InputSearch;
304
+ export default forwardRef(InputSearch);
@@ -0,0 +1,113 @@
1
+ import React, {FC, memo, useEffect, useRef} from 'react';
2
+ import {Platform, StyleSheet, TextInput, TextStyle} from 'react-native';
3
+ import {Colors} from '../Consts';
4
+ import {scaleSize} from '../Text';
5
+
6
+ export interface TextTypingProps {
7
+ data: string[];
8
+ timeDelayEnd?: number;
9
+ timeDelayNextChar?: number;
10
+ inputStyle?: TextStyle;
11
+ }
12
+
13
+ const TextTyping: FC<TextTypingProps> = ({
14
+ data = [],
15
+ timeDelayEnd = 2000,
16
+ timeDelayNextChar = 20,
17
+ inputStyle = {},
18
+ }) => {
19
+ const textRef = useRef<TextInput>(null);
20
+
21
+ const currentIndex = useRef<number>(0);
22
+ const currentCharIndex = useRef<number>(0);
23
+ const currentText = useRef('');
24
+
25
+ useEffect(() => {
26
+ setTimeout(() => {
27
+ playAnimation();
28
+ }, 1000);
29
+ }, []);
30
+
31
+ const playAnimation = () => {
32
+ const handleResetList = () => {
33
+ currentIndex.current = 0;
34
+ handleAnimation();
35
+ };
36
+
37
+ const handleAnimation = () => {
38
+ const listChar = data?.[currentIndex.current]?.split('') || [];
39
+ listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
40
+ };
41
+
42
+ currentIndex.current >= data?.length
43
+ ? handleResetList()
44
+ : handleAnimation();
45
+ };
46
+
47
+ const showChar = (char?: string) => {
48
+ const handleResetString = () => {
49
+ setTimeout(() => {
50
+ reset();
51
+ }, timeDelayEnd);
52
+ };
53
+
54
+ const handleShowChar = () => {
55
+ setTimeout(() => {
56
+ textRef.current?.setNativeProps?.({
57
+ text: currentText.current + char,
58
+ });
59
+ currentText.current += char;
60
+ currentCharIndex.current++;
61
+ playAnimation();
62
+ }, timeDelayNextChar);
63
+ };
64
+
65
+ currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
66
+ ? handleResetString()
67
+ : handleShowChar();
68
+ };
69
+
70
+ const reset = () => {
71
+ const handleNextString = () => {
72
+ currentIndex.current++;
73
+ playAnimation();
74
+ };
75
+
76
+ const handleReset = () => {
77
+ setTimeout(() => {
78
+ textRef.current?.setNativeProps?.({
79
+ text: currentText.current.slice(0, -1),
80
+ });
81
+ currentText.current = currentText.current.slice(0, -1);
82
+ currentCharIndex.current--;
83
+ reset();
84
+ }, timeDelayNextChar);
85
+ };
86
+
87
+ currentCharIndex.current <= 0 ? handleNextString() : handleReset();
88
+ };
89
+
90
+ return (
91
+ <TextInput
92
+ style={[styles.inputStyle, inputStyle]}
93
+ ref={textRef}
94
+ editable={false}
95
+ autoCorrect={false}
96
+ pointerEvents={'none'}
97
+ numberOfLines={1}
98
+ />
99
+ );
100
+ };
101
+
102
+ export default memo(TextTyping);
103
+
104
+ const styles = StyleSheet.create({
105
+ inputStyle: {
106
+ color: Colors.black_12,
107
+ fontSize: scaleSize(12),
108
+ height: Platform.select({
109
+ android: 60,
110
+ ios: 36,
111
+ }),
112
+ },
113
+ });
package/Input/common.tsx CHANGED
@@ -1,5 +1,10 @@
1
1
  import React, {FC, useContext} from 'react';
2
- import {View, ViewStyle} from 'react-native';
2
+ import {
3
+ GestureResponderEvent,
4
+ TouchableOpacity,
5
+ View,
6
+ ViewStyle,
7
+ } from 'react-native';
3
8
  import {ApplicationContext} from '../Application';
4
9
  import {Theme} from '../Application/types';
5
10
  import {Styles} from '../Consts';
@@ -14,6 +19,7 @@ type FloatingViewProps = {
14
19
  floatingIcon?: string;
15
20
  required?: boolean;
16
21
  style?: ViewStyle;
22
+ onPress?: (e: GestureResponderEvent) => void;
17
23
  };
18
24
 
19
25
  export const DEFAULT_HEIGHT = scaleSize(104);
@@ -23,7 +29,7 @@ export const getBorderColor = (
23
29
  theme: Theme,
24
30
  focused: boolean,
25
31
  errorMessage?: string,
26
- disabled?: boolean,
32
+ disabled?: boolean
27
33
  ) => {
28
34
  let borderColor = theme.colors.border.default;
29
35
 
@@ -42,7 +48,18 @@ export const getBorderColor = (
42
48
  return {borderColor};
43
49
  };
44
50
 
45
- export const getSizeStyle = (size?: 'small' | 'large') => {
51
+ export const getSizeStyle = (
52
+ size?: 'small' | 'large',
53
+ multiline: boolean = false
54
+ ) => {
55
+ if (multiline)
56
+ return [
57
+ styles.multilineContainer,
58
+ {
59
+ minHeight: size === 'small' ? 48 : 56,
60
+ },
61
+ ];
62
+
46
63
  if (size === 'small') {
47
64
  return styles.smallContainer;
48
65
  }
@@ -70,7 +87,6 @@ export const ErrorView: FC<{
70
87
  <Text
71
88
  style={Styles.flex}
72
89
  color={errorMessage ? errorColor : hintColor}
73
- numberOfLines={2}
74
90
  typography={'description_default_regular'}>
75
91
  {errorMessage ?? hintTextDefault}
76
92
  </Text>
@@ -90,6 +106,7 @@ export const FloatingView: FC<FloatingViewProps> = ({
90
106
  floatingIcon,
91
107
  required,
92
108
  style,
109
+ onPress,
93
110
  }) => {
94
111
  const {theme} = useContext(ApplicationContext);
95
112
 
@@ -122,12 +139,14 @@ export const FloatingView: FC<FloatingViewProps> = ({
122
139
  )}
123
140
  </Text>
124
141
  {!!floatingIcon && (
125
- <Icon
126
- color={floatingIconTintColor}
127
- source={floatingIcon}
128
- size={16}
129
- style={styles.floatingIcon}
130
- />
142
+ <TouchableOpacity activeOpacity={onPress ? 0.6 : 1} onPress={onPress}>
143
+ <Icon
144
+ color={floatingIconTintColor}
145
+ source={floatingIcon}
146
+ size={16}
147
+ style={styles.floatingIcon}
148
+ />
149
+ </TouchableOpacity>
131
150
  )}
132
151
  </View>
133
152
  );