@momo-kits/foundation 0.75.1-beta.9 → 0.75.2-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 (112) hide show
  1. package/Application/BottomTab/BottomTabBar.tsx +478 -0
  2. package/Application/BottomTab/index.tsx +134 -0
  3. package/Application/Components.tsx +552 -0
  4. package/Application/ModalScreen.tsx +244 -0
  5. package/Application/Navigation.ts +50 -0
  6. package/Application/NavigationContainer.tsx +128 -0
  7. package/Application/Navigator.ts +151 -0
  8. package/Application/StackScreen.tsx +146 -0
  9. package/Application/index.ts +37 -0
  10. package/{Navigation → Application}/types.ts +96 -27
  11. package/Application/utils.tsx +175 -0
  12. package/Assets/DotAnimation.json +256 -0
  13. package/Assets/SpinnerAnimation.json +1027 -0
  14. package/Assets/icon.json +89 -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 +90 -39
  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 +5 -1
  32. package/Divider/index.tsx +3 -1
  33. package/Icon/index.tsx +14 -10
  34. package/Icon/types.ts +4 -3
  35. package/IconButton/index.tsx +24 -15
  36. package/Image/index.tsx +63 -45
  37. package/Image/styles.ts +1 -9
  38. package/Image/types.ts +5 -1
  39. package/Input/Input.tsx +227 -115
  40. package/Input/InputDropDown.tsx +132 -0
  41. package/Input/InputMoney.tsx +174 -110
  42. package/Input/InputOTP.tsx +257 -0
  43. package/Input/InputSearch.tsx +160 -130
  44. package/Input/InputTextArea.tsx +149 -121
  45. package/Input/common.tsx +43 -18
  46. package/Input/index.tsx +205 -17
  47. package/Input/styles.ts +44 -7
  48. package/Input/utils.ts +2 -2
  49. package/Layout/Card.tsx +76 -37
  50. package/Layout/FloatingButton.tsx +128 -0
  51. package/Layout/GridSystem.tsx +35 -23
  52. package/Layout/Item.tsx +29 -33
  53. package/Layout/ItemList.tsx +55 -0
  54. package/Layout/ItemSectionList.tsx +39 -0
  55. package/Layout/Screen.tsx +378 -0
  56. package/Layout/Section.tsx +73 -35
  57. package/Layout/index.ts +13 -3
  58. package/Layout/styles.ts +101 -0
  59. package/Layout/types.ts +1 -37
  60. package/Layout/utils.ts +38 -43
  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/Dot.tsx +1 -1
  70. package/Pagination/PaginationDot.tsx +8 -3
  71. package/Pagination/PaginationNumber.tsx +8 -7
  72. package/Pagination/PaginationScroll.tsx +1 -1
  73. package/Pagination/PaginationWhiteDot.tsx +8 -5
  74. package/Pagination/styles.ts +3 -0
  75. package/Pagination/types.ts +2 -0
  76. package/Popup/PopupNotify.tsx +102 -44
  77. package/Popup/PopupPromotion.tsx +63 -22
  78. package/Popup/types.ts +15 -4
  79. package/Radio/index.tsx +35 -21
  80. package/Radio/types.ts +7 -5
  81. package/Skeleton/index.tsx +1 -1
  82. package/Skeleton/types.ts +3 -0
  83. package/Switch/index.tsx +35 -24
  84. package/Switch/types.ts +5 -3
  85. package/Tag/index.tsx +1 -1
  86. package/Text/index.tsx +32 -26
  87. package/Text/styles.ts +38 -0
  88. package/Text/types.ts +25 -13
  89. package/Text/utils.ts +14 -0
  90. package/Title/index.tsx +186 -0
  91. package/Title/styles.ts +79 -0
  92. package/Title/types.ts +20 -0
  93. package/code-scanner.js +143 -0
  94. package/index.ts +17 -3
  95. package/migrate_typo.js +56 -0
  96. package/package.json +44 -39
  97. package/publish.sh +2 -5
  98. package/tsconfig.json +24 -0
  99. package/verify.js +47 -0
  100. package/Assets/header-background.png +0 -0
  101. package/Layout/ScreenContainer.tsx +0 -121
  102. package/Navigation/BottomTab.tsx +0 -39
  103. package/Navigation/Components.tsx +0 -365
  104. package/Navigation/ModalScreen.tsx +0 -226
  105. package/Navigation/Navigation.ts +0 -44
  106. package/Navigation/NavigationButton.tsx +0 -54
  107. package/Navigation/NavigationContainer.tsx +0 -77
  108. package/Navigation/Navigator.ts +0 -82
  109. package/Navigation/Observer.ts +0 -79
  110. package/Navigation/StackScreen.tsx +0 -27
  111. package/Navigation/index.ts +0 -14
  112. package/Navigation/utils.tsx +0 -137
@@ -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,127 +12,185 @@ import {
6
12
  TouchableOpacity,
7
13
  View,
8
14
  } from 'react-native';
