@momo-kits/foundation 0.76.1-beta.2 → 0.77.1-optimize.10

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 (100) hide show
  1. package/Application/BottomTab/BottomTabBar.tsx +439 -0
  2. package/Application/BottomTab/index.tsx +138 -0
  3. package/Application/Components.tsx +348 -167
  4. package/Application/ModalScreen.tsx +135 -98
  5. package/Application/Navigation.ts +22 -16
  6. package/Application/NavigationContainer.tsx +99 -48
  7. package/Application/Navigator.ts +110 -41
  8. package/Application/StackScreen.tsx +129 -8
  9. package/Application/index.ts +23 -4
  10. package/Application/types.ts +86 -34
  11. package/Application/utils.tsx +67 -49
  12. package/Assets/DotAnimation.json +256 -0
  13. package/Assets/SpinnerAnimation.json +1027 -0
  14. package/Assets/icon.json +83 -5
  15. package/Assets/icon_bank.json +506 -0
  16. package/Assets/language.json +202 -0
  17. package/Badge/Badge.tsx +56 -0
  18. package/Badge/BadgeDot.tsx +11 -0
  19. package/Badge/BadgeRibbon.tsx +125 -0
  20. package/Badge/Shape.tsx +29 -0
  21. package/Badge/index.tsx +7 -0
  22. package/Badge/styles.ts +58 -0
  23. package/Badge/types.ts +63 -0
  24. package/Button/index.tsx +91 -40
  25. package/Button/styles.ts +19 -0
  26. package/CheckBox/index.tsx +58 -43
  27. package/CheckBox/styles.ts +1 -1
  28. package/CheckBox/types.ts +6 -4
  29. package/Consts/colors+spacing+radius.ts +18 -0
  30. package/Consts/styles.ts +0 -26
  31. package/Consts/theme.ts +4 -0
  32. package/Divider/index.tsx +2 -0
  33. package/Icon/index.tsx +14 -9
  34. package/Icon/types.ts +4 -3
  35. package/IconButton/index.tsx +24 -15
  36. package/Image/index.tsx +63 -47
  37. package/Image/styles.ts +1 -9
  38. package/Image/types.ts +5 -1
  39. package/Input/Input.tsx +227 -116
  40. package/Input/InputDropDown.tsx +132 -0
  41. package/Input/InputMoney.tsx +173 -111
  42. package/Input/InputOTP.tsx +257 -0
  43. package/Input/InputSearch.tsx +160 -132
  44. package/Input/InputTextArea.tsx +150 -123
  45. package/Input/common.tsx +35 -17
  46. package/Input/index.tsx +211 -18
  47. package/Input/styles.ts +44 -7
  48. package/Input/utils.ts +2 -2
  49. package/Layout/Card.tsx +67 -29
  50. package/Layout/FloatingButton.tsx +168 -0
  51. package/Layout/GridSystem.tsx +31 -21
  52. package/Layout/Item.tsx +21 -25
  53. package/Layout/ItemList.tsx +55 -0
  54. package/Layout/ItemSectionList.tsx +39 -0
  55. package/Layout/Screen.tsx +355 -153
  56. package/Layout/Section.tsx +66 -34
  57. package/Layout/index.ts +13 -3
  58. package/Layout/styles.ts +101 -0
  59. package/Layout/types.ts +1 -39
  60. package/Layout/utils.ts +27 -52
  61. package/Loader/DotLoader.tsx +37 -0
  62. package/Loader/Loader.tsx +13 -0
  63. package/Loader/ProgressBar.tsx +44 -0
  64. package/Loader/Spinner.tsx +29 -0
  65. package/Loader/index.tsx +4 -0
  66. package/Loader/styles.ts +10 -0
  67. package/Loader/types.ts +35 -0
  68. package/Loader/utils.ts +25 -0
  69. package/Pagination/PaginationDot.tsx +7 -2
  70. package/Pagination/PaginationNumber.tsx +8 -6
  71. package/Pagination/PaginationScroll.tsx +0 -1
  72. package/Pagination/PaginationWhiteDot.tsx +8 -5
  73. package/Pagination/styles.ts +3 -0
  74. package/Pagination/types.ts +2 -0
  75. package/Popup/PopupNotify.tsx +108 -46
  76. package/Popup/PopupPromotion.tsx +63 -22
  77. package/Popup/types.ts +15 -4
  78. package/Radio/index.tsx +35 -21
  79. package/Radio/types.ts +7 -5
  80. package/Skeleton/index.tsx +1 -1
  81. package/Skeleton/types.ts +3 -0
  82. package/Switch/index.tsx +35 -24
  83. package/Switch/types.ts +5 -3
  84. package/Tag/index.tsx +2 -4
  85. package/Text/index.tsx +27 -22
  86. package/Text/styles.ts +38 -0
  87. package/Text/types.ts +25 -13
  88. package/Text/utils.ts +1 -1
  89. package/Title/index.tsx +186 -0
  90. package/Title/styles.ts +79 -0
  91. package/Title/types.ts +20 -0
  92. package/code-scanner.js +143 -0
  93. package/index.ts +6 -0
  94. package/migrate_typo.js +56 -0
  95. package/package.json +14 -9
  96. package/tsconfig.json +24 -0
  97. package/verify.js +47 -0
  98. package/Application/BottomTab.tsx +0 -52
  99. package/Application/NavigationButton.tsx +0 -54
  100. package/Assets/header-background.png +0 -0
