@momo-kits/foundation 0.169.10-beta.1 → 0.169.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 (40) hide show
  1. package/Application/BottomSheet.hooks.ts +27 -17
  2. package/Application/BottomSheet.tsx +35 -27
  3. package/Application/BottomTab/BottomTabBar.tsx +6 -6
  4. package/Application/BottomTab/DuoRailTabBar.tsx +5 -5
  5. package/Application/BottomTab/TabScreen.tsx +11 -10
  6. package/Application/BottomTab/index.tsx +5 -5
  7. package/Application/BottomTab/usePressLift.ts +3 -3
  8. package/Application/Components/HeaderToolkitAction.tsx +16 -13
  9. package/Application/Components/useToolkitActions.tsx +22 -21
  10. package/Application/ModalScreen.tsx +19 -15
  11. package/Application/StackScreen.tsx +13 -9
  12. package/Application/modalLayout.ts +1 -0
  13. package/Context/index.ts +3 -1
  14. package/Image/index.tsx +6 -6
  15. package/Input/ErrorView.tsx +59 -0
  16. package/Input/FloatingView.tsx +101 -0
  17. package/Input/Input.tsx +40 -37
  18. package/Input/InputDropDown.tsx +13 -10
  19. package/Input/InputMoney.tsx +17 -17
  20. package/Input/InputMoneyView.tsx +12 -12
  21. package/Input/InputOTP.tsx +46 -34
  22. package/Input/InputPhoneNumber.tsx +48 -43
  23. package/Input/InputSearch.tsx +24 -23
  24. package/Input/InputTextArea.tsx +29 -23
  25. package/Input/RenderTrailing.tsx +79 -0
  26. package/Input/constants.ts +8 -0
  27. package/Input/utils.ts +126 -7
  28. package/Layout/FloatingButton.tsx +38 -24
  29. package/Layout/layoutMetrics.ts +43 -34
  30. package/Layout/screenTypes.ts +2 -3
  31. package/Pagination/PaginationScroll.tsx +5 -3
  32. package/Pagination/types.ts +3 -5
  33. package/Popup/PopupNotify.tsx +17 -14
  34. package/Popup/PopupPromotion.tsx +5 -4
  35. package/Popup/types.ts +2 -4
  36. package/Popup/utils.ts +7 -4
  37. package/Skeleton/index.tsx +15 -12
  38. package/package.json +1 -1
  39. package/Input/common.tsx +0 -257
  40. package/Input/moneyUtils.ts +0 -23
@@ -27,6 +27,12 @@ import { isIPhoneDuo } from './utils';
27
27
  import { useModalLayout } from './modalLayout';
28
28
  import { scheduleOnRN } from 'react-native-worklets';
29
29
 
