@momo-kits/foundation 0.92.21-beta.0 → 0.92.21-beta.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.
@@ -1,5 +1,6 @@
1
1
  import React, {
2
2
  forwardRef,
3
+ ForwardRefRenderFunction,
3
4
  useContext,
4
5
  useImperativeHandle,
5
6
  useRef,
@@ -15,177 +16,195 @@ import {
15
16
  import {ApplicationContext, ComponentContext} from '../Application';
16
17
  import {Icon} from '../Icon';
17
18
  import {Text} from '../Text';
18
- import {InputSearchProps} from './index';
19
+ import {InputSearchProps, InputSearchRef} from './index';
19
20
  import styles from './styles';
20
21
  import {checkTyping} from './utils';
22
+ import TextTyping from './TextTyping';
21
23
 
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);
24
+ const InputSearch: ForwardRefRenderFunction<
25
+ InputSearchRef,
26
+ InputSearchProps
27
+ > = (
28
+ {
29
+ placeholder,
30
+ onFocus,
31
+ onBlur,
32
+ iconColor,
33
+ value,
34
+ onChangeText,
35
+ icon,
36
+ buttonText = 'Hủy',
37
+ showButtonText = true,
38
+ style,
39
+ defaultValue,
40
+ onPressButtonText,
41
+ params,
42
+ hasColorBG = false,
43
+ accessibilityLabel,
44
+ typingData = [],
45
+ onPress,
46
+ ...props
47
+ },
48
+ ref
49
+ ) => {
50
+ const {theme} = useContext(ApplicationContext);
51
+ const [focused, setFocused] = useState(false);
52
+ const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
53
+ const inputRef = useRef<TextInput | null>(null);
48
54
 
49
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
50
- setFocused(true);
51
- onFocus?.(e);
52
- };
55
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
56
+ setFocused(true);
57
+ onFocus?.(e);
58
+ };
53
59
 
54
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
55
- setFocused(false);
56
- onBlur?.(e);
57
- };
60
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
61
+ setFocused(false);
62
+ onBlur?.(e);
63
+ };
58
64
 
59
- const onClearText = () => {
60
- inputRef?.current?.clear();
61
- _onChangeText('');
62
- };
65
+ const onClearText = () => {
66
+ inputRef?.current?.clear();
67
+ _onChangeText('');
68
+ };
63
69
 
64
- const _onChangeText = (text: string) => {
65
- checkTyping(text, haveValue, setHaveValue);
66
- onChangeText?.(text);
67
- };
70
+ const _onChangeText = (text: string) => {
71
+ checkTyping(text, haveValue, setHaveValue);
72
+ onChangeText?.(text);
73
+ };
68
74
 
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
- });
75
+ useImperativeHandle(ref, () => {
76
+ return {
77
+ clear: onClearText,
78
+ focus: () => inputRef.current?.focus(),
79
+ blur: () => inputRef.current?.blur(),
80
+ setText: (text: string) => _onChangeText(text),
81
+ };
82
+ });
77
83
 
78
- const renderInputView = () => {
84
+ const renderInputView = () => {
85
+ if (typingData?.length > 0) {
79
86
  return (
80
- <TextInput
81
- {...props}
82
- accessibilityLabel={accessibilityLabel}
83
- textAlignVertical="center"
84
- ref={inputRef}
87
+ <TouchableOpacity
85
88
  style={[
86
89
  styles.searchInput,
87
90
  {
88
- color: theme.colors.text.default,
91
+ justifyContent: 'center',
89
92
  },
90
93
  ]}
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
- />
94
+ onPress={onPress}
95
+ activeOpacity={onPress ? 0.6 : 1}>
96
+ <TextTyping data={typingData} />
97
+ </TouchableOpacity>
99
98
  );
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>
135
- );
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
99
  }
147
100
 
148
- const backgroundColor = hasColorBG
149
- ? theme.colors.background.default
150
- : theme.colors.background.surface;
101
+ return (
102
+ <TextInput
103
+ {...props}
104
+ accessibilityLabel={accessibilityLabel}
105
+ textAlignVertical="center"
106
+ ref={inputRef}
107
+ style={[
108
+ styles.searchInput,
109
+ {
110
+ color: theme.colors.text.default,
111
+ },
112
+ ]}
113
+ value={value}
114
+ onChangeText={_onChangeText}
115
+ onFocus={_onFocus}
116
+ onBlur={_onBlur}
117
+ placeholder={placeholder}
118
+ selectionColor={theme.colors.primary}
119
+ placeholderTextColor={theme.colors.text.hint}
120
+ />
121
+ );
122
+ };
151
123
 
