@momo-kits/foundation 0.92.21 → 0.92.23

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.
@@ -41,7 +41,7 @@ const OTPCaret: FC<CaretProps> = ({index, length}) => {
41
41
  duration: DURATION,
42
42
  useNativeDriver: true,
43
43
  }),
44
- ]),
44
+ ])
45
45
  ).start();
46
46
  }, []);
47
47
  const spacingStyle = !isNaN(Number(length)) &&
@@ -81,7 +81,7 @@ const InputOTP = forwardRef(
81
81
  hintText,
82
82
  ...props
83
83
  }: InputOTPProps,
84
- ref,
84
+ ref
85
85
  ) => {
86
86
  const MAX_LENGTH = 10;
87
87
  const [value, setValue] = useState('');
@@ -143,7 +143,7 @@ const InputOTP = forwardRef(
143
143
  {value[i] || '-'}
144
144
  </Text>
145
145
  )}
146
- </View>,
146
+ </View>
147
147
  );
148
148
  }
149
149
  return TextInputs;
@@ -248,7 +248,7 @@ const InputOTP = forwardRef(
248
248
  </TouchableOpacity>
249
249
  </ComponentContext.Provider>
250
250
  );
251
- },
251
+ }
252
252
  );
253
253
 
254
254
  export default InputOTP;
