@momo-kits/foundation 0.89.5 → 0.89.6-rc.2

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 (73) 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} +42 -18
  4. package/Application/Components.tsx +350 -86
  5. package/Application/Localize.ts +44 -0
  6. package/Application/ModalScreen.tsx +14 -185
  7. package/Application/Navigation.ts +5 -19
  8. package/Application/NavigationContainer.tsx +62 -72
  9. package/Application/Navigator.ts +53 -35
  10. package/Application/StackScreen.tsx +289 -32
  11. package/Application/index.ts +13 -15
  12. package/Application/types.ts +64 -35
  13. package/Application/utils.tsx +76 -30
  14. package/Assets/icon_bank.json +1 -1
  15. package/Badge/Badge.tsx +1 -1
  16. package/Badge/BadgeDotAnimation.tsx +95 -0
  17. package/Badge/index.tsx +2 -1
  18. package/Badge/styles.ts +50 -6
  19. package/Button/index.tsx +44 -26
  20. package/CheckBox/index.tsx +37 -26
  21. package/CheckBox/types.ts +5 -3
  22. package/Consts/colors+spacing+radius.ts +14 -0
  23. package/Icon/index.tsx +9 -8
  24. package/Icon/types.ts +2 -2
  25. package/IconButton/index.tsx +24 -14
  26. package/Image/index.tsx +23 -8
  27. package/Image/types.ts +2 -0
  28. package/Input/Input.tsx +116 -26
  29. package/Input/InputDropDown.tsx +66 -51
  30. package/Input/InputMoney.tsx +106 -15
  31. package/Input/InputOTP.tsx +49 -26
  32. package/Input/InputSearch.tsx +252 -99
  33. package/Input/InputTextArea.tsx +26 -6
  34. package/Input/TextTyping.tsx +113 -0
  35. package/Input/common.tsx +48 -22
  36. package/Input/index.tsx +112 -77
  37. package/Input/styles.ts +48 -11
  38. package/Input/utils.ts +1 -1
  39. package/Layout/Card.tsx +4 -3
  40. package/Layout/FloatingButton.tsx +168 -0
  41. package/Layout/GridSystem.tsx +1 -1
  42. package/Layout/ItemList.tsx +3 -2
  43. package/Layout/Screen.tsx +242 -121
  44. package/Layout/Section.tsx +4 -3
  45. package/Layout/styles.ts +11 -13
  46. package/Layout/types.ts +1 -1
  47. package/Layout/utils.ts +4 -4
  48. package/Loader/index.tsx +2 -0
  49. package/Pagination/PaginationScroll.tsx +2 -2
  50. package/Pagination/styles.ts +1 -1
  51. package/Popup/PopupNotify.tsx +56 -45
  52. package/Popup/PopupPromotion.tsx +5 -7
  53. package/Popup/types.ts +1 -7
  54. package/Radio/index.tsx +35 -20
  55. package/Radio/types.ts +5 -3
  56. package/Switch/index.tsx +32 -18
  57. package/Switch/types.ts +5 -3
  58. package/Tag/index.tsx +9 -2
  59. package/Tag/styles.ts +3 -2
  60. package/Tag/types.ts +12 -0
  61. package/Text/index.tsx +24 -5
  62. package/Text/styles.ts +8 -8
  63. package/Text/types.ts +19 -1
  64. package/Title/index.tsx +170 -103
  65. package/Title/styles.ts +36 -9
  66. package/Title/types.ts +16 -48
  67. package/code-scanner.js +125 -16
  68. package/index.ts +7 -0
  69. package/migrate_typo.js +56 -0
  70. package/package.json +11 -10
  71. package/publish.sh +16 -21
  72. package/tsconfig.json +1 -1
  73. package/verify.js +37 -40
