@momo-kits/foundation 0.169.9-beta.1 → 0.169.9

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 (63) hide show
  1. package/Application/BottomSheet.hooks.ts +27 -17
  2. package/Application/BottomSheet.tsx +35 -27
  3. package/Application/BottomTab/ActiveIndicator.tsx +5 -1
  4. package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
  5. package/Application/BottomTab/BottomTabBar.tsx +14 -16
  6. package/Application/BottomTab/CurvedContainer.tsx +8 -2
  7. package/Application/BottomTab/CustomBottomTabItem.tsx +2 -2
  8. package/Application/BottomTab/DuoRailTabBar.tsx +5 -5
  9. package/Application/BottomTab/DuoTabBar.tsx +15 -3
  10. package/Application/BottomTab/TabScreen.tsx +11 -10
  11. package/Application/BottomTab/index.tsx +5 -5
  12. package/Application/BottomTab/metrics.ts +23 -3
  13. package/Application/BottomTab/usePressLift.ts +3 -3
  14. package/Application/Components/HeaderAnimated.tsx +13 -38
  15. package/Application/Components/HeaderBackground.tsx +3 -16
  16. package/Application/Components/HeaderExtendHeader.tsx +28 -41
  17. package/Application/Components/HeaderLeft.tsx +18 -4
  18. package/Application/Components/HeaderRight.styles.ts +13 -15
  19. package/Application/Components/HeaderTitle.shared.ts +11 -10
  20. package/Application/Components/HeaderToolkitAction.tsx +54 -21
  21. package/Application/Components/NavigationButton.tsx +6 -9
  22. package/Application/Components/SearchHeader.tsx +7 -20
  23. package/Application/Components/useToolkitActions.tsx +22 -21
  24. package/Application/ModalScreen.tsx +19 -15
  25. package/Application/StackScreen.tsx +13 -18
  26. package/Application/index.ts +1 -11
  27. package/Application/modalLayout.ts +1 -0
  28. package/Application/navigationOptions.tsx +15 -6
  29. package/Application/types.header.ts +2 -7
  30. package/Application/types.tabs.ts +0 -1
  31. package/Application/types.ts +0 -7
  32. package/Context/index.ts +3 -1
  33. package/Image/index.tsx +6 -6
  34. package/Input/ErrorView.tsx +59 -0
  35. package/Input/FloatingView.tsx +101 -0
  36. package/Input/Input.tsx +40 -37
  37. package/Input/InputDropDown.tsx +13 -10
  38. package/Input/InputMoney.tsx +17 -17
  39. package/Input/InputMoneyView.tsx +12 -12
  40. package/Input/InputOTP.tsx +46 -34
  41. package/Input/InputPhoneNumber.tsx +48 -43
  42. package/Input/InputSearch.tsx +26 -39
  43. package/Input/InputTextArea.tsx +29 -23
  44. package/Input/RenderTrailing.tsx +79 -0
  45. package/Input/constants.ts +8 -0
  46. package/Input/index.tsx +1 -7
  47. package/Input/utils.ts +126 -7
  48. package/Layout/FloatingButton.tsx +38 -24
  49. package/Layout/Screen.tsx +0 -3
  50. package/Layout/layoutMetrics.ts +43 -34
  51. package/Layout/screenHeader.tsx +52 -55
  52. package/Layout/screenTypes.ts +4 -13
  53. package/Pagination/PaginationScroll.tsx +5 -3
  54. package/Pagination/types.ts +3 -5
  55. package/Popup/PopupNotify.tsx +17 -14
  56. package/Popup/PopupPromotion.tsx +5 -4
  57. package/Popup/types.ts +2 -4
  58. package/Popup/utils.ts +7 -4
  59. package/Skeleton/index.tsx +15 -12
  60. package/package.json +1 -1
  61. package/Application/headerLayout.ts +0 -165
  62. package/Input/common.tsx +0 -257
  63. package/Input/moneyUtils.ts +0 -23
@@ -20,18 +20,23 @@ import {
20
20
  } from '../Context';
21
21
  import { Colors, Spacing, Styles } from '../Consts';
22
22
  import { Icon } from '../Icon';
23
+ import { Image } from '../Image';
23
24
  import { useScaleSize, Text, exportFontFamily } from '../Text';