@@ -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,279 @@ 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';
21
24
 
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);
25
+ const TextTyping: FC<any> = ({
26
+ data = [],
27
+ timeDelayEnd = 2000,
28
+ timeDelayNextChar = 20,
29
+ }) => {
30
+ const textRef = useRef<TextInput>(null);
31
+
32
+ const currentIndex = useRef<number>(0);
33
+ const currentCharIndex = useRef<number>(0);
34
+ const currentText = useRef('');
35
+
36
+ useEffect(() => {
37
+ setTimeout(() => {
38
+ playAnimation();
39
+ }, 1000);
40
+ }, []);
41
+
42
+ const playAnimation = () => {
43
+ const handleResetList = () => {
44
+ currentIndex.current = 0;
45
+ handleAnimation();
46
+ };
47
+
48
+ const handleAnimation = () => {
49
+ const listChar = data?.[currentIndex.current]?.split('') || [];
50
+ listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
51
+ };
52
+
53
+ currentIndex.current >= data?.length
54
+ ? handleResetList()
55
+ : handleAnimation();
56
+ };
57
+
58
+ const showChar = (char?: string) => {
59
+ const handleResetString = () => {
60
+ setTimeout(() => {
61
+ reset();
62
+ }, timeDelayEnd);
52
63
  };
53
64
 
54
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
55
- setFocused(false);
56
- onBlur?.(e);
65
+ const handleShowChar = () => {
66
+ setTimeout(() => {
67
+ textRef.current?.setNativeProps?.({
68
+ text: currentText.current + char,
69
+ });
70
+ currentText.current += char;
71
+ currentCharIndex.current++;
72
+ playAnimation();
73
+ }, timeDelayNextChar);
57
74
  };
58
75
 
59
- const onClearText = () => {
60
- inputRef?.current?.clear();
61
- _onChangeText('');
76
+ currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
77
+ ? handleResetString()
78
+ : handleShowChar();
79
+ };
80
+
81
+ const reset = () => {
82
+ const handleNextString = () => {
83
+ currentIndex.current++;
84
+ playAnimation();
62
85
  };
63
86
 
64
- const _onChangeText = (text: string) => {
65
- checkTyping(text, haveValue, setHaveValue);
66
- onChangeText?.(text);
87
+ const handleReset = () => {
88
+ setTimeout(() => {
89
+ textRef.current?.setNativeProps?.({
90
+ text: currentText.current.slice(0, -1),
91
+ });
92
+ currentText.current = currentText.current.slice(0, -1);
93
+ currentCharIndex.current--;
94
+ reset();
95
+ }, timeDelayNextChar);
67
96
  };
68
97
 
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
- });
98
+ currentCharIndex.current <= 0 ? handleNextString() : handleReset();
99
+ };
100
+
101
+ return (
102
+ <TextInput
103
+ style={styles.inputStyle}
104
+ ref={textRef}
105
+ editable={false}
106
+ autoCorrect={false}
107
+ pointerEvents={'none'}
108
+ numberOfLines={1}
109
+ />
110
+ );
111
+ };
112
+
113
+ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
114
+ {
115
+ placeholder,
116
+ onFocus,
117
+ onBlur,
118
+ iconColor,
119
+ value,
120
+ onChangeText,
121
+ icon,
122
+ buttonText = 'Hủy',
123
+ showButtonText = true,
124
+ style,
125
+ defaultValue,
126
+ onPressButtonText,
127
+ params,
128
+ hasColorBG = false,
129
+ accessibilityLabel,
130
+ typingData = [],
131
+ onPress,
132
+ ...props
133
+ },
134
+ ref
135
+ ) => {
136
+ const {theme} = useContext(ApplicationContext);
137
+ const [focused, setFocused] = useState(false);
138
+ const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
139
+ const inputRef = useRef<TextInput | null>(null);
77
140
 
78
- const renderInputView = () => {
141
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
142
+ setFocused(true);
143
+ onFocus?.(e);
144
+ };
145
+
146
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
147
+ setFocused(false);
148
+ onBlur?.(e);
149
+ };
150
+
151
+ const onClearText = () => {
152
+ inputRef?.current?.clear();
153
+ _onChangeText('');
154
+ };
155
+
156
+ const _onChangeText = (text: string) => {
157
+ checkTyping(text, haveValue, setHaveValue);
158
+ onChangeText?.(text);
159
+ };
160
+
161
+ useImperativeHandle(ref, () => {
162
+ return {
163
+ clear: onClearText,
164
+ focus: () => inputRef.current?.focus(),
165
+ blur: () => inputRef.current?.blur(),
166
+ setText: (text: string) => _onChangeText(text),
167
+ };
168
+ });
169
+
170
+ const renderInputView = () => {
171
+ if (typingData?.length > 0) {
79
172
  return (
80
- <TextInput
81
- {...props}
82
- accessibilityLabel={accessibilityLabel}
83
- textAlignVertical="center"
84
- ref={inputRef}
173
+ <TouchableOpacity
85
174
  style={[
86
175
  styles.searchInput,
87
176
  {
88
- color: theme.colors.text.default,
177
+ justifyContent: 'center',
89
178
  },
90
179
  ]}
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>
180
+ onPress={onPress}
181
+ activeOpacity={onPress ? 0.6 : 1}>
182
+ <TextTyping data={typingData} />
183
+ </TouchableOpacity>
135
184
  );
136
- };
137
-
138
- let inputState = 'empty';
139
-
140
- if (focused) {
141
- inputState = 'focus';
142
- }
143
-
144
- if (value && value?.length > 0) {
145
- inputState = 'filled';
146
185
  }
147
186
 
148
- const backgroundColor = hasColorBG
149
- ? theme.colors.background.default
150
- : theme.colors.background.surface;
187
+ return (
188
+ <TextInput
189
+ {...props}
190
+ accessibilityLabel={accessibilityLabel}
191
+ textAlignVertical="center"
192
+ ref={inputRef}
193
+ style={[
194
+ styles.searchInput,
195
+ {
196
+ color: theme.colors.text.default,
197
+ },
198
+ ]}
199
+ value={value}
200
+ onChangeText={_onChangeText}
201
+ onFocus={_onFocus}
202
+ onBlur={_onBlur}
203
+ placeholder={placeholder}
204
+ selectionColor={theme.colors.primary}
205
+ placeholderTextColor={theme.colors.text.hint}
206
+ />
207
+ );
208
+ };
151
209
 
