@momo-kits/foundation 0.169.6 → 0.169.7-beta.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 (143) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +34 -44
  3. package/Application/BottomSheet.tsx +37 -40
  4. package/Application/BottomTab/ActiveIndicator.tsx +14 -15
  5. package/Application/BottomTab/BottomTabBar.tsx +62 -49
  6. package/Application/BottomTab/BottomTabInset.tsx +4 -8
  7. package/Application/BottomTab/CurvedContainer.tsx +12 -14
  8. package/Application/BottomTab/CustomBottomTabItem.tsx +13 -13
  9. package/Application/BottomTab/DuoBarCompression.tsx +19 -13
  10. package/Application/BottomTab/DuoRailTabBar.tsx +24 -20
  11. package/Application/BottomTab/DuoTabBar.tsx +10 -8
  12. package/Application/BottomTab/TabBarIcon.tsx +29 -15
  13. package/Application/BottomTab/TabScreen.tsx +5 -1
  14. package/Application/BottomTab/index.tsx +30 -33
  15. package/Application/BottomTab/metrics.ts +23 -23
  16. package/Application/BottomTab/tabItems.ts +1 -1
  17. package/Application/BottomTab/usePressLift.ts +2 -2
  18. package/Application/Components/BackgroundImageView.tsx +0 -4
  19. package/Application/Components/DuoGlassBackground.tsx +4 -5
  20. package/Application/Components/HeaderAnimated.tsx +0 -4
  21. package/Application/Components/HeaderBackground.tsx +0 -4
  22. package/Application/Components/HeaderExtendHeader.tsx +10 -11
  23. package/Application/Components/HeaderLeft.tsx +1 -2
  24. package/Application/Components/HeaderRight.tsx +1 -2
  25. package/Application/Components/HeaderTitle.shared.ts +9 -11
  26. package/Application/Components/HeaderTitle.tsx +3 -4
  27. package/Application/Components/HeaderToolkitAction.tsx +20 -17
  28. package/Application/Components/NavigationButton.tsx +1 -2
  29. package/Application/Components/SearchHeader.tsx +10 -17
  30. package/Application/Components/TitleJourney.tsx +2 -2
  31. package/Application/Components/TitleLocation.tsx +2 -2
  32. package/Application/Components/TitleUser.tsx +2 -2
  33. package/Application/Components/useToolkitActions.tsx +32 -17
  34. package/Application/KitAdoption/AdoptionBadge.tsx +119 -0
  35. package/Application/KitAdoption/AdoptionProbe.tsx +166 -0
  36. package/Application/KitAdoption/AdoptionStore.ts +80 -0
  37. package/Application/KitAdoption/Authorship/utils.ts +137 -0
  38. package/Application/KitAdoption/Slots/utils.ts +68 -0
  39. package/Application/KitAdoption/constants.ts +120 -0
  40. package/Application/KitAdoption/index.ts +12 -0
  41. package/Application/KitAdoption/styles.ts +55 -0
  42. package/Application/KitAdoption/types.ts +61 -0
  43. package/Application/KitAdoption/utils.ts +376 -0
  44. package/Application/Localize.ts +0 -7
  45. package/Application/ModalScreen.tsx +10 -10
  46. package/Application/Navigation.ts +16 -14
  47. package/Application/NavigationContainer.tsx +18 -19
  48. package/Application/Navigator.ts +48 -20
  49. package/Application/ScaleSizeProvider.tsx +6 -13
  50. package/Application/StackScreen.tracking.ts +33 -11
  51. package/Application/StackScreen.tsx +54 -10
  52. package/Application/TooltipPortal.tsx +5 -12
  53. package/Application/WidgetContainer.tsx +4 -10
  54. package/Application/__tests__/KitAdoption.integration.test.tsx +253 -0
  55. package/Application/__tests__/KitAdoption.test.ts +283 -0
  56. package/Application/__tests__/KitAdoptionAuthorship.test.ts +132 -0
  57. package/Application/__tests__/KitAdoptionStore.test.ts +110 -0
  58. package/Application/__tests__/KitAdoptionStructural.test.ts +193 -0
  59. package/Application/__tests__/modalLayout.test.ts +128 -0
  60. package/Application/modalLayout.ts +14 -13
  61. package/Application/navigationOptions.tsx +17 -21
  62. package/Badge/Badge.tsx +0 -5
  63. package/Badge/BadgeDot.tsx +0 -1
  64. package/Badge/BadgeDotAnimation.tsx +7 -15
  65. package/Badge/BadgeRibbon.tsx +38 -46
  66. package/Badge/Shape.tsx +0 -2
  67. package/Badge/types.ts +2 -2
  68. package/Button/index.tsx +16 -5
  69. package/Button/utils.ts +15 -9
  70. package/CheckBox/index.tsx +0 -4
  71. package/Consts/theme.ts +10 -10
  72. package/Context/index.ts +1 -5
  73. package/Divider/DashDivider.tsx +0 -1
  74. package/Divider/index.tsx +0 -4
  75. package/FoundationList/index.tsx +0 -4
  76. package/Icon/index.tsx +6 -6
  77. package/Icon/types.ts +1 -1
  78. package/IconButton/index.tsx +12 -4
  79. package/Image/index.tsx +7 -9
  80. package/Input/Input.tsx +9 -8
  81. package/Input/InputDropDown.tsx +12 -10
  82. package/Input/InputMoney.tsx +8 -12
  83. package/Input/InputMoneyView.tsx +7 -14
  84. package/Input/InputOTP.tsx +33 -35
  85. package/Input/InputPhoneNumber.tsx +9 -8
  86. package/Input/InputSearch.tsx +6 -4
  87. package/Input/InputTextArea.tsx +0 -4
  88. package/Input/SearchTextTyping.tsx +0 -4
  89. package/Input/SystemTextInput.tsx +2 -7
  90. package/Input/TextTyping.tsx +0 -4
  91. package/Input/common.tsx +12 -26
  92. package/Input/index.tsx +20 -17
  93. package/Input/moneyUtils.ts +4 -10
  94. package/Input/styles.ts +5 -0
  95. package/Input/utils.ts +12 -14
  96. package/Layout/Card.tsx +13 -16
  97. package/Layout/FloatingButton.tsx +0 -4
  98. package/Layout/Item.tsx +2 -6
  99. package/Layout/ItemList.tsx +7 -9
  100. package/Layout/ItemSectionList.tsx +1 -2
  101. package/Layout/ReservedRegions.tsx +25 -25
  102. package/Layout/Screen.tsx +58 -56
  103. package/Layout/Section.tsx +13 -17
  104. package/Layout/TrackingScope.tsx +0 -1
  105. package/Layout/constants.ts +19 -0
  106. package/Layout/screenContent.tsx +2 -4
  107. package/Layout/screenFooter.tsx +18 -28
  108. package/Layout/screenHeader.tsx +31 -34
  109. package/Layout/screenQuickReturn.tsx +20 -17
  110. package/Layout/screenScroll.ts +7 -4
  111. package/Layout/screenTypes.ts +2 -3
  112. package/Layout/types.ts +41 -3
  113. package/Layout/useGridSystem.ts +49 -0
  114. package/Layout/utils.ts +37 -38
  115. package/Loader/DotLoader.tsx +0 -4
  116. package/Loader/Loader.tsx +0 -4
  117. package/Loader/ProgressBar.tsx +0 -1
  118. package/Loader/Spinner.tsx +0 -4
  119. package/Loader/utils.ts +7 -11
  120. package/Pagination/Dot.tsx +0 -4
  121. package/Pagination/PaginationDot.tsx +0 -1
  122. package/Pagination/PaginationNumber.tsx +3 -5
  123. package/Pagination/PaginationScroll.tsx +0 -5
  124. package/Pagination/PaginationWhiteDot.tsx +0 -1
  125. package/Pagination/index.tsx +0 -4
  126. package/Pagination/types.ts +2 -2
  127. package/Popup/PopupNotify.tsx +48 -44
  128. package/Popup/PopupPromotion.tsx +7 -4
  129. package/Popup/types.ts +4 -4
  130. package/Popup/utils.ts +0 -4
  131. package/Radio/index.tsx +0 -4
  132. package/Skeleton/index.tsx +0 -8
  133. package/Switch/index.tsx +0 -1
  134. package/Tag/index.tsx +1 -5
  135. package/Tag/types.ts +3 -3
  136. package/Text/index.tsx +3 -14
  137. package/Text/styles.ts +1 -1
  138. package/Text/types.ts +6 -1
  139. package/Text/utils.ts +4 -20
  140. package/Title/index.tsx +32 -41
  141. package/ViewBoundary/index.tsx +2 -4
  142. package/index.ts +6 -0
  143. package/package.json +1 -1