9
- import {ApplicationContext} from '../Navigation';
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
+ changeText: (text: string) => _onChangeText(text),
80
+ setText: _onChangeText,
81
+ };
82
+ });
83
+
84
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
85
+ setFocused(true);
86
+ onFocus?.(e);
87
+ };
88
+
89
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
90
+ setFocused(false);
91
+ onBlur?.(e);
92
+ };
93
+
94
+ const renderInputView = () => {
95
+ const disabledColor = theme.colors.text.disable;
96
+ let textColor = theme.colors.text.default;
97
+ let placeholderColor = theme.colors.text.hint;
98
+ let iconTintColor = iconColor;
99
+
100
+ if (disabled) {
101
+ textColor = disabledColor;
102
+ placeholderColor = disabledColor;
103
+ iconTintColor = disabledColor;
104
+ }
105
+
106
+ return (
107
+ <View
108
+ style={[
109
+ styles.inputWrapper,
110
+ {backgroundColor: theme.colors.background.surface},
111
+ getSizeStyle(size),
112
+ getBorderColor(theme, focused, errorMessage, disabled),
113
+ ]}>
114
+ <FloatingView
115
+ floatingValue={floatingValue}
116
+ floatingIconColor={floatingIconColor}
117
+ disabled={disabled}
118
+ required={required}
119
+ floatingIcon={floatingIcon}
120
+ />
121
+ <View style={styles.inputView}>
122
+ <TextInput
123
+ {...props}
124
+ accessibilityLabel={accessibilityLabel}
125
+ editable={!disabled}
126
+ ref={inputRef}
127
+ keyboardType={'number-pad'}
128
+ style={[
129
+ styles.moneyInput,
130
+ {
131
+ color: textColor,
132
+ },
133
+ ]}
134
+ value={value}
135
+ onChangeText={_onChangeText}
136
+ onFocus={_onFocus}
137
+ onBlur={_onBlur}
138
+ selectionColor={theme.colors.primary}
139
+ placeholderTextColor={placeholderColor}
140
+ placeholder={'0đ'}
141
+ />
142
+ </View>
143
+ <View style={styles.iconView}>
144
+ {focused && (
145
+ <TouchableOpacity
146
+ style={styles.iconWrapper}
147
+ onPress={onClearText}>
148
+ <Icon
149
+ source="24_navigation_close_circle_full"
150
+ size={16}
151
+ color={theme.colors.text.hint}
152
+ />
153
+ </TouchableOpacity>
154
+ )}
155
+ {!!icon && <Icon color={iconTintColor} source={icon} />}
156
+ </View>
157
+ </View>
158
+ );
159
+ };
160
+
161
+ let inputState = 'active';
162
+
163
+ if (value && value?.length > 0) {
164
+ inputState = 'filled';
165
+ }
166
+ if (errorMessage && errorMessage?.length > 0) {
167
+ inputState = 'error';
43
168
  }
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
169
 
65
170
  if (disabled) {
66
- textColor = disabledColor;
67
- placeholderColor = disabledColor;
68
- iconTintColor = disabledColor;
171
+ inputState = 'disabled';
69
172
  }
70
173
 
71
174
  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
- editable={!disabled}
90
- textAlignVertical="top"
91
- ref={inputRef}
92
- keyboardType={'number-pad'}
93
- style={[
94
- styles.moneyInput,
95
- {
96
- color: textColor,
97
- },
98
- ]}
99
- value={value}
100
- onChangeText={_onChangeText}
101
- onFocus={_onFocus}
102
- onBlur={_onBlur}
103
- selectionColor={theme.colors.primary}
104
- placeholderTextColor={placeholderColor}
105
- placeholder={'0đ'}
175
+ <ComponentContext.Provider
176
+ value={{
177
+ componentName: 'InputMoney',
178
+ params,
179
+ componentLabel: floatingValue,
180
+ state: inputState,
181
+ componentId: accessibilityLabel,
182
+ }}>
183
+ <View style={[style, styles.wrapper]}>
184
+ {renderInputView()}
185
+ <ErrorView
186
+ errorMessage={errorMessage}
187
+ errorSpacing={errorSpacing}
188
+ hintText={hintText}
106
189
  />
107
190
  </View>
108
- <View style={styles.iconView}>
109
- {focused && (
110
- <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
111
- <Icon
112
- source="24_navigation_close_circle_full"
113
- size={16}
114
- color={theme.colors.text.hint}
115
- />
116
- </TouchableOpacity>
117
- )}
118
- {!!icon && <Icon color={iconTintColor} source={icon} />}
119
- </View>
120
- </View>
191
+ </ComponentContext.Provider>
121
192
  );
122
- };
123
-
124
- return (
125
- <View style={styles.wrapper} accessibilityLabel={`InputMoney`}>
126
- {renderInputView()}
127
- <ErrorView errorMessage={errorMessage} />
128
- </View>
129
- );
130
- };
193
+ },
194
+ );
131
195
 
132
196
  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;