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