@momo-kits/foundation 0.92.34 → 0.102.1

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.
@@ -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
 
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={{
@@ -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)) &&
@@ -82,7 +82,7 @@ const InputOTP = forwardRef(
82
82
  hintText,
83
83
  ...props
84
84
  }: InputOTPProps,
85
- ref,
85
+ ref
86
86
  ) => {
87
87
  const MAX_LENGTH = 10;
88
88
  const [value, setValue] = useState('');
@@ -147,7 +147,7 @@ const InputOTP = forwardRef(
147
147
  {value[i] || '-'}
148
148
  </Text>
149
149
  )}
150
- </View>,
150
+ </View>
151
151
  );
152
152
  }
153
153
  return TextInputs;
@@ -252,7 +252,7 @@ const InputOTP = forwardRef(
252
252
  </TouchableOpacity>
253
253
  </ComponentContext.Provider>
254
254
  );
255
- },
255
+ }
256
256
  );
257
257
 
258
258
  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,185 +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';
21
24
  import {Styles} from '../Consts';
22
25
 
23
- const InputSearch = forwardRef(
24
- (
25
- {
26
- placeholder,
27
- onFocus,
28
- onBlur,
29
- value,
30
- icon,
31
- iconColor,
32
- onPressIcon,
33
- trailing,
34
- trailingColor,
35
- onPressTrailing,
36
- onChangeText,
37
- buttonText = 'Hủy',
38
- showButtonText = true,
39
- style,
40
- defaultValue,
41
- onPressButtonText,
42
- params,
43
- hasColorBG = false,
44
- accessibilityLabel,
45
- ...props
46
- }: InputSearchProps,
47
- ref
48
- ) => {
49
- const {theme} = useContext(ApplicationContext);
50
- const [focused, setFocused] = useState(false);
51
- const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
52
- const inputRef = useRef<TextInput | null>(null);
53
-
54
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
55
- setFocused(true);
56
- 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();
47
+ };
48
+
49
+ const handleAnimation = () => {
50
+ const listChar = data?.[currentIndex.current]?.split('') || [];
51
+ listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
52
+ };
53
+
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);
57
75
  };
58
76
 
59
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
60
- setFocused(false);
61
- onBlur?.(e);
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();
62
86
  };
63
87
 
64
- const onClearText = () => {
65
- inputRef?.current?.clear();
66
- _onChangeText('');
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);
67
97
  };
68
98
 
69
- const _onChangeText = (text: string) => {
70
- checkTyping(text, haveValue, setHaveValue);
71
- onChangeText?.(text);
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),
72
172
  };
173
+ });
73
174
 
74
- useImperativeHandle(ref, () => {
75
- return {
76
- clear: onClearText,
77
- focus: () => inputRef.current?.focus(),
78
- blur: () => inputRef.current?.blur(),
79
- setText: (text: string) => _onChangeText(text),
80
- };
81
- });
82
-
83
- /**
84
- * Render the input view
85
- */
86
- const renderInputView = () => {
175
+ /**
176
+ * Render the input view
177
+ */
178
+ const renderInputView = () => {
179
+ if (typingData && typingData?.length > 0) {
87
180
  return (
88
- <TextInput
89
- {...props}
90
- accessibilityLabel={accessibilityLabel}
91
- textAlignVertical="center"
92
- ref={inputRef}
181
+ <TouchableOpacity
93
182
  style={[
94
183
  styles.searchInput,
95
184
  {
96
- color: theme.colors.text.default,
185
+ justifyContent: 'center',
97
186
  },
98
187
  ]}
99
- value={value}
100
- onChangeText={_onChangeText}
101
- onFocus={_onFocus}
102
- onBlur={_onBlur}
103
- placeholder={placeholder}
104
- selectionColor={theme.colors.primary}
105
- placeholderTextColor={theme.colors.text.hint}
106
- />
107
- );
108
- };
109
-
110
- /**
111
- * Render trailing icon or text
112
- */
113
- const renderTrailing = () => {
114
- return (
115
- <View style={Styles.row}>
116
- {focused && haveValue && (
117
- <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
118
- <Icon
119
- source="24_navigation_close_circle_full"
120
- size={16}
121
- color={theme.colors.text.hint}
122
- />
123
- </TouchableOpacity>
124
- )}
125
- {!!(icon || trailing) && (
126
- <TouchableOpacity
127
- style={Styles.row}
128
- onPress={onPressTrailing ?? onPressIcon}>
129
- <View
130
- style={[
131
- styles.divider,
132
- {
133
- backgroundColor: theme.colors.primary,
134
- },
135
- ]}
136
- />
137
- <Icon
138
- color={iconColor || trailingColor}
139
- source={(icon || trailing) as string}
140
- style={styles.iconSearchInput}
141
- />
142
- </TouchableOpacity>
143
- )}
144
- </View>
188
+ onPress={onPress}
189
+ activeOpacity={onPress ? 0.6 : 1}>
190
+ <TextTyping data={typingData} />
191
+ </TouchableOpacity>
145
192
  );
146
- };
147
-
148
- let inputState = 'empty';
149
- if (focused) {
150
- inputState = 'focus';
151
- }
152
- if (value && value?.length > 0) {
153
- inputState = 'filled';
154
193
  }
155
194
 
156
- const backgroundColor = hasColorBG
157
- ? theme.colors.background.default
158
- : 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
+ };
159
217
 
218
+ /**
219
+ * Render trailing icon or text
220
+ */
221
+ const renderTrailing = () => {
160
222
  return (
161
- <ComponentContext.Provider
162
- value={{
163
- componentName: 'InputSearch',
164
- params,
165
- state: inputState,
166
- componentId: accessibilityLabel,
167
- }}>
168
- <View style={[style, styles.searchInputContainer]}>
169
- <View
170
- style={[
171
- styles.searchInputWrapper,
172
- {
173
- backgroundColor,
174
- },
175
- ]}>
223
+ <View style={Styles.row}>
224
+ {focused && haveValue && (
225
+ <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
176
226
  <Icon
177
- source={'navigation_search'}
178
- size={24}
227
+ source="24_navigation_close_circle_full"
228
+ size={16}
179
229
  color={theme.colors.text.hint}
180
230
  />
181
- {renderInputView()}
182
- {renderTrailing()}
183
- </View>
184
- {showButtonText && (
185
- <TouchableOpacity onPress={onPressButtonText}>
186
- <Text
187
- typography={'action_default_bold'}
188
- style={styles.textButton}>
189
- {buttonText}
190
- </Text>
191
- </TouchableOpacity>
192
- )}
193
- </View>
194
- </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>
195
253
  );
254
+ };
255
+
256
+ let inputState = 'empty';
257
+ if (focused) {
258
+ inputState = 'focus';
196
259
  }
197
- );
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
+ };
198
303
 
199
- export default InputSearch;
304
+ export default forwardRef(InputSearch);