30
+ const DUO_ORIENTATIONS = ['portrait', 'landscape'];
31
+ const ENTER_DURATION = 250;
32
+ const EXIT_DURATION = 200;
33
+ const INITIAL_SCALE = 0.8;
34
+ const ENTER_EASING = Easing.bezier(0.2, 0.0, 0, 1.0);
35
+
30
36
  /**
31
37
  * Screen of the `Modal` route: a bottom sheet when pushed by
32
38
  * `showBottomSheet`, otherwise a centered popup.
@@ -72,7 +78,7 @@ const Modal: React.FC<ModalParams> = props => {
72
78
  }, [context, description, navigator?.maxApi, title]);
73
79
 
74
80
  const opacity = useRef(new Animated.Value(0)).current;
75
- const scale = useRef(new Animated.Value(0.8)).current;
81
+ const scale = useRef(new Animated.Value(INITIAL_SCALE)).current;
76
82
  const scrimStyle = useMemo(
77
83
  () => ({
78
84
  opacity: opacity.interpolate({
@@ -98,13 +104,13 @@ const Modal: React.FC<ModalParams> = props => {
98
104
  Animated.parallel([
99
105
  Animated.timing(opacity, {
100
106
  toValue: 1,
101
- duration: 250,
107
+ duration: ENTER_DURATION,
102
108
  useNativeDriver: true,
103
109
  }),
104
110
  Animated.timing(scale, {
105
111
  toValue: 1,
106
- duration: 250,
107
- easing: Easing.bezier(0.2, 0.0, 0, 1.0),
112
+ duration: ENTER_DURATION,
113
+ easing: ENTER_EASING,
108
114
  useNativeDriver: true,
109
115
  }),
110
116
  ]).start();
@@ -114,7 +120,7 @@ const Modal: React.FC<ModalParams> = props => {
114
120
  };
115
121
  }, [opacity, props.route.params, scale]);
116
122
 
117
- const onDismiss = useCallback(
123
+ const handleDismiss = useCallback(
118
124
  (callback = () => {}, preventClose = false) => {
119
125
  if (preventClose) {
120
126
  return;
@@ -122,12 +128,12 @@ const Modal: React.FC<ModalParams> = props => {
122
128
  Animated.parallel([
123
129
  Animated.timing(opacity, {
124
130
  toValue: 0,
125
- duration: 200,
131
+ duration: EXIT_DURATION,
126
132
  useNativeDriver: true,
127
133
  }),
128
134
  Animated.timing(scale, {
129
- toValue: 0.8,
130
- duration: 200,
135
+ toValue: INITIAL_SCALE,
136
+ duration: EXIT_DURATION,
131
137
  easing: Easing.linear,
132
138
  useNativeDriver: true,
133
139
  }),
@@ -143,18 +149,18 @@ const Modal: React.FC<ModalParams> = props => {
143
149
  const backHandler = BackHandler.addEventListener(
144
150
  'hardwareBackPress',
145
151
  () => {
146
- onDismiss(undefined, barrierDismissible);
152
+ handleDismiss(undefined, barrierDismissible);
147
153
  return true;
148
154
  },
149
155
  );
150
156
  return () => backHandler.remove();
151
- }, [barrierDismissible, onDismiss]);
157
+ }, [barrierDismissible, handleDismiss]);
152
158
 
153
159
  return (
154
160
  <Container
155
161
  transparent
156
162
  visible={true}
157
- onRequestClose={onDismiss}
163
+ onRequestClose={handleDismiss}
158
164
  style={StyleSheet.absoluteFillObject}
159
165
  isModalKit={true}
160
166
  supportedOrientations={isDuo ? DUO_ORIENTATIONS : undefined}
@@ -165,7 +171,7 @@ const Modal: React.FC<ModalParams> = props => {
165
171
  >
166
172
  <Pressable
167
173
  style={StyleSheet.absoluteFillObject}
168
- onPress={() => onDismiss(undefined, barrierDismissible)}
174
+ onPress={() => handleDismiss(undefined, barrierDismissible)}
169
175
  >
170
176
  <Animated.View style={[styles.overlayContent, scrimStyle]} />
171
177
  </Pressable>
@@ -188,15 +194,13 @@ const Modal: React.FC<ModalParams> = props => {
188
194
  modalStyle,
189
195
  ]}
190
196
  >
191
- <Component {...params} onRequestClose={onDismiss} />
197
+ <Component {...params} onRequestClose={handleDismiss} />
192
198
  </Animated.View>
193
199
  </KeyboardAvoidingView>
194
200
  </Container>
195
201
  );
196
202
  };
197
203
 
198
- const DUO_ORIENTATIONS = ['portrait', 'landscape'];
199
-
200
204
  const styles = StyleSheet.create({
201
205
  overlayContent: {
202
206
  flex: 1,
@@ -10,6 +10,10 @@ import { createScreenTracking } from './StackScreen.tracking';
10
10
  import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
11
11
  import { useHeaderFrameOptions } from './headerLayout';
12
12
 
13
+ const TRACKING_TIMEOUT = 5000;
14
+ const ELEMENT_IDLE_TIMEOUT = 2000;
15
+ const LOADING_IDLE_TIMEOUT = 2000;
16
+
13
17
  const runAfterInteractions = InteractionManager.runAfterInteractions;
14
18
 
15
19
  /**
@@ -80,7 +84,7 @@ const StackScreen: React.FC<any> = props => {
80
84
 
81
85
  useEffect(() => {
82
86
  let focusScreen: any;
83
- let onFocusApp: any;
87
+ let focusAppListener: { remove?: () => void } | undefined;
84
88
  if (['Invalid', 'screen'].includes(screenName)) {
85
89
  navigator?.maxApi?.showPopup?.('notice', {
86
90
  title: 'Invalid screen name',
@@ -94,19 +98,19 @@ const StackScreen: React.FC<any> = props => {
94
98
  if (!bottomTab) {
95
99
  focusScreen = props.navigation.addListener('focus', () => {
96
100
  navigator?.maxApi?.getDataObserver?.('current_screen', (item: any) => {
97
- onScreenNavigated(item?.screenName, screenName);
101
+ handleScreenNavigated(item?.screenName, screenName);
98
102
  navigator?.maxApi?.setDataObserver?.('current_screen', {
99
103
  screenName,
100
104
  startTime,
101
105
  });
102
106
  });
103
107
  });
104
- onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
108
+ focusAppListener = navigator?.maxApi?.listen?.('onFocusApp', () => {
105
109
  if (props.navigation.isFocused()) {
106
110
  navigator?.maxApi?.getDataObserver?.(
107
111
  'current_screen',
108
112
  (item: any) => {
109
- onScreenNavigated(item?.screenName, screenName);
113
+ handleScreenNavigated(item?.screenName, screenName);
110
114
  navigator?.maxApi?.setDataObserver?.('current_screen', {
111
115
  screenName,
112
116
  startTime,
@@ -135,7 +139,7 @@ const StackScreen: React.FC<any> = props => {
135
139
  tracking.current.timeoutTracking = setTimeout(() => {
136
140
  onScreenLoad();
137
141
  onScreenInteraction();
138
- }, 5000);
142
+ }, TRACKING_TIMEOUT);
139
143
 
140
144
  return () => {
141
145
  onScreenLoad();
@@ -146,13 +150,13 @@ const StackScreen: React.FC<any> = props => {
146
150
  // eslint-disable-next-line react-hooks/exhaustive-deps
147
151
  clearTimeout(tracking.current.timeoutLoading);
148
152
  focusScreen?.();
149
- onFocusApp?.remove?.();
153
+ focusAppListener?.remove?.();
150
154
  };
151
155
  // eslint-disable-next-line react-hooks/exhaustive-deps
152
156
  }, []);
153
157
 
154
158
  /** Reports a navigation to this screen: `back` if it was shown before. */