@@ -1,4 +1,13 @@
1
- import React, {forwardRef, useContext, useRef, useState} from 'react';
1
+ import React, {
2
+ FC,
3
+ forwardRef,
4
+ ForwardRefRenderFunction,
5
+ useContext,
6
+ useEffect,
7
+ useImperativeHandle,
8
+ useRef,
9
+ useState,
10
+ } from 'react';
2
11
  import {
3
12
  NativeSyntheticEvent,
4
13
  TextInput,
@@ -6,127 +15,271 @@ import {
6
15
  TouchableOpacity,
7
16
  View,
8
17
  } from 'react-native';
9
- import {InputSearchProps} from './index';
10
- import {ApplicationContext} from '../Application';
11
- import styles from './styles';
18
+ import {ApplicationContext, ComponentContext} from '../Application';
12
19
  import {Icon} from '../Icon';
13
20
  import {Text} from '../Text';
21
+ import {InputRef, InputSearchProps} from './index';
22
+ import styles from './styles';
14
23
  import {checkTyping} from './utils';
24
+ import {Styles} from '../Consts';
25
+
26
+ const TextTyping: FC<any> = ({
27
+ data = [],
28
+ timeDelayEnd = 2000,
29
+ timeDelayNextChar = 20,
30
+ }) => {
31
+ const textRef = useRef<TextInput>(null);
15
32
 
16
- const InputSearch = forwardRef(
17
- (
18
- {
19
- placeholder,
20
- onFocus,
21
- onBlur,
22
- iconColor,
23
- value,
24
- onChangeText,
25
- icon,
26
- buttonText = 'Hủy',
27
- showButtonText = true,
28
- style,
29
- defaultValue,
30
- onPressButtonText,
31
- hasColorBG = true,
32
- ...props
33
- }: InputSearchProps,
34
- ref,
35
- ) => {
36
- const {theme} = useContext(ApplicationContext);
37
- const [focused, setFocused] = useState(false);
38
- const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
39
- const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
40
-
41
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
42
- setFocused(true);
43
- onFocus?.(e);
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();
44
47
  };
45
48
 
46
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
47
- setFocused(false);
48
- onBlur?.(e);
49
+ const handleAnimation = () => {
50
+ const listChar = data?.[currentIndex.current]?.split('') || [];
51
+ listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
49
52
  };
50
53
 
51
- const onClearText = () => {
52
- inputRef?.current?.clear();
53
- _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);
64
+ };
65
+
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);
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();
54
86
  };
55
87
 
56
- const _onChangeText = (text: string) => {
57
- checkTyping(text, haveValue, setHaveValue);
58
- onChangeText?.(text);
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
+ };
155
+
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),
59
172
  };
173
+ });
60
174
 
61
- const renderInputView = () => {
175
+ /**
176
+ * Render the input view
177
+ */
178
+ const renderInputView = () => {
179
+ if (typingData && typingData?.length > 0) {
62
180
  return (
63
- <TextInput
64
- {...props}
65
- textAlignVertical="center"
66
- ref={inputRef}
181
+ <TouchableOpacity
67
182
  style={[
68
183
  styles.searchInput,
69
184
  {
70
- color: theme.colors.text.default,
185
+ justifyContent: 'center',
71
186
  },
72
187
  ]}
73
- value={value}
74
- onChangeText={_onChangeText}
75
- onFocus={_onFocus}
76
- onBlur={_onBlur}
77
- placeholder={placeholder}
78
- selectionColor={theme.colors.primary}
79
- placeholderTextColor={theme.colors.text.hint}
80
- />
188
+ onPress={onPress}
189
+ activeOpacity={onPress ? 0.6 : 1}>
190
+ <TextTyping data={typingData} />
191
+ </TouchableOpacity>
81
192
  );
82
- };
193
+ }
83
194
 
84
- const renderIconView = () => {
85
- return (
86
- <View
87
- style={{
88
- flexDirection: 'row',
89
- }}>
90
- {focused && haveValue && (
91
- <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
92
- <Icon
93
- source="24_navigation_close_circle_full"
94
- size={16}
95
- color={theme.colors.text.hint}
96
- />
97
- </TouchableOpacity>
98
- )}
99
- {!!icon && (
100
- <View style={{flexDirection: 'row'}}>
101
- <View
102
- style={[
103
- styles.divider,
104
- {
105
- backgroundColor: theme.colors.primary,
106
- },
107
- ]}
108
- />
109
- <Icon
110
- color={iconColor}
111
- source={icon}
112
- style={styles.iconSearchInput}
113
- />
114
- </View>
115
- )}
116
- </View>
117
- );
118
- };
119
-
120
- const backgroundColor = hasColorBG ? theme.colors.background.default : 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
+ };
121
217
 