@@ -52,10 +52,6 @@ type InputMoneyViewProps = Pick<
52
52
  inputProps: TextInputProps;
53
53
  };
54
54
 
55
- /**
56
- * Presentational body of InputMoney: bordered field, currency suffix (kept
57
- * after a placeholder that ends with it) and the clear/trailing icons.
58
- */
59
55
  export const InputMoneyView = ({
60
56
  size,
61
57
  errorMessage,
@@ -102,12 +98,9 @@ export const InputMoneyView = ({
102
98
  fontFamily: inputFontFamily,
103
99
  };
104
100
  const placeholderHasCurrency = placeholder.endsWith(currency);
105
- const shouldShowCurrency = !!displayValue || placeholderHasCurrency;
106
- const shouldShowClearIcon = !!(showClearIcon && focused && displayValue);
107
- let inputPlaceholder = placeholder;
108
- if (placeholderHasCurrency) {
109
- inputPlaceholder = placeholder.slice(0, -currency.length);
110
- }
101
+ const inputPlaceholder = placeholderHasCurrency
102
+ ? placeholder.slice(0, -currency.length)
103
+ : placeholder;
111
104
  return (
112
105
  <View
113
106
  style={[
@@ -154,7 +147,7 @@ export const InputMoneyView = ({
154
147
  keyboardType={'numeric'}
155
148
  maxLength={undefined}
156
149
  />
157
- {shouldShowCurrency && (
150
+ {displayValue || placeholderHasCurrency ? (
158
151
  <Text
159
152
  style={[
160
153
  textStyle,
@@ -164,10 +157,10 @@ export const InputMoneyView = ({
164
157
  >
165
158
  {currency}
166
159
  </Text>
167
- )}
160
+ ) : null}
168
161
  </Pressable>
169
162
  <View style={styles.iconView}>
170
- {shouldShowClearIcon && (
163
+ {showClearIcon && focused && displayValue ? (
171
164
  <TouchableOpacity
172
165
  accessibilityLabel={`${componentId}|clear-icon-touch`}
173
166
  style={styles.iconWrapper}
@@ -180,7 +173,7 @@ export const InputMoneyView = ({
180
173
  accessibilityLabel={`${componentId}|clear-icon`}
181
174
  />
182
175
  </TouchableOpacity>
183
- )}
176
+ ) : null}
184
177
  <RenderTrailing
185
178
  color={iconTintColor}
186
179
  icon={trailing}
@@ -38,7 +38,6 @@ import {
38
38
  MiniAppContext,
39
39
  } from '../Context';
40
40
 
41
- /** Blinking caret shown in the OTP slot being typed. */
42
41
  const OTPCaret: FC<CaretProps> = ({ index, length }) => {
43
42
  const DURATION = 300;
44
43
  const { theme } = useContext(ApplicationContext);
@@ -61,7 +60,9 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
61
60
  };
62
61
  }, [opacity]);
63
62
 
64
- const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
63
+ const animatedStyle = useAnimatedStyle(() => ({
64
+ opacity: opacity.value,
65
+ }));
65
66
 
66
67
  const hasNextInput = !isNaN(Number(length)) && index !== Number(length) - 1;
67
68
  const spacingStyle = hasNextInput && { marginRight: Spacing.L };
@@ -85,10 +86,6 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
85
86
  );
86
87
  };
87
88
 
88
- /**
89
- * OTP input: a hidden TextInput drives `length` slots (or a free-length row
90
- * when `length` is unset) with a blinking caret; accepts at most 10 chars.
91
- */
92
89
  const InputOTP = forwardRef(
93
90
  (
94
91
  {
@@ -124,16 +121,14 @@ const InputOTP = forwardRef(
124
121
  props.accessibilityLabel,
125
122
  );
126
123
 
127
- useImperativeHandle(ref, () => {
128
- return {
129
- onChangeText: (text: string) => {
130
- _onChangeText(text);
131
- },
132
- focus: () => inputRef.current?.focus(),
133
- blur: () => inputRef.current?.blur(),
134
- setText: (text: string) => _onChangeText(text),
135
- };
136
- });
124
+ useImperativeHandle(ref, () => ({
125
+ onChangeText: (text: string) => {
126
+ _onChangeText(text);
127
+ },
128
+ focus: () => inputRef.current?.focus(),
129
+ blur: () => inputRef.current?.blur(),
130
+ setText: (text: string) => _onChangeText(text),
131
+ }));
137
132
 
138
133
  const onFocusInput = (e: TextInputFocusEvent) => {
139
134
  setFocused(true);
@@ -147,10 +142,13 @@ const InputOTP = forwardRef(
147
142
 
148
143
  const _onChangeText = (text: string) => {
149
144
  if (text.length > MAX_LENGTH) return;
150
- const isNumeric = !isNaN(Number(text));
151
- if (dataType === 'number' && !isNumeric) return;
152
- const isFull = value.length >= Number(length);
153
- if (isNumeric && isFull && text.length >= value.length) return;
145
+ if (dataType === 'number' && isNaN(Number(text))) return;
146
+ if (
147
+ !isNaN(Number(text)) &&
148
+ value.length >= Number(length) &&
149
+ text.length >= value.length
150
+ )
151
+ return;
154
152
 
155
153
  setValue(text);
156
154
  onChangeText?.(text);
@@ -166,21 +164,21 @@ const InputOTP = forwardRef(
166
164
  const TextInputs: React.ReactNode[] = [];
167
165
  const textColors = theme.colors.text;
168
166
  for (let i = 0; i < inputLength; i++) {
169
- let slot: React.ReactNode;
170
- if (focused && value.length === i) {
171
- slot = <OTPCaret index={i} length={inputLength} />;
172
- } else {
173
- slot = (
174
- <Text
175
- style={i !== inputLength - 1 && { marginRight: Spacing.L }}
176
- color={value[i] ? textColors.default : textColors.hint}
177
- typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
178
- >
179
- {value[i] || '-'}
180
- </Text>
181
- );
182
- }
183
- TextInputs.push(<View key={`otp-input-${i}`}>{slot}</View>);
167
+ TextInputs.push(
168
+ <View key={`otp-input-${i}`}>
169
+ {focused && value.length === i ? (
170
+ <OTPCaret index={i} length={inputLength} />
171
+ ) : (
172
+ <Text
173
+ style={i !== inputLength - 1 && { marginRight: Spacing.L }}
174
+ color={value[i] ? textColors.default : textColors.hint}
175
+ typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
176
+ >
177
+ {value[i] || '-'}
178
+ </Text>
179
+ )}
180
+ </View>,
181
+ );
184
182
  }
185
183
  return TextInputs;
186
184
  };
@@ -34,8 +34,7 @@ import { Image } from '../Image';
34
34
  import { Typography } from '../Text/types';
35
35
 
36
36
  /**
37
- * Vietnamese phone number input with a fixed flag and `+84` prefix, number pad
38
- * by default, plus clear and trailing icons.
37
+ * Input default component
39
38
  */
40
39
  const InputPhoneNumber = forwardRef(
41
40
  (
@@ -147,6 +146,9 @@ const InputPhoneNumber = forwardRef(
147
146
  typography = 'header_m_bold';
148
147
  }
149
148
 
149
+ /**
150
+ * Render input view
151
+ */
150
152
  const renderInputView = () => {
151
153
  const disabledColor = theme.colors.text.disable;
152
154
  const secure = secureTextEntry;
@@ -154,11 +156,6 @@ const InputPhoneNumber = forwardRef(
154
156
  let placeholderColor = theme.colors.text.hint;
155
157
  let iconTintColor = trailingColor ?? theme.colors.text.default;
156
158
  const borderWidth = focused ? 1.5 : 1;
157
- let secureTextStyle = styles.secureTextLarge;
158
-
159
- if (size === 'small') {
160
- secureTextStyle = styles.secureTextSmall;
161
- }
162
159
 
163
160
  if (disabled) {
164
161
  textColor = disabledColor;
@@ -217,7 +214,11 @@ const InputPhoneNumber = forwardRef(
217
214
  color: textColor,
218
215
  },
219
216
  inputTextStyles,
220
- secure && haveValue && secureTextStyle,
217
+ secure &&
218
+ haveValue &&
219
+ (size === 'small'
220
+ ? styles.secureTextSmall
221
+ : styles.secureTextLarge),
221
222
  ]}
222
223
  defaultValue={defaultValue}
223
224
  allowFontScaling={false}
@@ -26,10 +26,6 @@ import styles from './styles';
26
26
  import { Styles } from '../Consts';
27
27
  import SystemTextInput from './SystemTextInput';
28
28
 
29
- /**
30
- * Search field with search icon, clear icon, optional trailing icon and a
31
- * cancel button; with `typingData` it shows an animated placeholder instead.
32
- */
33
29
  const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
34
30
  {
35
31
  placeholder,
@@ -110,6 +106,9 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
110
106
  };
111
107
  });
112
108
 
109
+ /**
110
+ * Render the input view
111
+ */
113
112
  const renderInputView = () => {
114
113
  if (typingData && typingData?.length > 0) {
115
114
  return (
@@ -150,6 +149,9 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
150
149
  );
151
150
  };
152
151
 
152
+ /**
153
+ * Render trailing icon or text
154
+ */
153
155
  const renderTrailing = () => {
154
156
  return (
155
157
  <View style={Styles.row}>
@@ -21,10 +21,6 @@ import styles from './styles';
21
21
  import SystemTextInput from './SystemTextInput';
22
22
  import { setAutomationID, useComponentId } from '../Application';
23
23
 
24
- /**
25
- * Multiline input with floating label, clear icon and a `length/maxLength`
26
- * counter; the ref exposes clear, focus, blur and setText.
27
- */
28
24
  const InputTextArea = forwardRef(
29
25
  (
30
26
  {
@@ -4,10 +4,6 @@ import { TextInput } from 'react-native';
4
4
  import { useScaleSize, exportFontFamily } from '../Text';
5
5
  import styles from './styles';
6
6
 
7
- /**
8
- * Animated search placeholder: types each string of `data` char by char, then
9
- * erases it, looping forever. Writes via setNativeProps to avoid re-renders.
10
- */
11
7
  export const TextTyping: FC<any> = ({
12
8
  data = [],
13
9
  timeDelayEnd = 2000,
@@ -11,16 +11,11 @@ interface SystemTextInputProps extends TextInputProps {
11
11
  ref?: React.RefObject<TextInput | null>;
12
12
  }
13
13
 
14
- /**
15
- * TextInput that still fires onPressIn/onPressOut when not editable on
16
- * Android, by moving them to a wrapping touchable.
17
- */
18
14
  const SystemTextInput: FC<SystemTextInputProps> = props => {
19
15
  const { editable, onPressIn, onPressOut, ref } = props;
20
16
 
21
- const isAndroidReadOnly = Platform.OS === 'android' && !editable;
22
- const hasPressHandler = !!(onPressIn || onPressOut);
23
- const needsAndroidWrapper = isAndroidReadOnly && hasPressHandler;
17
+ const needsAndroidWrapper =
18
+ Platform.OS === 'android' && !editable && (onPressIn || onPressOut);
24
19
 
25
20
  const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
26
21
  const inputProps: TextInputProps = {
@@ -11,10 +11,6 @@ export interface TextTypingProps {
11
11
  inputStyle?: TextStyle;
12
12
  }
13
13
 
14
- /**
15
- * Typing placeholder: types each string of `data` char by char, then erases
16
- * it, looping forever. Writes via setNativeProps to avoid re-renders.
17
- */
18
14
  const TextTyping: FC<TextTypingProps> = ({
19
15
  data = [],
20
16
  timeDelayEnd = 2000,
package/Input/common.tsx CHANGED
@@ -35,7 +35,6 @@ type TrailingProps = {
35
35
 
36
36
  export const MAX_LENGTH = 300;
37
37
 
38
- /** Border color by state; priority is disabled > error > focused > default. */
39
38
  export const getBorderColor = (
40
39
  theme: Theme,
41
40
  focused: boolean,
@@ -59,10 +58,6 @@ export const getBorderColor = (
59
58
  return { borderColor };
60
59
  };
61
60
 
62
- /**
63
- * Container style for `size`; multiline uses `height` as minHeight so the
64
- * field can grow with its content.
65
- */
66
61
  export const getSizeStyle = (
67
62
  size?: 'small' | 'large',
68
63
  multiline: boolean = false,
@@ -73,14 +68,13 @@ export const getSizeStyle = (
73
68
  scaleStyle = { height };
74
69
  }
75
70
 
76
- if (multiline) {
71
+ if (multiline)
77
72
  return [
78
73
  styles.multilineContainer,
79
74
  {
80
75
  minHeight: height,
81
76
  },
82
77
  ];
83
- }
84
78
 
85
79
  if (size === 'small') {
86
80
  return [styles.smallContainer, scaleStyle];
@@ -88,10 +82,6 @@ export const getSizeStyle = (
88
82
  return [styles.container, scaleStyle];
89
83
  };
90
84
 
91
- /**
92
- * Error or hint row under an input (error wins); with neither, `errorSpacing`
93
- * keeps an empty row so the layout doesn't jump when an error appears.
94
- */
95
85
  export const ErrorView: FC<{
96
86
  errorMessage?: string;
97
87
  errorSpacing?: boolean;
@@ -105,17 +95,21 @@ export const ErrorView: FC<{
105
95
  const scaledIconSize = useScaleSize(16);
106
96
 
107
97
  if (errorMessage || hintText) {
108
- const messageColor = errorMessage ? errorColor : hintColor;
109
- const messageType = errorMessage ? 'error' : 'hint';
110
98
  return (
111
99
  <View style={styles.errorView}>
112
100
  <View style={styles.errorIcon}>
113
- <Icon color={messageColor} source="ic_error" size={scaledIconSize} />
101
+ <Icon
102
+ color={errorMessage ? errorColor : hintColor}
103
+ source="ic_error"
104
+ size={scaledIconSize}
105
+ />
114
106
  </View>
115
107
  <Text
116
- accessibilityLabel={`${componentId}|${messageType}-text`}
108
+ accessibilityLabel={`${componentId}|${
109
+ errorMessage ? 'error' : 'hint'
110
+ }-text`}
117
111
  style={Styles.flex}
118
- color={messageColor}
112
+ color={errorMessage ? errorColor : hintColor}
119
113
  typography={'description_default_regular'}
120
114
  >
121
115
  {errorMessage ?? hintTextDefault}
@@ -129,10 +123,6 @@ export const ErrorView: FC<{
129
123
  return <View />;
130
124
  };
131
125
 
132
- /**
133
- * Floating label on the input's top border with a required `*` and an
134
- * optional pressable icon; renders nothing without `floatingValue`.
135
- */
136
126
  export const FloatingView: FC<FloatingViewProps> = ({
137
127
  floatingValue,
138
128
  floatingIconColor,
@@ -201,10 +191,6 @@ export const FloatingView: FC<FloatingViewProps> = ({
201
191
  return null;
202
192
  };
203
193
 
204
- /**
205
- * Trailing slot: spinner while `loading`, an icon when `icon` is a known icon
206
- * name or URL, otherwise `icon` as a text action (first 15 chars).
207
- */
208
194
  export const RenderTrailing: FC<TrailingProps> = ({
209
195
  loading,
210
196
  color,
@@ -218,8 +204,8 @@ export const RenderTrailing: FC<TrailingProps> = ({
218
204
  }
219
205
 
220
206
  if (icon) {
221
- const isIconName = !!(IconSources as any)?.[icon]?.uri;
222
- const isIcon = isIconName || icon.includes('http');
207
+ const isIcon =
208
+ (IconSources as any)?.[icon as string]?.uri || icon?.includes('http');
223
209
  const renderIconTouchable = (content: ReactNode) => {
224
210
  return (
225
211
  <TouchableOpacity
package/Input/index.tsx CHANGED
@@ -36,7 +36,7 @@ export interface InputProps extends TextInputProps {
36
36
  errorMessage?: string;
37
37
 
38
38
  /**
39
- * Optional. Trailing icon name or URL; any other string is shown as a text action.
39
+ * Optional. Represents the name or key of the icon to be displayed in the Input component.
40
40
  */
41
41
  trailing?: string;
42
42
 
@@ -69,18 +69,19 @@ export interface InputProps extends TextInputProps {
69
69
  required?: boolean;
70
70
 
71
71
  /**
72
- * Optional. If `true`, keeps an empty error row when there is no error or hint.
72
+ * Optional. If `true`,
73
+ * include spacing when Input not have error message.
73
74
  * Defaults to `false` if not provided.
74
75
  */
75
76
  errorSpacing?: boolean;
76
77
 
77
78
  /**
78
- * Optional. If `true`, shows a loading spinner in place of the trailing icon.
79
+ * If true, the icon of input will use be show indicator loading.
79
80
  */
80
81
  loading?: boolean;
81
82
 
82
83
  /**
83
- * Optional. Represents the leading icon in the Input component.
84
+ * Optional. Represents the leading icon in the Input component.
84
85
  */
85
86
  leadingIcon?: string;
86
87
 
@@ -110,13 +111,12 @@ export interface InputProps extends TextInputProps {
110
111
  hintText?: string;
111
112
 
112
113
  /**
113
- * Optional. Callback function to be called when the floating icon is pressed.
114
+ * Optional. Represents the style of the Input component.
114
115
  */
115
116
  onPressFloatingIcon?: () => void;
116
117
 
117
118
  /**
118
- * Optional. If `true`, shows a clear icon while focused with a value.
119
- * Defaults to `true` if not provided.
119
+ * Optional. Represents the style of the Input component.
120
120
  */
121
121
  showClearIcon?: boolean;
122
122
  }
@@ -137,12 +137,12 @@ export interface InputSearchProps extends TextInputProps {
137
137
 
138
138
  /**
139
139
  * Optional. If `true`, the text on the search button will be displayed.
140
- * Defaults to `true` if not provided.
140
+ * Defaults to `false` if not provided.
141
141
  */
142
142
  showButtonText?: boolean;
143
143
 
144
144
  /**
145
- * Optional. Callback function to be called when the search button is pressed.
145
+ * Optional. Represents the value for the floating title in the InputSearch component.
146
146
  */
147
147
  onPressButtonText?: () => void;
148
148
 
@@ -180,7 +180,7 @@ export interface InputSearchProps extends TextInputProps {
180
180
  onPressTrailing?: () => void;
181
181
 
182
182
  /**
183
- * Optional. Background color of the search field. Defaults to the surface color.
183
+ * Optional. If `true`, the user won't be able to interact with the Input component.
184
184
  */
185
185
  backgroundColor?: string;
186
186
 
@@ -195,7 +195,8 @@ export interface InputSearchProps extends TextInputProps {
195
195
  typingData?: string[];
196
196
 
197
197
  /**
198
- * Optional. Called when the field is pressed while showing `typingData`.
198
+ * Optional. Represents the callback function to be called when the search button is pressed.
199
+ * @param e
199
200
  */
200
201
  onPress?: (e: GestureResponderEvent) => void;
201
202
  }
@@ -258,7 +259,7 @@ export interface InputDropDownProps extends TouchableOpacityProps {
258
259
  placeholder?: string;
259
260
 
260
261
  /**
261
- * Optional. If `true`, the value wraps to multiple lines and the chevron aligns to the top.
262
+ * Optional. Represents the data for the dropdown.
262
263
  */
263
264
  multiline?: boolean;
264
265
 
@@ -302,18 +303,19 @@ export interface InputDropDownProps extends TouchableOpacityProps {
302
303
  required?: boolean;
303
304
 
304
305
  /**
305
- * Optional. If `true`, keeps an empty error row when there is no error or hint.
306
+ * Optional. If `true`,
307
+ * include spacing when Input not have error message.
306
308
  * Defaults to `false` if not provided.
307
309
  */
308
310
  errorSpacing?: boolean;
309
311
 
310
312
  /**
311
- * Optional. If `true`, shows a loading spinner in place of the trailing icon.
313
+ * If true, the icon of input will use be show indicator loading.
312
314
  */
313
315
  loading?: boolean;
314
316
 
315
317
  /**
316
- * Optional. Represents the leading icon in the Input component.
318
+ * Optional. Represents the leading icon in the Input component.
317
319
  */
318
320
  leadingIcon?: string;
319
321
 
@@ -338,14 +340,15 @@ export interface InputDropDownProps extends TouchableOpacityProps {
338
340
  hintText?: string;
339
341
 
340
342
  /**
341
- * Optional. Callback function to be called when the floating icon is pressed.
343
+ * Optional. Represents the style of the Input component.
342
344
  */
343
345
  onPressFloatingIcon?: () => void;
344
346
  }
345
347
 
346
348
  export interface InputPhoneNumberProps extends InputProps {
347
349
  /**
348
- * Optional. Currently unused: the displayed prefix is always `+84`.
350
+ * Optional. Represents the country code to be displayed in the InputPhoneNumber component.
351
+ * It should be in the format of a string, e.g., "+1" for the United States.
349
352
  */
350
353
  countryCode?: string;
351
354
  }
@@ -1,21 +1,15 @@
1
1
  export const moneyFormatter = new Intl.NumberFormat('vi-VN');
2
2
 
3
- /**
4
- * Locates the single edited span between `old` and `text`: common prefix
5
- * length (`start`), common suffix length (`suffix`) and removed + inserted
6
- * char count (`size`).
7
- */
8
3
  export const findEdit = (old: string, text: string) => {
9
4
  let start = 0;
10
5
  while (start < text.length && old[start] === text[start]) {
11
6
  start += 1;
12
7
  }
13
- const maxSuffix = Math.min(text.length, old.length) - start;
14
8
  let suffix = 0;
15
- while (suffix < maxSuffix) {
16
- if (old[old.length - 1 - suffix] !== text[text.length - 1 - suffix]) {
17
- break;
18
- }
9
+ while (
10
+ suffix < Math.min(text.length, old.length) - start &&
11
+ old[old.length - 1 - suffix] === text[text.length - 1 - suffix]
12
+ ) {
19
13
  suffix += 1;
20
14
  }
21
15
  const size = old.length + text.length - 2 * (start + suffix);
package/Input/styles.ts CHANGED
@@ -2,6 +2,7 @@ import { Platform, StyleSheet } from 'react-native';
2
2
  import { Colors, Radius, Spacing } from '../Consts';
3
3
 
4
4
  export default StyleSheet.create({
5
+ //input style
5
6
  input: {
6
7
  flex: 1,
7
8
  paddingLeft: Spacing.M,
@@ -64,6 +65,7 @@ export default StyleSheet.create({
64
65
  marginTop: Spacing.S,
65
66
  },
66
67
 
68
+ //text area style
67
69
  textAreaView: {
68
70
  flex: 1,
69
71
  },
@@ -92,6 +94,7 @@ export default StyleSheet.create({
92
94
  marginBottom: Spacing.XS,
93
95
  },
94
96
 
97
+ //searchInput
95
98
  searchInputWrapper: {
96
99
  flexDirection: 'row',
97
100
  borderRadius: Radius.XL,
@@ -133,6 +136,7 @@ export default StyleSheet.create({
133
136
  ios: 36,
134
137
  }),
135
138
  },
139
+ //OTP
136
140
  otpWrapper: {
137
141
  width: '100%',
138
142
  marginTop: Spacing.M,
@@ -179,6 +183,7 @@ export default StyleSheet.create({
179
183
  opacity: 0.02,
180
184
  letterSpacing: Spacing.XXL,
181
185
  },
186
+ //DropDown
182
187
  inputDropDownWrapper: {
183
188
  flexDirection: 'row',
184
189
  marginTop: Spacing.S,
package/Input/utils.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import { Localize } from '../Application';
2
2
 
3
- /**
4
- * Formats a number Vietnamese-style (`.` thousands, `,` decimals) with
5
- * `currency` appended; 0, NaN or a falsy value gives `0<currency>`.
6
- */
7
3
  const formatNumberToMoney = (number: number, currency = '') => {
8
4
  if (!number || isNaN(number) || Number(number) === 0) {
9
5
  return `0${currency}`;
@@ -45,10 +41,6 @@ const formatNumberToMoney = (number: number, currency = '') => {
45
41
  return isNegative ? `-${result}${currency}` : `${result}${currency}`;
46
42
  };
47
43
 
48
- /**
49
- * Parses a formatted money string back to a number by stripping
50
- * `currencyUnit`, separators, `đ` and spaces; unparsable text gives 0.
51
- */
52
44
  const formatMoneyToNumber = (money: string, currencyUnit: string) => {
53
45
  if (money && money.length > 0) {
54
46
  const moneyString = money
@@ -67,20 +59,26 @@ const formatMoneyToNumber = (money: string, currencyUnit: string) => {
67
59
  return Number(money);
68
60
  };
69
61
 
62
+ // --- PHONE VALIDATION ---
70
63
  function formatPhoneNumber(phone: string): string {
71
64
  return phone.replace(/\D/g, '');
72
65
  }
73
66
 
74
- /**
75
- * Keeps the digits of `phone` (prepending the `0` a 9-char local number lacks)
76
- * and returns a localized error when empty or not 10-11 digits.
77
- */
78
67
  function checkValidPhoneNumber(phone: string): {
79
68
  phoneFormatted: string;
80
69
  error?: string;
81
70
  } {
82
- const isMissingLeadingZero = phone.length === 9 && /^[1-9]/.test(phone);
83
- const phoneNumber = isMissingLeadingZero ? `0${phone}` : phone;
71
+ let phoneNumber = phone;
72
+
73
+ // if not starting with 0 and has 9 digits => prepend 0
74
+ if (
75
+ phoneNumber.length > 0 &&
76
+ phoneNumber[0] !== '0' &&
77
+ /^\d$/.test(phoneNumber[0]) &&
78
+ phoneNumber.length === 9
79
+ ) {
80
+ phoneNumber = '0' + phoneNumber;
81
+ }
84
82
 
85
83
  const phoneFormatted = formatPhoneNumber(phoneNumber);
86
84
  const localize = new Localize({ vi: {}, en: {} });