@@ -1,4 +1,10 @@
1
- import React, {FC, 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,128 +12,184 @@ import {
6
12
  TouchableOpacity,
7
13
  View,
8
14
  } from 'react-native';
9
- import {ApplicationContext} from '../Application';
10
- import styles from './styles';
15
+ import {ApplicationContext, ComponentContext} from '../Application';
16
+ import {Icon} from '../Icon';
11
17
  import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
12
18
  import {InputMoneyProps} from './index';
13
- import {Icon} from '../Icon';
19
+ import styles from './styles';
14
20
  import {formatMoneyToNumber, formatNumberToMoney} from './utils';
15
21
 
16
- const InputMoney: FC<InputMoneyProps> = ({
17
- onChangeText,
18
- floatingValue,
19
- floatingIcon,
20
- size = 'small',
21
- onBlur,
22
- onFocus,
23
- errorMessage,
24
- icon,
25
- disabled,
26
- floatingIconColor,
27
- iconColor,
28
- required,
29
- ...props
30
- }) => {
31
- const {theme} = useContext(ApplicationContext);
32
- const [focused, setFocused] = useState(false);
33
- const inputRef = useRef<any>(null);
34
- const [value, setValue] = useState('');
35
- const onClearText = () => {
36
- inputRef?.current?.clear();
37
- setValue('');
38
- onChangeText?.('');
39
- };
40
- const _onChangeText = (text: string) => {
41
- if (text.length < value.length && value.indexOf(text) === 0) {
42
- text = value.slice(0, -2) + 'đ';
22
+ const InputMoney = forwardRef(
23
+ (
24
+ {
25
+ onChangeText,
26
+ floatingValue,
27
+ floatingIcon,
28
+ size = 'small',
29
+ onBlur,
30
+ onFocus,
31
+ errorMessage,
32
+ icon,
33
+ disabled = false,
34
+ floatingIconColor,
35
+ iconColor,
36
+ required = false,
37
+ errorSpacing,
38
+ style,
39
+ params,
40
+ defaultValue = '',
41
+ accessibilityLabel,
42
+ hintText,
43
+ ...props
44
+ }: InputMoneyProps,
45
+ ref,
46
+ ) => {
47
+ const {theme} = useContext(ApplicationContext);
48
+ const [focused, setFocused] = useState(false);
49
+ const inputRef = useRef<TextInput>(null);
50
+
51
+ const validateText = (text: string) => {
52
+ const valueFormat = formatMoneyToNumber(text, 'đ');
53
+ return formatNumberToMoney(valueFormat, 'đ');
54
+ };
55
+
56
+ const [value, setValue] = useState(
57
+ defaultValue ? validateText(defaultValue) : '',
58
+ );
59
+
60
+ const onClearText = () => {
61
+ inputRef?.current?.clear();
62
+ setValue('');
63
+ onChangeText?.('');
64
+ };
65
+
66
+ const _onChangeText = (text: string) => {
67
+ if (text.length < value.length && value.indexOf(text) === 0) {
68
+ text = value.slice(0, -2) + 'đ';
69
+ }
70
+ setValue(validateText(text));
71
+ onChangeText?.(formatMoneyToNumber(text, 'đ').toString());
72
+ };
73
+
74
+ useImperativeHandle(ref, () => {
75
+ return {
76
+ clear: onClearText,
77
+ focus: () => inputRef.current?.focus(),
78
+ blur: () => inputRef.current?.blur(),
79
+ setText: _onChangeText,
80
+ };
81
+ });
82
+
83
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
84
+ setFocused(true);
85
+ onFocus?.(e);
86
+ };
87
+
88
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
89
+ setFocused(false);
90
+ onBlur?.(e);
91
+ };
92
+
93
+ const renderInputView = () => {
94
+ const disabledColor = theme.colors.text.disable;
95
+ let textColor = theme.colors.text.default;
96
+ let placeholderColor = theme.colors.text.hint;
97
+ let iconTintColor = iconColor;
98
+
99
+ if (disabled) {
100
+ textColor = disabledColor;
101
+ placeholderColor = disabledColor;
102
+ iconTintColor = disabledColor;
103
+ }
104
+
105
+ return (
106
+ <View
107
+ style={[
108
+ styles.inputWrapper,
109
+ {backgroundColor: theme.colors.background.surface},
110
+ getSizeStyle(size),
111
+ getBorderColor(theme, focused, errorMessage, disabled),
112
+ ]}>
113
+ <FloatingView
114
+ floatingValue={floatingValue}
115
+ floatingIconColor={floatingIconColor}
116
+ disabled={disabled}
117
+ required={required}
118
+ floatingIcon={floatingIcon}
119
+ />
120
+ <View style={styles.inputView}>
121
+ <TextInput
122
+ {...props}
123
+ accessibilityLabel={accessibilityLabel}
124
+ editable={!disabled}
125
+ ref={inputRef}
126
+ keyboardType={'number-pad'}
127
+ style={[
128
+ styles.moneyInput,
129
+ {
130
+ color: textColor,
131
+ },
132
+ ]}
133
+ value={value}
134
+ onChangeText={_onChangeText}
135
+ onFocus={_onFocus}
136
+ onBlur={_onBlur}
137
+ selectionColor={theme.colors.primary}
138
+ placeholderTextColor={placeholderColor}
139
+ placeholder={'0đ'}
140
+ />
141
+ </View>
142
+ <View style={styles.iconView}>
143
+ {focused && (
144
+ <TouchableOpacity
145
+ style={styles.iconWrapper}
146
+ onPress={onClearText}>
147
+ <Icon
148
+ source="24_navigation_close_circle_full"
149
+ size={16}
150
+ color={theme.colors.text.hint}
151
+ />
152
+ </TouchableOpacity>
153
+ )}
154
+ {!!icon && <Icon color={iconTintColor} source={icon} />}
155
+ </View>
156
+ </View>
157
+ );
158
+ };
159
+
160
+ let inputState = 'active';
161
+
162
+ if (value && value?.length > 0) {
163
+ inputState = 'filled';
164
+ }
165
+ if (errorMessage && errorMessage?.length > 0) {
166
+ inputState = 'error';
43
167
  }
44
- const valueFormat = formatMoneyToNumber(text, 'đ');
45
- setValue(formatNumberToMoney(valueFormat, 'đ'));
46
- onChangeText?.(formatMoneyToNumber(text, 'đ').toString());
47
- };
48
-
49
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
50
- setFocused(true);
51
- onFocus?.(e);
52
- };
53
-
54
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
55
- setFocused(false);
56
- onBlur?.(e);
57
- };
58
-
59
- const renderInputView = () => {
60
- const disabledColor = theme.colors.text.disable;
61
- let textColor = theme.colors.text.default;
62
- let placeholderColor = theme.colors.text.hint;
63
- let iconTintColor = iconColor;
64
168
 
65
169
  if (disabled) {
66
- textColor = disabledColor;
67
- placeholderColor = disabledColor;
68
- iconTintColor = disabledColor;
170
+ inputState = 'disabled';
69
171
  }
70
172
 
71
173
  return (
72
- <View
73
- style={[
74
- getSizeStyle(size),
75
- getBorderColor(focused, errorMessage, disabled),
76
- styles.inputWrapper,
77
- {backgroundColor: theme.colors.background.surface},
78
- ]}>
79
- <FloatingView
80
- floatingValue={floatingValue}
81
- floatingIconColor={floatingIconColor}
82
- disabled={disabled}
83
- required={required}
84
- floatingIcon={floatingIcon}
85
- />
86
- <View style={styles.inputView}>
87
- <TextInput
88
- {...props}
89
- accessibilityLabel={`TextInput`}
90
- editable={!disabled}
91
- textAlignVertical="top"
92
- ref={inputRef}
93
- keyboardType={'number-pad'}
94
- style={[
95
- styles.moneyInput,
96
- {
97
- color: textColor,
98
- },
99
- ]}
100
- value={value}
101
- onChangeText={_onChangeText}
102
- onFocus={_onFocus}
103
- onBlur={_onBlur}
104
- selectionColor={theme.colors.primary}
105
- placeholderTextColor={placeholderColor}
106
- placeholder={'0đ'}
174
+ <ComponentContext.Provider
175
+ value={{
176
+ componentName: 'InputMoney',
177
+ params,
178
+ componentLabel: floatingValue,
179
+ state: inputState,
180
+ componentId: accessibilityLabel,
181
+ }}>
182
+ <View style={[style, styles.wrapper]}>
183
+ {renderInputView()}
184
+ <ErrorView
185
+ errorMessage={errorMessage}
186
+ errorSpacing={errorSpacing}
187
+ hintText={hintText}
107
188
  />
108
189
  </View>
109
- <View style={styles.iconView}>
110
- {focused && (
111
- <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
112
- <Icon
113
- source="24_navigation_close_circle_full"
114
- size={16}
115
- color={theme.colors.text.hint}
116
- />
117
- </TouchableOpacity>
118
- )}
119
- {!!icon && <Icon color={iconTintColor} source={icon} />}
120
- </View>
121
- </View>
190
+ </ComponentContext.Provider>
122
191
  );
123
- };
124
-
125
- return (
126
- <View style={styles.wrapper} accessibilityLabel={`InputMoney`}>
127
- {renderInputView()}
128
- <ErrorView errorMessage={errorMessage} />
129
- </View>
130
- );
131
- };
192
+ },
193
+ );
132
194
 