210
+ const renderIconView = () => {
152
211
  return (
153
- <ComponentContext.Provider
154
- value={{
155
- componentName: 'InputSearch',
156
- params,
157
- state: inputState,
158
- componentId: accessibilityLabel,
212
+ <View
213
+ style={{
214
+ flexDirection: 'row',
159
215
  }}>
160
- <View style={[style, styles.searchInputContainer]}>
161
- <View
162
- style={[
163
- styles.searchInputWrapper,
164
- {
165
- backgroundColor,
166
- },
167
- ]}>
216
+ {focused && haveValue && (
217
+ <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
168
218
  <Icon
169
- source={'navigation_search'}
170
- size={24}
219
+ source="24_navigation_close_circle_full"
220
+ size={16}
171
221
  color={theme.colors.text.hint}
172
222
  />
173
- {renderInputView()}
174
- {renderIconView()}
223
+ </TouchableOpacity>
224
+ )}
225
+ {!!icon && (
226
+ <View style={{flexDirection: 'row'}}>
227
+ <View
228
+ style={[
229
+ styles.divider,
230
+ {
231
+ backgroundColor: theme.colors.primary,
232
+ },
233
+ ]}
234
+ />
235
+ <Icon
236
+ color={iconColor}
237
+ source={icon}
238
+ style={styles.iconSearchInput}
239
+ />
175
240
  </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>
241
+ )}
242
+ </View>
187
243
  );
188
- },
189
- );
244
+ };
245
+
246
+ let inputState = 'empty';
247
+
248
+ if (focused) {
249
+ inputState = 'focus';
250
+ }
251
+
252
+ if (value && value?.length > 0) {
253
+ inputState = 'filled';
254
+ }
255
+
256
+ const backgroundColor = hasColorBG
257
+ ? theme.colors.background.default
258
+ : theme.colors.background.surface;
259
+
260
+ return (
261
+ <ComponentContext.Provider
262
+ value={{
263
+ componentName: 'InputSearch',
264
+ params,
265
+ state: inputState,
266
+ componentId: accessibilityLabel,
267
+ }}>
268
+ <View style={[style, styles.searchInputContainer]}>
269
+ <View
270
+ style={[
271
+ styles.searchInputWrapper,
272
+ {
273
+ backgroundColor,
274
+ },
275
+ ]}>
276
+ <Icon
277
+ source={'navigation_search'}
278
+ size={24}
279
+ color={theme.colors.text.hint}
280
+ />
281
+ {renderInputView()}
282
+ {renderIconView()}
283
+ </View>
284
+ {showButtonText && (
285
+ <TouchableOpacity onPress={onPressButtonText}>
286
+ <Text typography={'action_default_bold'} style={styles.textButton}>
287
+ {buttonText}
288
+ </Text>
289
+ </TouchableOpacity>
290
+ )}
291
+ </View>
292
+ </ComponentContext.Provider>
293
+ );
294
+ };
190
295
 
191
- export default InputSearch;
296
+ 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/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import {TextInputProps, ViewStyle} from 'react-native';
1
+ import {GestureResponderEvent, TextInputProps, ViewStyle} from 'react-native';
2
2
  import Input from './Input';
3
3
  import InputDropDown from './InputDropDown';
4
4
  import InputMoney from './InputMoney';
@@ -143,6 +143,17 @@ export interface InputSearchProps extends TextInputProps {
143
143
  * Optional. Represents the style of the InputSearch component.
144
144
  */
145
145
  params?: any;
146
+
147
+ /**
148
+ * Optional. Represents the data for typing animation.
149
+ */
150
+ typingData?: string[];
151
+
152
+ /**
153
+ * Optional. Represents the callback function to be called when the search button is pressed.
154
+ * @param e
155
+ */
156
+ onPress?: (e: GestureResponderEvent) => void;
146
157
  }
147
158
 
148
159
  export interface InputMoneyProps extends Omit<InputProps, 'placeholder'> {}
@@ -282,4 +293,11 @@ export interface InputDropDownProps extends InputProps {
282
293
  style?: ViewStyle | ViewStyle[];
283
294
  }
284
295
 
296
+ export type InputRef = {
297
+ clear: () => void;
298
+ focus: () => void | undefined;
299
+ blur: () => void | undefined;
300
+ setText: (text: string) => void;
301
+ };
302
+
285
303
  export {Input, InputDropDown, InputMoney, InputOTP, InputSearch, InputTextArea};
package/Input/styles.ts CHANGED
@@ -1,5 +1,5 @@
1
- import {StyleSheet} from 'react-native';
2
- import {Radius, Spacing} from '../Consts';
1
+ import {Platform, StyleSheet} from 'react-native';
2
+ import {Colors, Radius, Spacing} from '../Consts';
3
3
  import {scaleSize} from '../Text';
4
4
 
5
5
  export default StyleSheet.create({
@@ -133,7 +133,14 @@ export default StyleSheet.create({
133
133
  paddingVertical: Spacing.S,
134
134
  fontWeight: 'bold',
135
135
  },
136
-
136
+ inputStyle: {
137
+ color: Colors.black_12,
138
+ fontSize: scaleSize(12),
139
+ height: Platform.select({
140
+ android: 60,
141
+ ios: 36,
142
+ }),
143
+ },
137
144
  //OTP
138
145
  otpWrapper: {
139
146
  width: '100%',