@momo-kits/foundation 0.92.26-beta.2 → 0.92.26-beta.21

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.
@@ -115,6 +115,7 @@ export type BottomSheetParams = {
115
115
  title: string;
116
116
  surface?: boolean;
117
117
  };
118
+ useNativeModal?: boolean;
118
119
  surface?: boolean;
119
120
  onDismiss?: (type?: string) => void;
120
121
  barrierDismissible?: boolean;
@@ -145,19 +146,29 @@ export type RuntimeToolType = {
145
146
  key: string;
146
147
  };
147
148
 
148
- export interface NavigationOptions extends StackNavigationOptions {
149
+ export interface NavigationOptions
150
+ extends Omit<StackNavigationOptions, 'headerRight'> {
149
151
  preventBack?: PopupNotifyProps;
150
152
  onPressLeftHeader?: () => void;
151
153
  surface?: boolean;
152
154
  hiddenBack?: boolean;
153
155
  customTitle?: TitleCustomProps;
154
- toolkitParams?: {
155
- pinnedTool?: PinnedToolType;
156
- runtimeTools?: RuntimeToolType[];
157
- preventClose?: PopupNotifyProps;
158
- };
156
+ headerRight?: HeaderRightToolkit | HeaderRightActions | any;
159
157
  }
160
158
 
159
+ export type HeaderRightToolkit = {
160
+ type?: 'toolkit';
161
+ pinnedTool?: PinnedToolType;
162
+ runtimeTools?: RuntimeToolType[];
163
+ preventClose?: PopupNotifyProps;
164
+ useMore?: boolean;
165
+ };
166
+
167
+ export type HeaderRightActions = {
168
+ type?: 'icon';
169
+ buttons: NavigationButtonProps[];
170
+ };
171
+
161
172
  export interface HeaderBackProps extends NavigationButtonProps {
162
173
  preventBack?: PopupNotifyProps;
163
174
  onPressLeftHeader?: () => void;
@@ -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={{
@@ -44,7 +44,7 @@ const InputMoney = forwardRef(
44
44
  value: _value,
45
45
  ...props
46
46
  }: InputMoneyProps,
47
- ref,
47
+ ref
48
48
  ) => {
49
49
  const {theme} = useContext(ApplicationContext);
50
50
  const [focused, setFocused] = useState(false);
@@ -63,7 +63,7 @@ const InputMoney = forwardRef(
63
63
  };
64
64
 
65
65
  const [value, setValue] = useState(
66
- defaultValue ? validateText(defaultValue) : '',
66
+ defaultValue ? validateText(defaultValue) : ''
67
67
  );
68
68
 
69
69
  const onClearText = () => {
@@ -198,7 +198,7 @@ const InputMoney = forwardRef(
198
198
  </View>
199
199
  </ComponentContext.Provider>
200
200
  );
201
- },
201
+ }
202
202
  );
203
203
 
204
204
  export default InputMoney;
@@ -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);