@momo-kits/foundation 0.81.52 → 0.81.53-beta.0

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 (60) 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} +43 -18
  4. package/Application/Components.tsx +71 -53
  5. package/Application/ModalScreen.tsx +12 -183
  6. package/Application/Navigation.ts +2 -2
  7. package/Application/NavigationContainer.tsx +54 -53
  8. package/Application/Navigator.ts +53 -35
  9. package/Application/StackScreen.tsx +225 -30
  10. package/Application/index.ts +8 -12
  11. package/Application/types.ts +45 -27
  12. package/Application/utils.tsx +19 -15
  13. package/Assets/icon_bank.json +1 -1
  14. package/Badge/Badge.tsx +1 -1
  15. package/Button/index.tsx +44 -25
  16. package/CheckBox/index.tsx +37 -26
  17. package/CheckBox/types.ts +5 -3
  18. package/Consts/colors+spacing+radius.ts +14 -0
  19. package/Icon/index.tsx +9 -8
  20. package/Icon/types.ts +2 -2
  21. package/IconButton/index.tsx +24 -14
  22. package/Image/index.tsx +23 -8
  23. package/Image/types.ts +2 -0
  24. package/Input/Input.tsx +116 -26
  25. package/Input/InputDropDown.tsx +66 -51
  26. package/Input/InputMoney.tsx +106 -15
  27. package/Input/InputOTP.tsx +45 -22
  28. package/Input/InputSearch.tsx +92 -44
  29. package/Input/InputTextArea.tsx +26 -6
  30. package/Input/common.tsx +48 -22
  31. package/Input/index.tsx +94 -77
  32. package/Input/styles.ts +35 -5
  33. package/Input/utils.ts +1 -1
  34. package/Layout/Card.tsx +4 -3
  35. package/Layout/FloatingButton.tsx +168 -0
  36. package/Layout/GridSystem.tsx +1 -1
  37. package/Layout/ItemList.tsx +3 -2
  38. package/Layout/Screen.tsx +87 -49
  39. package/Layout/Section.tsx +4 -3
  40. package/Layout/styles.ts +11 -7
  41. package/Layout/utils.ts +4 -4
  42. package/Loader/index.tsx +2 -0
  43. package/Popup/PopupNotify.tsx +55 -44
  44. package/Popup/PopupPromotion.tsx +5 -7
  45. package/Popup/types.ts +1 -7
  46. package/Radio/index.tsx +35 -20
  47. package/Radio/types.ts +5 -3
  48. package/Switch/index.tsx +32 -18
  49. package/Switch/types.ts +5 -3
  50. package/Tag/styles.ts +3 -2
  51. package/Text/index.tsx +24 -5
  52. package/Text/styles.ts +8 -8
  53. package/Title/index.tsx +170 -103
  54. package/Title/styles.ts +36 -9
  55. package/Title/types.ts +16 -48
  56. package/code-scanner.js +125 -16
  57. package/index.ts +7 -0
  58. package/package.json +6 -6
  59. package/publish.sh +16 -21
  60. package/tsconfig.json +1 -1
@@ -15,12 +15,12 @@ import {
15
15
  TouchableOpacity,
16
16
  View,
17
17
  } from 'react-native';
18
- import {CaretProps, InputOTPProps} from './index';
18
+ import {ApplicationContext, ComponentContext} from '../Application';
19
+ import {Spacing} from '../Consts';
19
20
  import {scaleSize, Text} from '../Text';
20
- import styles from './styles';
21
21
  import {ErrorView, getBorderColor} from './common';
22
- import {ApplicationContext} from '../Application';
23
- import {Spacing} from '../Consts';
22
+ import {CaretProps, InputOTPProps} from './index';
23
+ import styles from './styles';
24
24
 