133
195
  export default InputMoney;
@@ -0,0 +1,257 @@
1
+ import React, {
2
+ FC,
3
+ forwardRef,
4
+ useContext,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import {
11
+ Animated,
12
+ NativeSyntheticEvent,
13
+ TextInput,
14
+ TextInputFocusEventData,
15
+ TouchableOpacity,
16
+ View,
17
+ } from 'react-native';
18
+ import {ApplicationContext, ComponentContext} from '../Application';
19
+ import {Spacing} from '../Consts';
20
+ import {scaleSize, Text} from '../Text';
21
+ import {ErrorView, getBorderColor} from './common';
22
+ import {CaretProps, InputOTPProps} from './index';
23
+ import styles from './styles';
24
+
25
+ const OTPCaret: FC<CaretProps> = ({index, length}) => {
26
+ const DURATION = 300;
27
+ const {theme} = useContext(ApplicationContext);
28
+ const opacity = useRef(new Animated.Value(0)).current;
29
+
30
+ useEffect(() => {
31
+ Animated.loop(
32
+ Animated.sequence([
33
+ Animated.timing(opacity, {
34
+ toValue: 1,
35
+ duration: DURATION,
36
+ useNativeDriver: true,
37
+ }),
38
+ Animated.delay(DURATION * 2),
39
+ Animated.timing(opacity, {
40
+ toValue: 0,
41
+ duration: DURATION,
42
+ useNativeDriver: true,
43
+ }),
44
+ ]),
45
+ ).start();
46
+ }, []);
47
+ const spacingStyle = !isNaN(Number(length)) &&
48
+ index !== Number(length) - 1 && {marginRight: Spacing.L};
49
+
50
+ return (
51
+ <View style={[{flexDirection: 'row', alignItems: 'center'}, spacingStyle]}>
52
+ <Animated.View
53
+ style={{
54
+ height: scaleSize(12),
55
+ width: 1,
56
+ backgroundColor: theme.colors.primary,
57
+ opacity,
58
+ }}
59
+ />
60
+ <Text color={theme.colors.text.hint} typography={'body_default_regular'}>
61
+ -
62
+ </Text>
63
+ </View>
64
+ );
65
+ };
66
+
67
+ const InputOTP = forwardRef(
68
+ (
69
+ {
70
+ length,
71
+ errorMessage,
72
+ errorSpacing,
73
+ floatingValue = 'Label',
74
+ floatingIconColor,
75
+ floatingIcon,
76
+ placeholder,
77
+ onChangeText,
78
+ onFocus,
79
+ onBlur,
80
+ dataType = 'number',
81
+ params,
82
+ style,
83
+ accessibilityLabel,
84
+ hintText,
85
+ ...props
86
+ }: InputOTPProps,
87
+ ref,
88
+ ) => {
89
+ const MAX_LENGTH = 10;
90
+ const [value, setValue] = useState('');
91
+ const [focused, setFocused] = useState(false);
92
+ const inputRef = useRef<TextInput | null>(null);
93
+ const {theme} = useContext(ApplicationContext);
94
+
95
+ useImperativeHandle(ref, () => ({
96
+ onChangeText: (text: string) => {
97
+ _onChangeText(text);
98
+ },
99
+ focus: () => inputRef.current?.focus(),
100
+ blur: () => inputRef.current?.blur(),
101
+ setText: (text: string) => _onChangeText(text),
102
+ }));
103
+
104
+ const onFocusInput = () => {
105
+ setFocused(true);
106
+ inputRef.current?.focus();
107
+ };
108
+
109
+ const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
110
+ setFocused(false);
111
+ onBlur?.(e);
112
+ };
113
+
114
+ const _onChangeText = (text: string) => {
115
+ if (text.length > MAX_LENGTH) return;
116
+ if (dataType === 'number' && isNaN(Number(text))) return;
117
+ if (
118
+ !isNaN(Number(text)) &&
119
+ value.length >= Number(length) &&
120
+ text.length >= value.length
121
+ )
122
+ return;
123
+
124
+ setValue(text);
125
+ onChangeText?.(text);
126
+ };
127
+
128
+ const renderInputs = (length: number) => {
129
+ const TextInputs: React.ReactNode[] = [];
130
+ for (let i = 0; i < length; i++) {
131
+ TextInputs.push(
132
+ <View>
133
+ {focused && value.length === i ? (
134
+ <OTPCaret index={i} length={length} />
135
+ ) : (
136
+ <Text
137
+ style={i !== length - 1 && {marginRight: Spacing.L}}
138
+ color={
139
+ !!value[i]
140
+ ? theme.colors.text.default
141
+ : theme.colors.text.hint
142
+ }
143
+ typography={
144
+ !!value[i] ? 'header_m_bold' : 'body_default_regular'
145
+ }>
146
+ {value[i] || '-'}
147
+ </Text>
148
+ )}
149
+ </View>,
150
+ );
151
+ }
152
+ return TextInputs;
153
+ };
154
+
155
+ const renderUnidentifiedInputs = () => {
156
+ const splitValue = value.split('');
157
+
158
+ if (!focused && value.length === 0 && !!placeholder) {
159
+ return (
160
+ <Text
161
+ color={theme.colors.text.hint}
162
+ typography={'description_default_regular'}>
163
+ {placeholder}
164
+ </Text>
165
+ );
166
+ }
167
+
168
+ return (
169
+ <View style={{flexDirection: 'row'}}>
170
+ {splitValue.map((letter, index) => {
171
+ return (
172
+ <Text
173
+ style={
174
+ (index !== value.length - 1 ||
175
+ (focused && value.length !== MAX_LENGTH)) && {
176
+ marginRight: Spacing.L,
177
+ }
178
+ }
179
+ typography={'header_m_bold'}>
180
+ {letter}
181
+ </Text>
182
+ );
183
+ })}
184
+ {focused && value.length !== MAX_LENGTH && <OTPCaret />}
185
+ </View>
186
+ );
187
+ };
188
+
189
+ const renderInputView = () => {
190
+ return (
191
+ <View
192
+ style={[
193
+ styles.otpInput,
194
+ {backgroundColor: theme.colors.background.surface},
195
+ getBorderColor(theme, focused, errorMessage),
196
+ ]}>
197
+ {!!floatingValue && (
198
+ <View
199
+ style={[
200
+ styles.otpFloatingView,
201
+ {backgroundColor: theme.colors.background.surface},
202
+ ]}>
203
+ <Text
204
+ typography={'description_default_regular'}
205
+ color={theme.colors.text.hint}>
206
+ {floatingValue}
207
+ </Text>
208
+ </View>
209
+ )}
210
+ <View style={styles.otpInputsView}>
211
+ {length ? renderInputs(length) : renderUnidentifiedInputs()}
212
+ </View>
213
+ <View style={styles.inputView}>
214
+ <TextInput
215
+ {...props}
216
+ accessibilityLabel={accessibilityLabel}
217
+ ref={inputRef}
218
+ value={value}
219
+ onChangeText={_onChangeText}
220
+ keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
221
+ onFocus={onFocusInput}
222
+ onBlur={onBlurInput}
223
+ style={{opacity: 0}}
224
+ selectionColor={theme.colors.primary}
225
+ placeholderTextColor={theme.colors.text.hint}
226
+ />
227
+ </View>
228
+ </View>
229
+ );
230
+ };
231
+
232
+ return (
233
+ <ComponentContext.Provider
234
+ value={{
235
+ componentName: 'InputOTP',
236
+ params,
237
+ componentLabel: floatingValue,
238
+ state: 'enabled',
239
+ componentId: accessibilityLabel,
240
+ }}>
241
+ <TouchableOpacity
242
+ activeOpacity={1}
243
+ onPress={onFocusInput}
244
+ style={[style, styles.otpWrapper]}>
245
+ {renderInputView()}
246
+ <ErrorView
247
+ errorMessage={errorMessage}
248
+ errorSpacing={errorSpacing}
249
+ hintText={hintText}
250
+ />
251
+ </TouchableOpacity>
252
+ </ComponentContext.Provider>
253
+ );
254
+ },
255
+ );
256
+
257
+ export default InputOTP;