218
+ /**
219
+ * Render trailing icon or text
220
+ */
221
+ const renderTrailing = () => {
122
222
  return (
223
+ <View style={Styles.row}>
224
+ {focused && haveValue && (
225
+ <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
226
+ <Icon
227
+ source="24_navigation_close_circle_full"
228
+ size={16}
229
+ color={theme.colors.text.hint}
230
+ />
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>
253
+ );
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
+ }}>
123
276
  <View style={[style, styles.searchInputContainer]}>
124
277
  <View
125
278
  style={[
126
279
  styles.searchInputWrapper,
127
280
  {
128
281
  backgroundColor,
129
- }
282
+ },
130
283
  ]}>
131
284
  <Icon
132
285
  source={'navigation_search'}
@@ -134,7 +287,7 @@ const InputSearch = forwardRef(
134
287
  color={theme.colors.text.hint}
135
288
  />
136
289
  {renderInputView()}
137
- {renderIconView()}
290
+ {renderTrailing()}
138
291
  </View>
139
292
  {showButtonText && (
140
293
  <TouchableOpacity onPress={onPressButtonText}>
@@ -144,8 +297,8 @@ const InputSearch = forwardRef(
144
297
  </TouchableOpacity>
145
298
  )}
146
299
  </View>
147
- );
148
- },
149
- );
300
+ </ComponentContext.Provider>
301
+ );
302
+ };
150
303
 
151
- export default InputSearch;
304
+ export default forwardRef(InputSearch);
@@ -1,4 +1,10 @@
1
- import React, {forwardRef, useContext, useRef, useState} from 'react';
1
+ import React, {
2
+ forwardRef,
3
+ useContext,
4
+ useImperativeHandle,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
2
8
  import {
3
9
  NativeSyntheticEvent,
4
10
  TextInput,
@@ -6,7 +12,8 @@ import {
6
12
  TouchableOpacity,
7
13
  View,
8
14
  } from 'react-native';
9
- import styles from './styles';
15
+ import {ApplicationContext} from '../Application';
16
+ import {Icon} from '../Icon';
10
17
  import {Text} from '../Text';
11
18
  import {
12
19
  DEFAULT_HEIGHT,
@@ -16,8 +23,7 @@ import {
16
23
  MAX_LENGTH,
17
24
  } from './common';
18
25
  import {InputTextAreaProps} from './index';
19
- import {Icon} from '../Icon';
20
- import {ApplicationContext} from '../Application';
26
+ import styles from './styles';
21
27
 
22
28
  const InputTextArea = forwardRef(
23
29
  (
@@ -38,6 +44,7 @@ const InputTextArea = forwardRef(
38
44
  required,
39
45
  errorSpacing,
40
46
  style,
47
+ hintText,
41
48
  ...props
42
49
  }: InputTextAreaProps,
43
50
  ref,
@@ -45,7 +52,7 @@ const InputTextArea = forwardRef(
45
52
  const {theme} = useContext(ApplicationContext);
46
53
  const [focused, setFocused] = useState(false);
47
54
  const [inputValue, setInputValue] = useState(defaultValue ?? '');
48
- const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
55
+ const inputRef = useRef<TextInput | null>(null);
49
56
 
50
57
  const onClearText = () => {
51
58
  inputRef?.current?.clear();
@@ -67,6 +74,15 @@ const InputTextArea = forwardRef(
67
74
  onBlur?.(e);
68
75
  };
69
76
 
77
+ useImperativeHandle(ref, () => {
78
+ return {
79
+ clear: onClearText,
80
+ focus: () => inputRef.current?.focus(),
81
+ blur: () => inputRef.current?.blur(),
82
+ setText: (text: string) => _onChangeText(text),
83
+ };
84
+ });
85
+
70
86
  const renderCountingView = () => {
71
87
  return (
72
88
  <View style={styles.countingView}>
@@ -150,7 +166,11 @@ const InputTextArea = forwardRef(
150
166
  return (
151
167
  <View style={[style, styles.wrapper]}>
152
168
  {renderInputView()}
153
- <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
169
+ <ErrorView
170
+ errorMessage={errorMessage}
171
+ errorSpacing={errorSpacing}
172
+ hintText={hintText}
173
+ />
154
174
  </View>
155
175
  );
156
176
  },
@@ -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
+ });