124
+ const renderIconView = () => {
152
125
  return (
153
- <ComponentContext.Provider
154
- value={{
155
- componentName: 'InputSearch',
156
- params,
157
- state: inputState,
158
- componentId: accessibilityLabel,
126
+ <View
127
+ style={{
128
+ flexDirection: 'row',
159
129
  }}>
160
- <View style={[style, styles.searchInputContainer]}>
161
- <View
162
- style={[
163
- styles.searchInputWrapper,
164
- {
165
- backgroundColor,
166
- },
167
- ]}>
130
+ {focused && haveValue && (
131
+ <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
168
132
  <Icon
169
- source={'navigation_search'}
170
- size={24}
133
+ source="24_navigation_close_circle_full"
134
+ size={16}
171
135
  color={theme.colors.text.hint}
172
136
  />
173
- {renderInputView()}
174
- {renderIconView()}
137
+ </TouchableOpacity>
138
+ )}
139
+ {!!icon && (
140
+ <View style={{flexDirection: 'row'}}>
141
+ <View
142
+ style={[
143
+ styles.divider,
144
+ {
145
+ backgroundColor: theme.colors.primary,
146
+ },
147
+ ]}
148
+ />
149
+ <Icon
150
+ color={iconColor}
151
+ source={icon}
152
+ style={styles.iconSearchInput}
153
+ />
175
154
  </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>
155
+ )}
156
+ </View>
187
157
  );
188
- },
189
- );
158
+ };
159
+
160
+ let inputState = 'empty';
161
+
162
+ if (focused) {
163
+ inputState = 'focus';
164
+ }
165
+
166
+ if (value && value?.length > 0) {
167
+ inputState = 'filled';
168
+ }
169
+
170
+ const backgroundColor = hasColorBG
171
+ ? theme.colors.background.default
172
+ : theme.colors.background.surface;
173
+
174
+ return (
175
+ <ComponentContext.Provider
176
+ value={{
177
+ componentName: 'InputSearch',
178
+ params,
179
+ state: inputState,
180
+ componentId: accessibilityLabel,
181
+ }}>
182
+ <View style={[style, styles.searchInputContainer]}>
183
+ <View
184
+ style={[
185
+ styles.searchInputWrapper,
186
+ {
187
+ backgroundColor,
188
+ },
189
+ ]}>
190
+ <Icon
191
+ source={'navigation_search'}
192
+ size={24}
193
+ color={theme.colors.text.hint}
194
+ />
195
+ {renderInputView()}
196
+ {renderIconView()}
197
+ </View>
198
+ {showButtonText && (
199
+ <TouchableOpacity onPress={onPressButtonText}>
200
+ <Text typography={'action_default_bold'} style={styles.textButton}>
201
+ {buttonText}
202
+ </Text>
203
+ </TouchableOpacity>
204
+ )}
205
+ </View>
206
+ </ComponentContext.Provider>
207
+ );
208
+ };
190
209
 
191
- export default InputSearch;
210
+ 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,9 @@ export interface InputSearchProps extends TextInputProps {
143
143
  * Optional. Represents the style of the InputSearch component.
144
144
  */
145
145
  params?: any;
146
+
147
+ typingData?: string[];
148
+ onPress?: (e: GestureResponderEvent) => void;
146
149
  }
147
150
 
148
151
  export interface InputMoneyProps extends Omit<InputProps, 'placeholder'> {}
@@ -282,4 +285,11 @@ export interface InputDropDownProps extends InputProps {
282
285
  style?: ViewStyle | ViewStyle[];
283
286
  }
284
287
 
288
+ export type InputSearchRef = {
289
+ clear: () => void;
290
+ focus: () => void | undefined;
291
+ blur: () => void | undefined;
292
+ setText: (text: string) => void;
293
+ };
294
+
285
295
  export {Input, InputDropDown, InputMoney, InputOTP, InputSearch, InputTextArea};
@@ -0,0 +1,58 @@
1
+ import React, {FC, useEffect, useRef} from 'react';
2
+ import {InputSearch} from '../Input';
3
+ import {Animated, Dimensions} from 'react-native';
4
+ import {AnimatedInputSearchProps} from './types';
5
+ import {Spacing} from '../Consts';
6
+ import {scaleSize} from '../Text';
7
+
8
+ const {width: screenWidth} = Dimensions.get('window');
9
+ const SCREEN_PADDING = 12;
10
+ const BACK_WIDTH = scaleSize(28);
11
+
12
+ const AnimatedInputSearch: FC<AnimatedInputSearchProps> = ({
13
+ animatedValue,
14
+ headerRightWidth,
15
+ inputSearchProps,
16
+ inputRef,
17
+ }) => {
18
+ const widthAnimated = useRef(new Animated.Value(0));
19
+
20
+ useEffect(() => {
21
+ const listener = animatedValue.addListener(({value}) => {
22
+ widthAnimated.current.setValue(value);
23
+ });
24
+ return () => {
25
+ animatedValue?.removeListener(listener);
26
+ };
27
+ }, []);
28
+
29
+ const translateX = widthAnimated.current.interpolate({
30
+ inputRange: [0, 100],
31
+ outputRange: [SCREEN_PADDING, BACK_WIDTH + SCREEN_PADDING * 2],
32
+ extrapolate: 'clamp',
33
+ });
34
+
35
+ return (
36
+ <Animated.View
37
+ style={{
38
+ transform: [{translateX}],
39
+ marginVertical: Spacing.S,
40
+ width: widthAnimated.current.interpolate({
41
+ inputRange: [0, 100],
42
+ outputRange: [
43
+ screenWidth - SCREEN_PADDING * 2,
44
+ screenWidth - SCREEN_PADDING * 3 - BACK_WIDTH - headerRightWidth,
45
+ ],
46
+ extrapolate: 'clamp',
47
+ }),
48
+ }}>
49
+ <InputSearch
50
+ ref={inputRef}
51
+ {...inputSearchProps}
52
+ showButtonText={false}
53
+ />
54
+ </Animated.View>
55
+ );
56
+ };
57
+
58
+ export default AnimatedInputSearch;