155
- const onScreenNavigated = (pre: string, current: string) => {
159
+ const handleScreenNavigated = (pre: string, current: string) => {
156
160
  if (!isBackgroundToForeground) {
157
161
  const item: any = {
158
162
  preScreenName: pre,
@@ -243,7 +247,7 @@ const StackScreen: React.FC<any> = props => {
243
247
 
244
248
  onScreenLoad();
245
249
  onScreenInteraction();
246
- }, 2000);
250
+ }, ELEMENT_IDLE_TIMEOUT);
247
251
  },
248
252
  onLoading: (loading: boolean) => {
249
253
  if (loading && tracking.current.timeStartLoading === 0) {
@@ -257,7 +261,7 @@ const StackScreen: React.FC<any> = props => {
257
261
  clearTimeout(tracking.current.timeoutLoading);
258
262
  tracking.current.timeoutLoading = setTimeout(() => {
259
263
  onScreenLoading();
260
- }, 2000);
264
+ }, LOADING_IDLE_TIMEOUT);
261
265
  },
262
266
  onSetParams: (item: ScreenTrackingParams) => {
263
267
  tracking.current.params = item;
@@ -73,6 +73,7 @@ type ModalLayoutInput = {
73
73
  hinge?: ReservedRegion;
74
74
  };
75
75
 
76
+ /** `value` limited to the `[min, max]` range. */
76
77
  const clamp = (value: number, min: number, max: number) => {
77
78
  return Math.min(Math.max(value, min), max);
78
79
  };
package/Context/index.ts CHANGED
@@ -5,9 +5,11 @@ import { defaultContext } from '../Consts';
5
5
  const Context = createContext({});
6
6
  const ApplicationContext = createContext(defaultContext);
7
7
 
8
- // Shared by the host app via `Platform`; standalone falls back to a local one.
8
+ /** Mini app context shared by the host via `Platform`; local if standalone. */
9
9
  const MiniAppContext = (Platform as any).MiniAppContext ?? Context;
10
+ /** Screen context shared by the host via `Platform`; local if standalone. */
10
11
  const ScreenContext = (Platform as any).ScreenContext ?? Context;
12
+ /** Component context shared by the host via `Platform`; local if standalone. */
11
13
  const ComponentContext = (Platform as any).ComponentContext ?? Context;
12
14
  const SkeletonContext = createContext({ loading: false });
13
15
  /**
package/Image/index.tsx CHANGED
@@ -44,7 +44,7 @@ const Image: React.FC<ImageProps> = ({
44
44
  const component: any = useContext(ComponentContext);
45
45
  const context = useContext<any>(MiniAppContext);
46
46
  const skeleton = useContext(SkeletonContext);
47
- const error = useRef(false);
47
+ const hasErrorRef = useRef(false);
48
48
  const [status, setStatus] = useState<Status>('success');
49
49
 
50
50
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
@@ -92,30 +92,30 @@ const Image: React.FC<ImageProps> = ({
92
92
  showBaseLineDebug && styles.debugBaseLine,
93
93
  ]}
94
94
  onProgress={event => {
95
- error.current = false;
95
+ hasErrorRef.current = false;
96
96
  if (status !== 'loading' && loading) {
97
97
  setStatus('loading');
98
98
  }
99
99
  onProgress?.(event);
100
100
  }}
101
101
  onLoadStart={() => {
102
- error.current = false;
102
+ hasErrorRef.current = false;
103
103
  onLoadStart?.();
104
104
  }}
105
105
  onLoad={event => {
106
- error.current = false;
106
+ hasErrorRef.current = false;
107
107
  onLoad?.(event);
108
108
  }}
109
109
  onLoadEnd={() => {
110
110
  let current: Status = 'success';
111
- if (error.current) {
111
+ if (hasErrorRef.current) {
112
112
  current = 'error';
113
113
  }
114
114
  setStatus(prevState => (prevState !== current ? current : prevState));
115
115
  onLoadEnd?.();
116
116
  }}
117
117
  onError={() => {
118
- error.current = true;
118
+ hasErrorRef.current = true;
119
119
  onError?.();
120
120
  }}
121
121
  >
@@ -0,0 +1,59 @@
1
+ import React, { type FC, useContext } from 'react';
2
+ import { View } from 'react-native';
3
+ import { Styles } from '../Consts';
4
+ import { ApplicationContext } from '../Context';
5
+ import { Icon } from '../Icon';
6
+ import { Text, useScaleSize } from '../Text';
7
+ import styles from './styles';
8
+
9
+ const ICON_SIZE = 16;
10
+
11
+ type ErrorViewProps = {
12
+ errorMessage?: string;
13
+ errorSpacing?: boolean;
14
+ hintText?: string;
15
+ componentId?: string;
16
+ };
17
+
18
+ /**
19
+ * Error or hint row under an input (error wins); with neither, `errorSpacing`
20
+ * keeps an empty row so the layout doesn't jump when an error appears.
21
+ */
22
+ const ErrorView: FC<ErrorViewProps> = ({
23
+ errorMessage,
24
+ errorSpacing,
25
+ hintText,
26
+ componentId,
27
+ }) => {
28
+ const { theme } = useContext(ApplicationContext);
29
+ const errorColor = theme.colors.error.primary;
30
+ const hintColor = theme.colors.text.hint;
31
+ const hintTextDefault = hintText ?? 'Không thể chỉnh sửa';
32
+ const scaledIconSize = useScaleSize(ICON_SIZE);
33
+
34
+ if (errorMessage || hintText) {
35
+ const messageColor = errorMessage ? errorColor : hintColor;
36
+ const messageType = errorMessage ? 'error' : 'hint';
37
+ return (
38
+ <View style={styles.errorView}>
39
+ <View style={styles.errorIcon}>
40
+ <Icon color={messageColor} source="ic_error" size={scaledIconSize} />
41
+ </View>
42
+ <Text
43
+ accessibilityLabel={`${componentId}|${messageType}-text`}
44
+ style={Styles.flex}
45
+ color={messageColor}
46
+ typography={'description_default_regular'}
47
+ >
48
+ {errorMessage ?? hintTextDefault}
49
+ </Text>
50
+ </View>
51
+ );
52
+ }
53
+ if (errorSpacing) {
54
+ return <View style={[styles.errorView, styles.errorSpacing]} />;
55
+ }
56
+ return <View />;
57
+ };
58
+
59
+ export { ErrorView };
@@ -0,0 +1,101 @@
1
+ import React, { type FC, useContext } from 'react';
2
+ import {
3
+ type GestureResponderEvent,
4
+ TouchableOpacity,
5
+ View,
6
+ type ViewStyle,
7
+ } from 'react-native';
8
+ import { ApplicationContext } from '../Context';
9
+ import { Icon } from '../Icon';
10
+ import { Text, useScaleSize } from '../Text';
11
+ import { PRESSED_OPACITY } from './constants';
12
+ import styles from './styles';
13
+
14
+ const LABEL_OFFSET = 10;
15
+ const ICON_SIZE = 16;
16
+
17
+ type FloatingViewProps = {
18
+ floatingValue?: string;
19
+ floatingIconColor?: string;
20
+ componentId?: string;
21
+ disabled?: boolean;
22
+ floatingIcon?: string;
23
+ required?: boolean;
24
+ style?: ViewStyle;
25
+ onPress?: (e: GestureResponderEvent) => void;
26
+ };
27
+
28
+ /**
29
+ * Floating label on the input's top border with a required `*` and an
30
+ * optional pressable icon; renders nothing without `floatingValue`.
31
+ */
32
+ const FloatingView: FC<FloatingViewProps> = ({
33
+ floatingValue,
34
+ floatingIconColor,
35
+ disabled,
36
+ floatingIcon,
37
+ required,
38
+ style,
39
+ onPress,
40
+ componentId,
41
+ }) => {
42
+ const { theme } = useContext(ApplicationContext);
43
+ const scaledTop = useScaleSize(LABEL_OFFSET);
44
+ if (!floatingValue) {
45
+ return null;
46
+ }
47
+
48
+ let floatingTextColor = theme.colors.text.hint;
49
+ let floatingIconTintColor = floatingIconColor;
50
+ if (disabled) {
51
+ floatingTextColor = theme.colors.text.disable;
52
+ floatingIconTintColor = theme.colors.text.disable;
53
+ }
54
+
55
+ return (
56
+ <View
57
+ style={[
58
+ styles.floatingView,
59
+ styles.floatingViewLayer,
60
+ {
61
+ backgroundColor: theme.colors.background.surface,
62
+ top: -scaledTop,
63
+ },
64
+ style,
65
+ ]}
66
+ >
67
+ <Text
68
+ color={floatingTextColor}
69
+ typography={'label_s_medium'}
70
+ accessibilityLabel={`${componentId}|floating-text`}
71
+ >
72
+ {floatingValue}
73
+ {required && (
74
+ <Text
75
+ typography={'label_s_medium'}
76
+ color={theme.colors.error.primary}
77
+ >
78
+ *
79
+ </Text>
80
+ )}
81
+ </Text>
82
+ {!!floatingIcon && (
83
+ <TouchableOpacity
84
+ activeOpacity={onPress ? PRESSED_OPACITY : 1}
85
+ onPress={onPress}
86
+ accessibilityLabel={`${componentId}|floating-icon-touch`}
87
+ >
88
+ <Icon
89
+ color={floatingIconTintColor}
90
+ source={floatingIcon}
91
+ size={ICON_SIZE}
92
+ style={styles.floatingIcon}
93
+ accessibilityLabel={`${componentId}|floating-icon`}
94
+ />
95
+ </TouchableOpacity>
96
+ )}
97
+ </View>
98
+ );
99
+ };
100
+
101
+ export { FloatingView };
package/Input/Input.tsx CHANGED
@@ -13,22 +13,25 @@ import {
13
13
  } from 'react-native';
14
14
  import { useComponentId } from '../Application';
15
15
  import { Icon } from '../Icon';
16
- import { exportFontFamily, useScaleSize } from '../Text';
17
- import {
18
- ErrorView,
19
- FloatingView,
20
- getBorderColor,
21
- getSizeStyle,
22
- RenderTrailing,
23
- } from './common';
24
- import { InputProps } from './index';
25
- import styles from './styles';
26
- import SystemTextInput from './SystemTextInput';
27
16
  import {
28
17
  ApplicationContext,
29
18
  ComponentContext,
30
19
  MiniAppContext,
31
20
  } from '../Context';
21
+ import { exportFontFamily, useScaleSize } from '../Text';
22
+ import { CLEAR_ICON_SIZE, INPUT_FONT_SIZE } from './constants';
23
+ import { ErrorView } from './ErrorView';
24
+ import { FloatingView } from './FloatingView';
25
+ import { InputProps } from './index';
26
+ import { RenderTrailing } from './RenderTrailing';
27
+ import styles from './styles';
28
+ import SystemTextInput from './SystemTextInput';
29
+ import {
30
+ getBorderColor,
31
+ getInputHeight,
32
+ getLeadingIconSize,
33
+ getSizeStyle,
34
+ } from './utils';
32
35
 
33
36
  /**
34
37
  * Text input with floating label, leading/trailing/clear icons and an error or
@@ -73,11 +76,11 @@ const Input = forwardRef(
73
76
  ) => {
74
77
  const { theme } = useContext(ApplicationContext);
75
78
  const context = useContext<any>(MiniAppContext);
76
- const scaledFontSize = useScaleSize(14);
77
- const scaleHeight = useScaleSize(size === 'small' ? 48 : 56);
78
- const [focused, setFocused] = useState(false);
79
+ const scaledFontSize = useScaleSize(INPUT_FONT_SIZE);
80
+ const scaleHeight = useScaleSize(getInputHeight(size));
81
+ const [isFocused, setIsFocused] = useState(false);
79
82
  const [internalText, setInternalText] = useState(defaultValue || '');
80
- const haveValue = value !== undefined ? !!value : !!internalText;
83
+ const hasValue = value !== undefined ? !!value : !!internalText;
81
84
  const inputRef = useRef<TextInput | null>(null);
82
85
  const componentName = 'Input';
83
86
 
@@ -88,30 +91,30 @@ const Input = forwardRef(
88
91
  props.accessibilityLabel,
89
92
  );
90
93
 
91
- const onClearText = () => {
94
+ const handleClearText = () => {
92
95
  inputRef?.current?.clear();
93
96
  inputRef.current?.setNativeProps({ text: '' });
94
- _onChangeText('');
97
+ handleChangeText('');
95
98
  };
96
99
 
97
- const _onChangeText = (text: string) => {
100
+ const handleChangeText = (text: string) => {
98
101
  if (value === undefined) {
99
102
  setInternalText(text);
100
103
  }
101
104
  onChangeText?.(text);
102
105
  };
103
106
 
104
- const _onFocus = (e: TextInputFocusEvent) => {
105
- setFocused(true);
107
+ const handleFocus = (e: TextInputFocusEvent) => {
108
+ setIsFocused(true);
106
109
  onFocus?.(e);
107
110
  };
108
111
 
109
- const _onBlur = (e: TextInputFocusEvent) => {
110
- setFocused(false);
112
+ const handleBlur = (e: TextInputFocusEvent) => {
113
+ setIsFocused(false);
111
114
  onBlur?.(e);
112
115
  };
113
116
 
114
- const _setText = (text: string) => {
117
+ const handleSetText = (text: string) => {
115
118
  if (__DEV__ && value !== undefined) {
116
119
  console.error(
117
120
  `${componentName}: calling ref.setText() on a controlled input (\`value\` is provided) is an anti-pattern. ` +
@@ -120,21 +123,21 @@ const Input = forwardRef(
120
123
  );
121
124
  }
122
125
  inputRef?.current?.setNativeProps({ text });
123
- _onChangeText(text);
126
+ handleChangeText(text);
124
127
  };
125
128
 
126
129
  useImperativeHandle(ref, () => {
127
130
  return {
128
- clear: onClearText,
131
+ clear: handleClearText,
129
132
  focus: () => inputRef.current?.focus(),
130
133
  blur: () => inputRef.current?.blur(),
131
- setText: _setText,
134
+ setText: handleSetText,
132
135
  };
133
136
  });
134
137
 
135
138
  const renderInputView = () => {
136
139
  const disabledColor = theme.colors.text.disable;
137
- const secure = secureTextEntry;
140
+ const isSecure = secureTextEntry;
138
141
  let textColor = theme.colors.text.default;
139
142
  let placeholderColor = theme.colors.text.hint;
140
143
  let iconTintColor = trailingColor;
@@ -156,7 +159,7 @@ const Input = forwardRef(
156
159
  styles.inputWrapper,
157
160
  { backgroundColor: theme.colors.background.surface },
158
161
  getSizeStyle(size, undefined, scaleHeight),
159
- getBorderColor(theme, focused, errorMessage, disabled),
162
+ getBorderColor(theme, isFocused, errorMessage, disabled),
160
163
  ]}
161
164
  >
162
165
  <FloatingView
@@ -178,7 +181,7 @@ const Input = forwardRef(
178
181
  <Icon
179
182
  color={leadingIconColor}
180
183
  source={leadingIcon}
181
- size={size === 'small' ? 24 : 32}
184
+ size={getLeadingIconSize(size)}
182
185
  accessibilityLabel={`${componentId}|leading-icon`}
183
186
  />
184
187
  </TouchableOpacity>
@@ -188,7 +191,7 @@ const Input = forwardRef(
188
191
  {...props}
189
192
  accessibilityLabel={`${componentId}`}
190
193
  editable={editable && !disabled}
191
- secureTextEntry={secure}
194
+ secureTextEntry={isSecure}
192
195
  textAlignVertical="center"
193
196
  ref={inputRef}
194
197
  accessibilityState={{
@@ -202,15 +205,15 @@ const Input = forwardRef(
202
205
  color: textColor,
203
206
  fontFamily: exportFontFamily(fontWeight ?? 'regular'),
204
207
  },
205
- secure && haveValue && secureTextStyle,
208
+ isSecure && hasValue && secureTextStyle,
206
209
  ]}
207
210
  defaultValue={defaultValue}
208
211
  allowFontScaling={false}
209
212
  textBreakStrategy="highQuality"
210
213
  value={value}
211
- onChangeText={_onChangeText}
212
- onFocus={_onFocus}
213
- onBlur={_onBlur}
214
+ onChangeText={handleChangeText}
215
+ onFocus={handleFocus}
216
+ onBlur={handleBlur}
214
217
  placeholder={placeholder}
215
218
  selectionColor={theme.colors.primary}
216
219
  placeholderTextColor={placeholderColor}
@@ -218,16 +221,16 @@ const Input = forwardRef(
218
221
  />
219
222
  </View>
220
223
  <View style={styles.iconView}>
221
- {showClearIcon && focused && haveValue && (
224
+ {showClearIcon && isFocused && hasValue && (
222
225
  <TouchableOpacity
223
226
  accessibilityLabel={`${componentId}|clear-icon-touch`}
224
227
  accessible={false}
225
228
  style={styles.iconWrapper}
226
- onPress={onClearText}
229
+ onPress={handleClearText}
227
230
  >
228
231
  <Icon
229
232
  source="24_navigation_close_circle_full"
230
- size={16}
233
+ size={CLEAR_ICON_SIZE}
231
234
  color={theme.colors.text.hint}
232
235
  accessibilityLabel={`${componentId}|clear-icon`}
233
236
  />
@@ -6,17 +6,20 @@ import {
6
6
  ComponentContext,
7
7
  MiniAppContext,
8
8
  } from '../Context';
9
+ import { Spacing } from '../Consts';
9
10
  import { Icon } from '../Icon';
11
+ import { Text, useScaleSize } from '../Text';
12
+ import { PRESSED_OPACITY } from './constants';
13
+ import { ErrorView } from './ErrorView';
14
+ import { FloatingView } from './FloatingView';
15
+ import { InputDropDownProps } from './index';
16
+ import styles from './styles';
10
17
  import {
11
- ErrorView,
12
- FloatingView,
13
18
  getBorderColor,
19
+ getInputHeight,
20
+ getLeadingIconSize,
14
21
  getSizeStyle,
15
- } from './common';
16
- import { InputDropDownProps } from './index';
17
- import { Text, useScaleSize } from '../Text';
18
- import styles from './styles';
19
- import { Spacing } from '../Consts';
22
+ } from './utils';
20
23
 
21
24
  /**
22
25
  * Pressable field styled like an input with a chevron; shows `value` or
@@ -44,7 +47,7 @@ const InputDropDown = ({
44
47
  }: InputDropDownProps) => {
45
48
  const { theme } = useContext(ApplicationContext);
46
49
  const context = useContext<any>(MiniAppContext);
47
- const scaleHeight = useScaleSize(size === 'small' ? 48 : 56);
50
+ const scaleHeight = useScaleSize(getInputHeight(size));
48
51
  const componentName = 'InputDropDown';
49
52
 
50
53
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
@@ -99,7 +102,7 @@ const InputDropDown = ({
99
102
  <Icon
100
103
  color={leadingIconColor}
101
104
  source={leadingIcon}
102
- size={size === 'small' ? 24 : 32}
105
+ size={getLeadingIconSize(size)}
103
106
  accessibilityLabel={`${componentId}|leading-icon`}
104
107
  />
105
108
  </View>
@@ -136,7 +139,7 @@ const InputDropDown = ({
136
139
  {...props}
137
140
  accessibilityLabel={`${componentId}|touch`}
138
141
  accessibilityState={{ disabled: disabled as boolean }}
139
- activeOpacity={0.6}
142
+ activeOpacity={PRESSED_OPACITY}
140
143
  style={[
141
144
  style,
142
145
  styles.wrapper,