24
- import {
25
- ErrorView,
26
- getBorderColor,
27
- getSizeStyle,
28
- RenderTrailing,
29
- } from './common';
25
+ import { Typography } from '../Text/types';
26
+ import { CLEAR_ICON_SIZE } from './constants';
27
+ import { ErrorView } from './ErrorView';
30
28
  import { InputPhoneNumberProps } from './index';
29
+ import { RenderTrailing } from './RenderTrailing';
31
30
  import styles from './styles';
32
31
  import SystemTextInput from './SystemTextInput';
33
- import { Image } from '../Image';
34
- import { Typography } from '../Text/types';
32
+ import { getBorderColor, getInputHeight, getSizeStyle } from './utils';
33
+
34
+ const SMALL_FONT_SIZE = 14;
35
+ const LARGE_FONT_SIZE = 18;
36
+ const SMALL_DIVIDER_HEIGHT = 24;
37
+ const LARGE_DIVIDER_HEIGHT = 32;
38
+ const FOCUSED_BORDER_WIDTH = 1.5;
39
+ const FLAG_SIZE = 24;
35
40
 
36
41
  /**
37
42
  * Vietnamese phone number input with a fixed flag and `+84` prefix, number pad
@@ -67,10 +72,10 @@ const InputPhoneNumber = forwardRef(
67
72
  ) => {
68
73
  const { theme } = useContext(ApplicationContext);
69
74
  const context = useContext<any>(MiniAppContext);
70
- const scaleHeight = useScaleSize(size === 'small' ? 48 : 56);
71
- const [focused, setFocused] = useState(false);
75
+ const scaleHeight = useScaleSize(getInputHeight(size));
76
+ const [isFocused, setIsFocused] = useState(false);
72
77
  const [internalText, setInternalText] = useState(defaultValue || '');
73
- const haveValue = value !== undefined ? !!value : !!internalText;
78
+ const hasValue = value !== undefined ? !!value : !!internalText;
74
79
  const inputRef = useRef<TextInput | null>(null);
75
80
  const componentName = 'InputPhoneNumber';
76
81
 
@@ -81,29 +86,29 @@ const InputPhoneNumber = forwardRef(
81
86
  props.accessibilityLabel,
82
87
  );
83
88
 
84
- const onClearText = () => {
89
+ const handleClearText = () => {
85
90
  inputRef?.current?.clear();
86
- _onChangeText('');
91
+ handleChangeText('');
87
92
  };
88
93
 
89
- const _onChangeText = (text: string) => {
94
+ const handleChangeText = (text: string) => {
90
95
  if (value === undefined) {
91
96
  setInternalText(text);
92
97
  }
93
98
  onChangeText?.(text);
94
99
  };
95
100
 
96
- const _onFocus = (e: TextInputFocusEvent) => {
97
- setFocused(true);
101
+ const handleFocus = (e: TextInputFocusEvent) => {
102
+ setIsFocused(true);
98
103
  onFocus?.(e);
99
104
  };
100
105
 
101
- const _onBlur = (e: TextInputFocusEvent) => {
102
- setFocused(false);
106
+ const handleBlur = (e: TextInputFocusEvent) => {
107
+ setIsFocused(false);
103
108
  onBlur?.(e);
104
109
  };
105
110
 
106
- const _setText = (text: string) => {
111
+ const handleSetText = (text: string) => {
107
112
  if (__DEV__ && value !== undefined) {
108
113
  console.error(
109
114
  `${componentName}: calling ref.setText() on a controlled input (\`value\` is provided) is an anti-pattern. ` +
@@ -112,48 +117,48 @@ const InputPhoneNumber = forwardRef(
112
117
  );
113
118
  }
114
119
  inputRef?.current?.setNativeProps({ text });
115
- _onChangeText(text);
120
+ handleChangeText(text);
116
121
  };
117
122
 
118
123
  useImperativeHandle(ref, () => {
119
124
  return {
120
- clear: onClearText,
125
+ clear: handleClearText,
121
126
  focus: () => inputRef.current?.focus(),
122
127
  blur: () => inputRef.current?.blur(),
123
- setText: _setText,
128
+ setText: handleSetText,
124
129
  };
125
130
  });
126
131
 
127
132
  let inputTextStyles = {};
128
- let dividerHeight = 24;
129
- let size14 = useScaleSize(14);
130
- let size18 = useScaleSize(18);
133
+ let dividerHeight = SMALL_DIVIDER_HEIGHT;
134
+ const smallFontSize = useScaleSize(SMALL_FONT_SIZE);
135
+ const largeFontSize = useScaleSize(LARGE_FONT_SIZE);
131
136
  const semiboldFontFamily = exportFontFamily('semibold');
132
137
  const boldFontFamily = exportFontFamily('bold');
133
138
  let typography: Typography;
134
139
  if (size === 'small') {
135
140
  inputTextStyles = {
136
- fontSize: size14,
141
+ fontSize: smallFontSize,
137
142
  fontFamily: semiboldFontFamily,
138
143
  };
139
- dividerHeight = 24;
144
+ dividerHeight = SMALL_DIVIDER_HEIGHT;
140
145
  typography = 'header_s_semibold';
141
146
  } else {
142
147
  inputTextStyles = {
143
- fontSize: size18,
148
+ fontSize: largeFontSize,
144
149
  fontFamily: boldFontFamily,
145
150
  };
146
- dividerHeight = 32;
151
+ dividerHeight = LARGE_DIVIDER_HEIGHT;
147
152
  typography = 'header_m_bold';
148
153
  }
149
154
 
150
155
  const renderInputView = () => {
151
156
  const disabledColor = theme.colors.text.disable;
152
- const secure = secureTextEntry;
157
+ const isSecure = secureTextEntry;
153
158
  let textColor = theme.colors.text.default;
154
159
  let placeholderColor = theme.colors.text.hint;
155
160
  let iconTintColor = trailingColor ?? theme.colors.text.default;
156
- const borderWidth = focused ? 1.5 : 1;
161
+ const borderWidth = isFocused ? FOCUSED_BORDER_WIDTH : 1;
157
162
  let secureTextStyle = styles.secureTextLarge;
158
163
 
159
164
  if (size === 'small') {
@@ -173,7 +178,7 @@ const InputPhoneNumber = forwardRef(
173
178
  { backgroundColor: theme.colors.background.surface },
174
179
  getSizeStyle(size, undefined, scaleHeight),
175
180
  { borderWidth },
176
- getBorderColor(theme, focused, errorMessage, disabled),
181
+ getBorderColor(theme, isFocused, errorMessage, disabled),
177
182
  ]}
178
183
  >
179
184
  <View style={styles.inputView}>
@@ -204,7 +209,7 @@ const InputPhoneNumber = forwardRef(
204
209
  {...props}
205
210
  accessibilityLabel={`${componentId}`}
206
211
  editable={editable && !disabled}
207
- secureTextEntry={secure}
212
+ secureTextEntry={isSecure}
208
213
  textAlignVertical="center"
209
214
  ref={inputRef}
210
215
  accessibilityState={{
@@ -217,15 +222,15 @@ const InputPhoneNumber = forwardRef(
217
222
  color: textColor,
218
223
  },
219
224
  inputTextStyles,
220
- secure && haveValue && secureTextStyle,
225
+ isSecure && hasValue && secureTextStyle,
221
226
  ]}
222
227
  defaultValue={defaultValue}
223
228
  allowFontScaling={false}
224
229
  textBreakStrategy="highQuality"
225
230
  value={value}
226
- onChangeText={_onChangeText}
227
- onFocus={_onFocus}
228
- onBlur={_onBlur}
231
+ onChangeText={handleChangeText}
232
+ onFocus={handleFocus}
233
+ onBlur={handleBlur}
229
234
  placeholder={placeholder}
230
235
  selectionColor={theme.colors.primary}
231
236
  placeholderTextColor={placeholderColor}
@@ -233,16 +238,16 @@ const InputPhoneNumber = forwardRef(
233
238
  />
234
239
  </View>
235
240
  <View style={styles.iconView}>
236
- {showClearIcon && focused && haveValue && (
241
+ {showClearIcon && isFocused && hasValue && (
237
242
  <TouchableOpacity
238
243
  accessibilityLabel={`${componentId}|clear-icon-touch`}
239
244
  accessible={false}
240
245
  style={styles.iconWrapper}
241
- onPress={onClearText}
246
+ onPress={handleClearText}
242
247
  >
243
248
  <Icon
244
249
  source="24_navigation_close_circle_full"
245
- size={16}
250
+ size={CLEAR_ICON_SIZE}
246
251
  color={theme.colors.text.hint}
247
252
  accessibilityLabel={`${componentId}|clear-icon`}
248
253
  />
@@ -299,9 +304,9 @@ const InputPhoneNumber = forwardRef(
299
304
 
300
305
  const innerStyle = StyleSheet.create({
301
306
  iconFlag: {
302
- width: 24,
303
- height: 24,
304
- marginHorizontal: 2,
307
+ width: FLAG_SIZE,
308
+ height: FLAG_SIZE,
309
+ marginHorizontal: Spacing.XXS,
305
310
  },
306
311
  });
307
312
 
@@ -13,18 +13,18 @@ import {
13
13
  View,
14
14
  } from 'react-native';
15
15
  import { useComponentId } from '../Application';
16
- import { HeaderBarMetrics } from '../Application/headerLayout';
17
16
  import {
18
17
  ApplicationContext,
19
18
  ComponentContext,
20
19
  MiniAppContext,
21
20
  } from '../Context';
22
21
  import { Icon } from '../Icon';
22
+ import { Styles } from '../Consts';
23
23
  import { Text, useScaleSize, exportFontFamily } from '../Text';
24
+ import { CLEAR_ICON_SIZE, INPUT_FONT_SIZE, PRESSED_OPACITY } from './constants';
24
25
  import { InputRef, InputSearchProps } from './index';
25
26
  import { TextTyping } from './SearchTextTyping';
26
27
  import styles from './styles';
27
- import { Styles } from '../Consts';
28
28
  import SystemTextInput from './SystemTextInput';
29
29
 
30
30
  const SEARCH_ICON_SIZE = 24;
@@ -52,29 +52,21 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
52
52
  backgroundColor,
53
53
  onPress,
54
54
  typingData,
55
- size = 'default',
56
55
  ...props
57
56
  },
58
57
  ref,
59
58
  ) => {
60
59
  const { theme } = useContext(ApplicationContext);
61
60
  const context = useContext<any>(MiniAppContext);
62
- const scaledFontSize = useScaleSize(14);
61
+ const scaledFontSize = useScaleSize(INPUT_FONT_SIZE);
63
62
  const searchFontFamily = exportFontFamily('regular');
64
63
 
65
64
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
66
- const isHeaderSize = size === 'header';
67
- const inputHeightStyle = isHeaderSize && {
68
- height: HeaderBarMetrics.searchHeight,
69
- };
70
- const searchIconSize = isHeaderSize
71
- ? HeaderBarMetrics.iconSize
72
- : SEARCH_ICON_SIZE;
73
65
 
74
- const [focused, setFocused] = useState(false);
66
+ const [isFocused, setIsFocused] = useState(false);
75
67
  const [internalText, setInternalText] = useState(defaultValue || '');
76
68
  const currentValue = value !== undefined ? value : internalText;
77
- const haveValue = !!currentValue;
69
+ const hasValue = !!currentValue;
78
70
  const inputRef = useRef<TextInput | null>(null);
79
71
 
80
72
  const componentName = 'InputSearch';
@@ -84,40 +76,40 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
84
76
  props.accessibilityLabel,
85
77
  );
86
78
 
87
- const _onFocus = (e: TextInputFocusEvent) => {
88
- setFocused(true);
79
+ const handleFocus = (e: TextInputFocusEvent) => {
80
+ setIsFocused(true);
89
81
  onFocus?.(e);
90
82
  };
91
83
 
92
- const _onBlur = (e: TextInputFocusEvent) => {
93
- setFocused(false);
84
+ const handleBlur = (e: TextInputFocusEvent) => {
85
+ setIsFocused(false);
94
86
  onBlur?.(e);
95
87
  };
96
88
 
97
- const onClearText = () => {
89
+ const handleClearText = () => {
98
90
  inputRef?.current?.clear();
99
91
  inputRef.current?.setNativeProps({ text: '' });
100
- _onChangeText('');
92
+ handleChangeText('');
101
93
  };
102
94
 
103
- const _onChangeText = (text: string) => {
95
+ const handleChangeText = (text: string) => {
104
96
  if (value === undefined) {
105
97
  setInternalText(text);
106
98
  }
107
99
  onChangeText?.(text);
108
100
  };
109
101
 
110
- const _setText = (text: string) => {
102
+ const handleSetText = (text: string) => {
111
103
  inputRef?.current?.setNativeProps({ text });
112
- _onChangeText(text);
104
+ handleChangeText(text);
113
105
  };
114
106
 
115
107
  useImperativeHandle(ref, () => {
116
108
  return {
117
- clear: onClearText,
109
+ clear: handleClearText,
118
110
  focus: () => inputRef.current?.focus(),
119
111
  blur: () => inputRef.current?.blur(),
120
- setText: _setText,
112
+ setText: handleSetText,
121
113
  };
122
114
  });
123
115
 
@@ -125,13 +117,9 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
125
117
  if (typingData && typingData?.length > 0) {
126
118
  return (
127
119
  <TouchableOpacity
128
- style={[
129
- styles.searchInput,
130
- styles.searchInputTyping,
131
- inputHeightStyle,
132
- ]}
120
+ style={[styles.searchInput, styles.searchInputTyping]}
133
121
  onPress={onPress}
134
- activeOpacity={onPress ? 0.6 : 1}
122
+ activeOpacity={onPress ? PRESSED_OPACITY : 1}
135
123
  >
136
124
  <TextTyping data={typingData} />
137
125
  </TouchableOpacity>
@@ -147,7 +135,6 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
147
135
  ref={inputRef}
148
136
  style={[
149
137
  styles.searchInput,
150
- inputHeightStyle,
151
138
  {
152
139
  fontSize: scaledFontSize,
153
140
  color: theme.colors.text.default,
@@ -155,10 +142,10 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
155
142
  },
156
143
  ]}
157
144
  value={value}
158
- onChangeText={_onChangeText}
145
+ onChangeText={handleChangeText}
159
146
  defaultValue={defaultValue}
160
- onFocus={_onFocus}
161
- onBlur={_onBlur}
147
+ onFocus={handleFocus}
148
+ onBlur={handleBlur}
162
149
  placeholder={placeholder}
163
150
  selectionColor={theme.colors.primary}
164
151
  placeholderTextColor={theme.colors.text.hint}
@@ -169,15 +156,15 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
169
156
  const renderTrailing = () => {
170
157
  return (
171
158
  <View style={Styles.row}>
172
- {haveValue && (
159
+ {hasValue && (
173
160
  <TouchableOpacity
174
161
  style={styles.iconWrapper}
175
- onPress={onClearText}
162
+ onPress={handleClearText}
176
163
  accessibilityLabel={`${componentId}|close-icon-touch`}
177
164
  >
178
165
  <Icon
179
166
  source="24_navigation_close_circle_full"
180
- size={16}
167
+ size={CLEAR_ICON_SIZE}
181
168
  color={theme.colors.text.hint}
182
169
  />
183
170
  </TouchableOpacity>
@@ -208,7 +195,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
208
195
  };
209
196
 
210
197
  let inputState = 'empty';
211
- if (focused) {
198
+ if (isFocused) {
212
199
  inputState = 'focus';
213
200
  }
214
201
  if (value && value?.length > 0) {
@@ -241,7 +228,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
241
228
  >
242
229
  <Icon
243
230
  source={'navigation_search'}
244
- size={searchIconSize}
231
+ size={SEARCH_ICON_SIZE}
245
232
  color={theme.colors.text.hint}
246
233
  />
247
234
  {renderInputView()}
@@ -12,14 +12,20 @@ import {
12
12
  TouchableOpacity,
13
13
  View,
14
14
  } from 'react-native';
15
+ import { setAutomationID, useComponentId } from '../Application';
15
16
  import { ApplicationContext, MiniAppContext } from '../Context';
16
17
  import { Icon } from '../Icon';
17
18
  import { Text, useScaleSize, exportFontFamily } from '../Text';
18
- import { ErrorView, FloatingView, getBorderColor, MAX_LENGTH } from './common';
19
+ import { CLEAR_ICON_SIZE, INPUT_FONT_SIZE } from './constants';
20
+ import { ErrorView } from './ErrorView';
21
+ import { FloatingView } from './FloatingView';
19
22
  import { InputTextAreaProps } from './index';
20
23
  import styles from './styles';
21
24
  import SystemTextInput from './SystemTextInput';
22
- import { setAutomationID, useComponentId } from '../Application';
25
+ import { getBorderColor } from './utils';
26
+
27
+ const DEFAULT_MAX_LENGTH = 300;
28
+ const DEFAULT_HEIGHT = 104;
23
29
 
24
30
  /**
25
31
  * Multiline input with floating label, clear icon and a `length/maxLength`
@@ -40,7 +46,7 @@ const InputTextArea = forwardRef(
40
46
  defaultValue,
41
47
  height,
42
48
  placeholder,
43
- maxLength = MAX_LENGTH,
49
+ maxLength = DEFAULT_MAX_LENGTH,
44
50
  required,
45
51
  errorSpacing,
46
52
  style,
@@ -57,12 +63,12 @@ const InputTextArea = forwardRef(
57
63
  );
58
64
  const { theme } = useContext(ApplicationContext);
59
65
  const context = useContext<any>(MiniAppContext);
60
- const DEFAULT_HEIGHT = useScaleSize(104);
66
+ const defaultHeight = useScaleSize(DEFAULT_HEIGHT);
61
67
 
62
68
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
63
- const scaledFontSize = useScaleSize(14);
69
+ const scaledFontSize = useScaleSize(INPUT_FONT_SIZE);
64
70
  const inputFontFamily = exportFontFamily('regular');
65
- const [focused, setFocused] = useState(false);
71
+ const [isFocused, setIsFocused] = useState(false);
66
72
  const [inputValue, setInputValue] = useState(defaultValue ?? '');
67
73
  const inputRef = useRef<TextInput | null>(null);
68
74
 
@@ -70,33 +76,33 @@ const InputTextArea = forwardRef(
70
76
  setInputValue(value ?? '');
71
77
  }, [value]);
72
78
 
73
- const onClearText = () => {
79
+ const handleClearText = () => {
74
80
  inputRef?.current?.clear();
75
81
  inputRef.current?.setNativeProps({ text: '' });
76
- _onChangeText('');
82
+ handleChangeText('');
77
83
  };
78
84
 
79
- const _onChangeText = (text: string) => {
85
+ const handleChangeText = (text: string) => {
80
86
  onChangeText?.(text);
81
87
  setInputValue(text);
82
88
  };
83
89
 
84
- const _onFocus = (e: TextInputFocusEvent) => {
85
- setFocused(true);
90
+ const handleFocus = (e: TextInputFocusEvent) => {
91
+ setIsFocused(true);
86
92
  onFocus?.(e);
87
93
  };
88
94
 
89
- const _onBlur = (e: TextInputFocusEvent) => {
90
- setFocused(false);
95
+ const handleBlur = (e: TextInputFocusEvent) => {
96
+ setIsFocused(false);
91
97
  onBlur?.(e);
92
98
  };
93
99
 
94
100
  useImperativeHandle(ref, () => {
95
101
  return {
96
- clear: onClearText,
102
+ clear: handleClearText,
97
103
  focus: () => inputRef.current?.focus(),
98
104
  blur: () => inputRef.current?.blur(),
99
- setText: (text: string) => _onChangeText(text),
105
+ setText: (text: string) => handleChangeText(text),
100
106
  };
101
107
  });
102
108
 
@@ -129,10 +135,10 @@ const InputTextArea = forwardRef(
129
135
  style={[
130
136
  styles.textAreaWrapper,
131
137
  {
132
- height: height || DEFAULT_HEIGHT,
138
+ height: height || defaultHeight,
133
139
  backgroundColor: theme.colors.background.surface,
134
140
  },
135
- getBorderColor(theme, focused, errorMessage, disabled),
141
+ getBorderColor(theme, isFocused, errorMessage, disabled),
136
142
  ]}
137
143
  >
138
144
  <FloatingView
@@ -175,23 +181,23 @@ const InputTextArea = forwardRef(
175
181
  textBreakStrategy="highQuality"
176
182
  multiline={true}
177
183
  value={inputValue}
178
- onChangeText={_onChangeText}
179
- onFocus={_onFocus}
180
- onBlur={_onBlur}
184
+ onChangeText={handleChangeText}
185
+ onFocus={handleFocus}
186
+ onBlur={handleBlur}
181
187
  placeholder={placeholder}
182
188
  selectionColor={theme.colors.primary}
183
189
  placeholderTextColor={placeholderColor}
184
190
  />
185
191
  </View>
186
- {focused && inputValue.length > 0 && (
192
+ {isFocused && inputValue.length > 0 && (
187
193
  <TouchableOpacity
188
194
  style={styles.iconWrapper}
189
- onPress={onClearText}
195
+ onPress={handleClearText}
190
196
  accessibilityLabel={`${componentId}|clear-icon-touch`}
191
197
  >
192
198
  <Icon
193
199
  source="24_navigation_close_circle_full"
194
- size={16}
200
+ size={CLEAR_ICON_SIZE}
195
201
  color={theme.colors.text.hint}
196
202
  accessibilityLabel={`${componentId}|clear-icon`}
197
203
  />
@@ -0,0 +1,79 @@
1
+ import React, { type FC, type ReactNode, useContext } from 'react';
2
+ import { TouchableOpacity } from 'react-native';
3
+ import IconSources from '../Assets/icon.json';
4
+ import { ApplicationContext } from '../Context';
5
+ import { Icon } from '../Icon';
6
+ import { Loader } from '../Loader';
7
+ import { Text } from '../Text';
8
+ import styles from './styles';
9
+
10
+ const ICON_SIZE = 24;
11
+ const TEXT_MAX_LENGTH = 15;
12
+
13
+ type IconSourceMap = Record<string, { uri?: string } | undefined>;
14
+
15
+ type TrailingProps = {
16
+ loading?: boolean;
17
+ color?: string;
18
+ icon?: string;
19
+ componentId?: string;
20
+ onPressIcon?: () => void;
21
+ };
22
+
23
+ /**
24
+ * Trailing slot: spinner while `loading`, an icon when `icon` is a known icon
25
+ * name or URL, otherwise `icon` as a text action (first 15 chars).
26
+ */
27
+ const RenderTrailing: FC<TrailingProps> = ({
28
+ loading,
29
+ color,
30
+ icon,
31
+ onPressIcon,
32
+ componentId,
33
+ }) => {
34
+ const { theme } = useContext(ApplicationContext);
35
+ if (loading) {
36
+ return <Loader type={'spinner'} color={color} style={styles.icon} />;
37
+ }
38
+ if (!icon) {
39
+ return null;
40
+ }
41
+
42
+ const iconSources: IconSourceMap = IconSources;
43
+ const isIconName = !!iconSources[icon]?.uri;
44
+ const isIcon = isIconName || icon.includes('http');
45
+ const renderIconTouchable = (content: ReactNode) => {
46
+ return (
47
+ <TouchableOpacity
48
+ accessibilityLabel={`${componentId}|trailing-touch`}
49
+ onPress={onPressIcon}
50
+ style={styles.icon}
51
+ >
52
+ {content}
53
+ </TouchableOpacity>
54
+ );
55
+ };
56
+
57
+ if (isIcon) {
58
+ return renderIconTouchable(
59
+ <Icon
60
+ color={color}
61
+ source={icon}
62
+ size={ICON_SIZE}
63
+ accessibilityLabel={`${componentId}|trailing-icon`}
64
+ />,
65
+ );
66
+ }
67
+ return renderIconTouchable(
68
+ <Text
69
+ typography="action_xs_bold"
70
+ color={color ?? theme.colors.primary}
71
+ numberOfLines={1}
72
+ accessibilityLabel={`${componentId}|trailing-text`}
73
+ >
74
+ {icon.substring(0, TEXT_MAX_LENGTH)}
75
+ </Text>,
76
+ );
77
+ };
78
+
79
+ export { RenderTrailing };
@@ -0,0 +1,8 @@
1
+ /** Input text size before font scaling. */
2
+ const INPUT_FONT_SIZE = 14;
3
+ /** Size of the clear (x) icon shown while a focused field has a value. */
4
+ const CLEAR_ICON_SIZE = 16;
5
+ /** Pressed opacity of the floating icon, dropdown and typing placeholder. */
6
+ const PRESSED_OPACITY = 0.6;
7
+
8
+ export { INPUT_FONT_SIZE, CLEAR_ICON_SIZE, PRESSED_OPACITY };
package/Input/index.tsx CHANGED
@@ -194,12 +194,6 @@ export interface InputSearchProps extends TextInputProps {
194
194
  */
195
195
  typingData?: string[];
196
196
 
197
- /**
198
- * Optional. `header` is the 32pt field with a 20pt search icon that sits in the
199
- * navigation header. Defaults to `default`, the 36pt field for content.
200
- */
201
- size?: 'default' | 'header';
202
-
203
197
  /**
204
198
  * Optional. Called when the field is pressed while showing `typingData`.
205
199
  */
@@ -373,4 +367,4 @@ export {
373
367
  InputPhoneNumber,
374
368
  };
375
369
 
376
- export { checkValidPhoneNumber };
370
+ export {checkValidPhoneNumber};