25
25
  const OTPCaret: FC<CaretProps> = ({index, length}) => {
26
26
  const DURATION = 300;
@@ -70,15 +70,16 @@ const InputOTP = forwardRef(
70
70
  length,
71
71
  errorMessage,
72
72
  errorSpacing,
73
- floatingValue,
74
- floatingIconColor,
75
- floatingIcon,
73
+ floatingValue = 'Label',
76
74
  placeholder,
77
75
  onChangeText,
78
- onFocus,
79
76
  onBlur,
77
+ onFocus,
80
78
  dataType = 'number',
79
+ params,
81
80
  style,
81
+ accessibilityLabel,
82
+ hintText,
82
83
  ...props
83
84
  }: InputOTPProps,
84
85
  ref,
@@ -86,13 +87,19 @@ const InputOTP = forwardRef(
86
87
  const MAX_LENGTH = 10;
87
88
  const [value, setValue] = useState('');
88
89
  const [focused, setFocused] = useState(false);
89
- const inputRef = useRef<TextInput>(null);
90
+ const inputRef = useRef<TextInput | null>(null);
90
91
  const {theme} = useContext(ApplicationContext);
91
92
 
92
93
  useImperativeHandle(ref, () => ({
93
94
  onChangeText: (text: string) => {
94
95
  _onChangeText(text);
95
96
  },
97
+ focus: () => {
98
+ setFocused(true);
99
+ inputRef.current?.focus()
100
+ },
101
+ blur: () => inputRef.current?.blur(),
102
+ setText: (text: string) => _onChangeText(text),
96
103
  }));
97
104
 
98
105
  const onFocusInput = () => {
@@ -119,16 +126,16 @@ const InputOTP = forwardRef(
119
126
  onChangeText?.(text);
120
127
  };
121
128
 
122
- const renderInputs = (length: number) => {
129
+ const renderInputs = (inputLength: number) => {
123
130
  const TextInputs: React.ReactNode[] = [];
124
- for (let i = 0; i < length; i++) {
131
+ for (let i = 0; i < inputLength; i++) {
125
132
  TextInputs.push(
126
133
  <View>
127
134
  {focused && value.length === i ? (
128
- <OTPCaret index={i} length={length} />
135
+ <OTPCaret index={i} length={inputLength} />
129
136
  ) : (
130
137
  <Text
131
- style={i !== length - 1 && {marginRight: Spacing.L}}
138
+ style={i !== inputLength - 1 && {marginRight: Spacing.L}}
132
139
  color={
133
140
  !!value[i]
134
141
  ? theme.colors.text.default
@@ -194,7 +201,9 @@ const InputOTP = forwardRef(
194
201
  styles.otpFloatingView,
195
202
  {backgroundColor: theme.colors.background.surface},
196
203
  ]}>
197
- <Text typography={'description_default_regular'}>
204
+ <Text
205
+ typography={'description_default_regular'}
206
+ color={theme.colors.text.hint}>
198
207
  {floatingValue}
199
208
  </Text>
200
209
  </View>
@@ -205,11 +214,12 @@ const InputOTP = forwardRef(
205
214
  <View style={styles.inputView}>
206
215
  <TextInput
207
216
  {...props}
217
+ accessibilityLabel={accessibilityLabel}
208
218
  ref={inputRef}
209
219
  value={value}
210
220
  onChangeText={_onChangeText}
211
221
  keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
212
- onFocus={onFocusInput}
222
+ onFocus={onFocus}
213
223
  onBlur={onBlurInput}
214
224
  style={{opacity: 0}}
215
225
  selectionColor={theme.colors.primary}
@@ -221,13 +231,26 @@ const InputOTP = forwardRef(
221
231
  };
222
232
 
223
233
  return (
224
- <TouchableOpacity
225
- activeOpacity={1}
226
- onPress={onFocusInput}
227
- style={[style, styles.otpWrapper]}>
228
- {renderInputView()}
229
- <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
230
- </TouchableOpacity>
234
+ <ComponentContext.Provider
235
+ value={{
236
+ componentName: 'InputOTP',
237
+ params,
238
+ componentLabel: floatingValue,
239
+ state: 'enabled',
240
+ componentId: accessibilityLabel,
241
+ }}>
242
+ <TouchableOpacity
243
+ activeOpacity={1}
244
+ onPress={onFocusInput}
245
+ style={[style, styles.otpWrapper]}>
246
+ {renderInputView()}
247
+ <ErrorView
248
+ errorMessage={errorMessage}
249
+ errorSpacing={errorSpacing}
250
+ hintText={hintText}
251
+ />
252
+ </TouchableOpacity>
253
+ </ComponentContext.Provider>
231
254
  );
232
255
  },
233
256
  );
@@ -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,12 +12,13 @@ import {
6
12
  TouchableOpacity,
7
13
  View,
8
14
  } from 'react-native';
9
- import {InputSearchProps} from './index';
10
- import {ApplicationContext} from '../Application';
11
- import styles from './styles';
15
+ import {ApplicationContext, ComponentContext} from '../Application';
12
16
  import {Icon} from '../Icon';
13
17
  import {Text} from '../Text';
18
+ import {InputSearchProps} from './index';
19
+ import styles from './styles';
14
20
  import {checkTyping} from './utils';
21
+ import {Styles} from '../Consts';
15
22
 
16
23
  const InputSearch = forwardRef(
17
24
  (
@@ -19,24 +26,30 @@ const InputSearch = forwardRef(
19
26
  placeholder,
20
27
  onFocus,
21
28
  onBlur,
22
- iconColor,
23
29
  value,
24
- onChangeText,
25
30
  icon,
31
+ iconColor,
32
+ onPressIcon,
33
+ trailing,
34
+ trailingColor,
35
+ onPressTrailing,
36
+ onChangeText,
26
37
  buttonText = 'Hủy',
27
38
  showButtonText = true,
28
39
  style,
29
40
  defaultValue,
30
41
  onPressButtonText,
31
- hasColorBG = true,
42
+ params,
43
+ hasColorBG = false,
44
+ accessibilityLabel,
32
45
  ...props
33
46
  }: InputSearchProps,
34
- ref,
47
+ ref
35
48
  ) => {
36
49
  const {theme} = useContext(ApplicationContext);
37
50
  const [focused, setFocused] = useState(false);
38
51
  const [haveValue, setHaveValue] = useState(!!value || !!defaultValue);
39
- const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
52
+ const inputRef = useRef<TextInput | null>(null);
40
53
 
41
54
  const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
42
55
  setFocused(true);
@@ -58,10 +71,23 @@ const InputSearch = forwardRef(
58
71
  onChangeText?.(text);
59
72
  };
60
73
 
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
+ */
61
86
  const renderInputView = () => {
62
87
  return (
63
88
  <TextInput
64
89
  {...props}
90
+ accessibilityLabel={accessibilityLabel}
65
91
  textAlignVertical="center"
66
92
  ref={inputRef}
67
93
  style={[
@@ -81,12 +107,12 @@ const InputSearch = forwardRef(
81
107
  );
82
108
  };
83
109
 
84
- const renderIconView = () => {
110
+ /**
111
+ * Render trailing icon or text
112
+ */
113
+ const renderTrailing = () => {
85
114
  return (
86
- <View
87
- style={{
88
- flexDirection: 'row',
89
- }}>
115
+ <View style={Styles.row}>
90
116
  {focused && haveValue && (
91
117
  <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
92
118
  <Icon
@@ -96,8 +122,10 @@ const InputSearch = forwardRef(
96
122
  />
97
123
  </TouchableOpacity>
98
124
  )}
99
- {!!icon && (
100
- <View style={{flexDirection: 'row'}}>
125
+ {!!(icon || trailing) && (
126
+ <TouchableOpacity
127
+ style={Styles.row}
128
+ onPress={onPressTrailing ?? onPressIcon}>
101
129
  <View
102
130
  style={[
103
131
  styles.divider,
@@ -107,45 +135,65 @@ const InputSearch = forwardRef(
107
135
  ]}
108
136
  />
109
137
  <Icon
110
- color={iconColor}
111
- source={icon}
138
+ color={iconColor || trailingColor}
139
+ source={(icon || trailing) as string}
112
140
  style={styles.iconSearchInput}
113
141
  />
114
- </View>
142
+ </TouchableOpacity>
115
143
  )}
116
144
  </View>
117
145
  );
118
146
  };
119
147
 
120
- const backgroundColor = hasColorBG ? theme.colors.background.default : theme.colors.background.surface
148
+ let inputState = 'empty';
149
+ if (focused) {
150
+ inputState = 'focus';
151
+ }
152
+ if (value && value?.length > 0) {
153
+ inputState = 'filled';
154
+ }
155
+
156
+ const backgroundColor = hasColorBG
157
+ ? theme.colors.background.default
158
+ : theme.colors.background.surface;
121
159
 
122
160
  return (
123
- <View style={[style, styles.searchInputContainer]}>
124
- <View
125
- style={[
126
- styles.searchInputWrapper,
127
- {
128
- backgroundColor,
129
- }
130
- ]}>
131
- <Icon
132
- source={'navigation_search'}
133
- size={24}
134
- color={theme.colors.text.hint}
135
- />
136
- {renderInputView()}
137
- {renderIconView()}
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
+ ]}>
176
+ <Icon
177
+ source={'navigation_search'}
178
+ size={24}
179
+ color={theme.colors.text.hint}
180
+ />
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
+ )}
138
193
  </View>
139
- {showButtonText && (
140
- <TouchableOpacity onPress={onPressButtonText}>
141
- <Text typography={'action_default_bold'} style={styles.textButton}>
142
- {buttonText}
143
- </Text>
144
- </TouchableOpacity>
145
- )}
146
- </View>
194
+ </ComponentContext.Provider>
147
195
  );
148
- },
196
+ }
149
197
  );
150
198
 
151
199
  export default 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
  },
package/Input/common.tsx CHANGED
@@ -1,11 +1,16 @@
1
- import {View, ViewStyle} from 'react-native';
2
- import styles from './styles';
3
- import {scaleSize, Text} from '../Text';
4
1
  import React, {FC, useContext} from 'react';
2
+ import {
3
+ GestureResponderEvent,
4
+ TouchableOpacity,
5
+ View,
6
+ ViewStyle,
7
+ } from 'react-native';
5
8
  import {ApplicationContext} from '../Application';
6
- import {Icon} from '../Icon';
7
- import {Styles} from '../Consts';
8
9
  import {Theme} from '../Application/types';
10
+ import {Styles} from '../Consts';
11
+ import {Icon} from '../Icon';
12
+ import {scaleSize, Text} from '../Text';
13
+ import styles from './styles';
9
14
 
10
15
  type FloatingViewProps = {
11
16
  floatingValue?: string;
@@ -14,6 +19,7 @@ type FloatingViewProps = {
14
19
  floatingIcon?: string;
15
20
  required?: boolean;
16
21
  style?: ViewStyle;
22
+ onPress?: (e: GestureResponderEvent) => void;
17
23
  };
18
24
 
19
25
  export const DEFAULT_HEIGHT = scaleSize(104);
@@ -23,7 +29,7 @@ export const getBorderColor = (
23
29
  theme: Theme,
24
30
  focused: boolean,
25
31
  errorMessage?: string,
26
- disabled?: boolean,
32
+ disabled?: boolean
27
33
  ) => {
28
34
  let borderColor = theme.colors.border.default;
29
35
 
@@ -42,30 +48,47 @@ export const getBorderColor = (
42
48
  return {borderColor};
43
49
  };
44
50
 
45
- export const getSizeStyle = (size?: 'small' | 'large') => {
51
+ export const getSizeStyle = (
52
+ size?: 'small' | 'large',
53
+ multiline: boolean = false
54
+ ) => {
55
+ if (multiline)
56
+ return [
57
+ styles.multilineContainer,
58
+ {
59
+ minHeight: size === 'small' ? 48 : 56,
60
+ },
61
+ ];
62
+
46
63
  if (size === 'small') {
47
64
  return styles.smallContainer;
48
65
  }
49
66
  return styles.container;
50
67
  };
51
- export const ErrorView: FC<{errorMessage?: string; errorSpacing?: boolean}> = ({
52
- errorMessage,
53
- errorSpacing,
54
- }) => {
68
+ export const ErrorView: FC<{
69
+ errorMessage?: string;
70
+ errorSpacing?: boolean;
71
+ hintText?: string;
72
+ }> = ({errorMessage, errorSpacing, hintText}) => {
55
73
  const {theme} = useContext(ApplicationContext);
56
74
  const errorColor = theme.colors.error.primary;
57
- if (errorMessage) {
75
+ const hintColor = theme.colors.text.hint;
76
+ const hintTextDefault = hintText ?? 'Không thể chỉnh sửa';
77
+ if (errorMessage || hintText) {
58
78
  return (
59
79
  <View style={styles.errorView}>
60
80
  <View style={styles.errorIcon}>
61
- <Icon color={errorColor} source="ic_error" size={16} />
81
+ <Icon
82
+ color={errorMessage ? errorColor : hintColor}
83
+ source="ic_error"
84
+ size={16}
85
+ />
62
86
  </View>
63
87
  <Text
64
88
  style={Styles.flex}
65
- color={errorColor}
66
- numberOfLines={2}
89
+ color={errorMessage ? errorColor : hintColor}
67
90
  typography={'description_default_regular'}>
68
- {errorMessage}
91
+ {errorMessage ?? hintTextDefault}
69
92
  </Text>
70
93
  </View>
71
94
  );
@@ -83,6 +106,7 @@ export const FloatingView: FC<FloatingViewProps> = ({
83
106
  floatingIcon,
84
107
  required,
85
108
  style,
109
+ onPress,
86
110
  }) => {
87
111
  const {theme} = useContext(ApplicationContext);
88
112
 
@@ -115,12 +139,14 @@ export const FloatingView: FC<FloatingViewProps> = ({
115
139
  )}
116
140
  </Text>
117
141
  {!!floatingIcon && (
118
- <Icon
119
- color={floatingIconTintColor}
120
- source={floatingIcon}
121
- size={16}
122
- style={styles.floatingIcon}
123
- />
142
+ <TouchableOpacity activeOpacity={onPress ? 0.6 : 1} onPress={onPress}>
143
+ <Icon
144
+ color={floatingIconTintColor}
145
+ source={floatingIcon}
146
+ size={16}
147
+ style={styles.floatingIcon}
148
+ />
149
+ </TouchableOpacity>
124
150
  )}
125
151
  </View>
126
152
  );