@momo-kits/foundation 0.168.1-beta.37 → 0.168.1-beta.39

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 (131) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +44 -34
  3. package/Application/BottomSheet.tsx +35 -26
  4. package/Application/BottomTab/ActiveIndicator.tsx +15 -14
  5. package/Application/BottomTab/BottomTabBar.tsx +49 -62
  6. package/Application/BottomTab/BottomTabInset.tsx +8 -4
  7. package/Application/BottomTab/CurvedContainer.tsx +14 -12
  8. package/Application/BottomTab/CustomBottomTabItem.tsx +13 -13
  9. package/Application/BottomTab/DuoBarCompression.tsx +13 -19
  10. package/Application/BottomTab/DuoRailTabBar.tsx +20 -24
  11. package/Application/BottomTab/DuoTabBar.tsx +8 -10
  12. package/Application/BottomTab/TabBarIcon.tsx +15 -29
  13. package/Application/BottomTab/TabScreen.tsx +1 -5
  14. package/Application/BottomTab/index.tsx +33 -30
  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 +4 -0
  19. package/Application/Components/DuoGlassBackground.tsx +5 -4
  20. package/Application/Components/HeaderAnimated.tsx +40 -12
  21. package/Application/Components/HeaderBackground.tsx +19 -2
  22. package/Application/Components/HeaderExtendHeader.tsx +51 -37
  23. package/Application/Components/HeaderLeft.tsx +5 -18
  24. package/Application/Components/HeaderRight.styles.ts +15 -13
  25. package/Application/Components/HeaderRight.tsx +2 -1
  26. package/Application/Components/HeaderTitle.shared.ts +17 -16
  27. package/Application/Components/HeaderTitle.tsx +4 -3
  28. package/Application/Components/HeaderToolkitAction.tsx +29 -57
  29. package/Application/Components/NavigationButton.tsx +10 -6
  30. package/Application/Components/SearchHeader.tsx +37 -17
  31. package/Application/Components/TitleJourney.tsx +2 -2
  32. package/Application/Components/TitleLocation.tsx +2 -2
  33. package/Application/Components/TitleUser.tsx +2 -2
  34. package/Application/Components/useToolkitActions.tsx +17 -32
  35. package/Application/Localize.ts +7 -0
  36. package/Application/ModalScreen.tsx +9 -3
  37. package/Application/Navigation.ts +14 -16
  38. package/Application/NavigationContainer.tsx +19 -16
  39. package/Application/Navigator.ts +20 -48
  40. package/Application/ScaleSizeProvider.tsx +13 -6
  41. package/Application/StackScreen.tracking.ts +11 -26
  42. package/Application/StackScreen.tsx +17 -35
  43. package/Application/TooltipPortal.tsx +12 -5
  44. package/Application/WidgetContainer.tsx +10 -4
  45. package/Application/headerLayout.ts +158 -0
  46. package/Application/index.ts +11 -1
  47. package/Application/modalLayout.ts +13 -14
  48. package/Application/navigationOptions.tsx +27 -32
  49. package/Application/types.header.ts +7 -2
  50. package/Application/types.ts +7 -0
  51. package/Badge/Badge.tsx +5 -0
  52. package/Badge/BadgeDot.tsx +1 -0
  53. package/Badge/BadgeDotAnimation.tsx +15 -7
  54. package/Badge/BadgeRibbon.tsx +46 -38
  55. package/Badge/Shape.tsx +2 -0
  56. package/Badge/types.ts +2 -2
  57. package/Button/index.tsx +5 -16
  58. package/Button/utils.ts +9 -15
  59. package/CheckBox/index.tsx +4 -0
  60. package/Consts/theme.ts +10 -10
  61. package/Context/index.ts +5 -1
  62. package/Divider/DashDivider.tsx +1 -0
  63. package/Divider/index.tsx +4 -0
  64. package/FoundationList/index.tsx +4 -0
  65. package/Icon/index.tsx +6 -6
  66. package/Icon/types.ts +1 -1
  67. package/IconButton/index.tsx +4 -12
  68. package/Image/index.tsx +9 -7
  69. package/Input/Input.tsx +8 -9
  70. package/Input/InputDropDown.tsx +10 -12
  71. package/Input/InputMoney.tsx +12 -8
  72. package/Input/InputMoneyView.tsx +14 -7
  73. package/Input/InputOTP.tsx +35 -33
  74. package/Input/InputPhoneNumber.tsx +8 -9
  75. package/Input/InputSearch.tsx +22 -8
  76. package/Input/InputTextArea.tsx +4 -0
  77. package/Input/SearchTextTyping.tsx +4 -0
  78. package/Input/SystemTextInput.tsx +7 -2
  79. package/Input/TextTyping.tsx +4 -0
  80. package/Input/common.tsx +26 -12
  81. package/Input/index.tsx +24 -21
  82. package/Input/moneyUtils.ts +10 -4
  83. package/Input/styles.ts +0 -5
  84. package/Input/utils.ts +14 -12
  85. package/Layout/Card.tsx +7 -4
  86. package/Layout/FloatingButton.tsx +4 -0
  87. package/Layout/Item.tsx +6 -2
  88. package/Layout/ItemList.tsx +9 -7
  89. package/Layout/ItemSectionList.tsx +2 -1
  90. package/Layout/ReservedRegions.tsx +25 -25
  91. package/Layout/Screen.tsx +59 -58
  92. package/Layout/Section.tsx +8 -5
  93. package/Layout/TrackingScope.tsx +1 -0
  94. package/Layout/screenContent.tsx +4 -2
  95. package/Layout/screenFooter.tsx +28 -18
  96. package/Layout/screenHeader.tsx +88 -82
  97. package/Layout/screenQuickReturn.tsx +17 -20
  98. package/Layout/screenScroll.ts +4 -7
  99. package/Layout/screenTypes.ts +13 -4
  100. package/Layout/types.ts +2 -4
  101. package/Layout/utils.ts +31 -37
  102. package/Loader/DotLoader.tsx +4 -0
  103. package/Loader/Loader.tsx +4 -0
  104. package/Loader/ProgressBar.tsx +1 -0
  105. package/Loader/Spinner.tsx +4 -0
  106. package/Loader/utils.ts +11 -7
  107. package/Pagination/Dot.tsx +4 -0
  108. package/Pagination/PaginationDot.tsx +1 -0
  109. package/Pagination/PaginationNumber.tsx +5 -3
  110. package/Pagination/PaginationScroll.tsx +5 -0
  111. package/Pagination/PaginationWhiteDot.tsx +1 -0
  112. package/Pagination/index.tsx +4 -0
  113. package/Pagination/types.ts +2 -2
  114. package/Popup/PopupNotify.tsx +44 -48
  115. package/Popup/PopupPromotion.tsx +4 -7
  116. package/Popup/types.ts +4 -4
  117. package/Popup/utils.ts +4 -0
  118. package/Radio/index.tsx +4 -0
  119. package/Skeleton/index.tsx +8 -0
  120. package/Switch/index.tsx +1 -0
  121. package/Tag/index.tsx +5 -1
  122. package/Tag/types.ts +3 -3
  123. package/Text/index.tsx +14 -3
  124. package/Text/styles.ts +1 -1
  125. package/Text/types.ts +1 -6
  126. package/Text/utils.ts +20 -4
  127. package/Title/index.tsx +41 -32
  128. package/ViewBoundary/index.tsx +4 -2
  129. package/index.ts +0 -6
  130. package/package.json +37 -37
  131. package/Application/__tests__/modalLayout.test.ts +0 -128
@@ -41,6 +41,10 @@ export interface IconButtonProps extends TouchableOpacityProps {
41
41
  params?: any;
42
42
  }
43
43
 
44
+ /**
45
+ * Icon-only button. `type` sets the background and icon color; `'disabled'`
46
+ * also blocks presses.
47
+ */
44
48
  const IconButton: React.FC<IconButtonProps> = ({
45
49
  type = 'primary',
46
50
  icon,
@@ -54,9 +58,6 @@ const IconButton: React.FC<IconButtonProps> = ({
54
58
 
55
59
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
56
60
 
57
- /**
58
- * get size icon button
59
- */
60
61
  const getSizeStyle = () => {
61
62
  switch (size) {
62
63
  case 'small':
@@ -68,16 +69,10 @@ const IconButton: React.FC<IconButtonProps> = ({
68
69
  }
69
70
  };
70
71
 
71
- /**
72
- * get shape icon button
73
- */
74
72
  const getShapeStyle = () => {
75
73
  return shape === 'square' ? styles.square : styles.circle;
76
74
  };
77
75
 
78
- /**
79
- * get style for icon button by type
80
- */
81
76
  const getTypeStyle = () => {
82
77
  switch (type) {
83
78
  case 'disabled':
@@ -111,9 +106,6 @@ const IconButton: React.FC<IconButtonProps> = ({
111
106
  }
112
107
  };
113
108
 
114
- /**
115
- * get color for icon
116
- */
117
109
  const getIconColor = () => {
118
110
  switch (type) {
119
111
  case 'disabled':
package/Image/index.tsx CHANGED
@@ -15,6 +15,7 @@ import { ImageProps } from './types';
15
15
 
16
16
  type Status = 'success' | 'loading' | 'error';
17
17
 
18
+ /** Last path segment of a `{ uri }` source, used in QA labels; '' otherwise. */
18
19
  export const getImageName = (source: any): string => {
19
20
  const parts = source?.uri?.split?.('/');
20
21
  if (parts?.length > 0) {
@@ -23,6 +24,10 @@ export const getImageName = (source: any): string => {
23
24
  return '';
24
25
  };
25
26
 
27
+ /**
28
+ * FastImage that shows a skeleton while loading (if `loading`) or inside a
29
+ * loading `SkeletonProvider`, and a fail icon on error.
30
+ */
26
31
  const Image: React.FC<ImageProps> = ({
27
32
  style,
28
33
  source,
@@ -44,15 +49,12 @@ const Image: React.FC<ImageProps> = ({
44
49
 
45
50
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
46
51
 
47
- let accessibilityLabel: any = `img|${getImageName(source)}`;
52
+ const imageName = getImageName(source);
53
+ let accessibilityLabel: any = `img|${imageName}`;
48
54
  if (component?.componentId) {
49
- accessibilityLabel = `${component?.componentId}|img|${getImageName(
50
- source,
51
- )}`;
55
+ accessibilityLabel = `${component?.componentId}|img|${imageName}`;
52
56
  }
53
- /**
54
- * render content loading | fail | rendered
55
- */
57
+
56
58
  const renderContent = () => {
57
59
  if (status === 'loading' || status === 'error') {
58
60
  let content = <Skeleton />;
package/Input/Input.tsx CHANGED
@@ -31,7 +31,8 @@ import {
31
31
  } from '../Context';
32
32
 
33
33
  /**
34
- * Input default component
34
+ * Text input with floating label, leading/trailing/clear icons and an error or
35
+ * hint row; controlled via `value` or uncontrolled via `defaultValue`.
35
36
  */
36
37
  const Input = forwardRef(
37
38
  (
@@ -131,15 +132,17 @@ const Input = forwardRef(
131
132
  };
132
133
  });
133
134
 
134
- /**
135
- * Render input view
136
- */
137
135
  const renderInputView = () => {
138
136
  const disabledColor = theme.colors.text.disable;
139
137
  const secure = secureTextEntry;
140
138
  let textColor = theme.colors.text.default;
141
139
  let placeholderColor = theme.colors.text.hint;
142
140
  let iconTintColor = trailingColor;
141
+ let secureTextStyle = styles.secureTextLarge;
142
+
143
+ if (size === 'small') {
144
+ secureTextStyle = styles.secureTextSmall;
145
+ }
143
146
 
144
147
  if (disabled) {
145
148
  textColor = disabledColor;
@@ -199,11 +202,7 @@ const Input = forwardRef(
199
202
  color: textColor,
200
203
  fontFamily: exportFontFamily(fontWeight ?? 'regular'),
201
204
  },
202
- secure &&
203
- haveValue &&
204
- (size === 'small'
205
- ? styles.secureTextSmall
206
- : styles.secureTextLarge),
205
+ secure && haveValue && secureTextStyle,
207
206
  ]}
208
207
  defaultValue={defaultValue}
209
208
  allowFontScaling={false}
@@ -1,5 +1,5 @@
1
1
  import React, { useContext } from 'react';
2
- import { TouchableOpacity, View } from 'react-native';
2
+ import { TouchableOpacity, View, type ViewStyle } from 'react-native';
3
3
  import { useComponentId } from '../Application';
4
4
  import {
5
5
  ApplicationContext,
@@ -18,6 +18,10 @@ import { Text, useScaleSize } from '../Text';
18
18
  import styles from './styles';
19
19
  import { Spacing } from '../Consts';
20
20
 
21
+ /**
22
+ * Pressable field styled like an input with a chevron; shows `value` or
23
+ * `placeholder`. Opening the picker is left to `onPress`.
24
+ */
21
25
  const InputDropDown = ({
22
26
  value,
23
27
  floatingValue,
@@ -50,18 +54,19 @@ const InputDropDown = ({
50
54
  props.accessibilityLabel,
51
55
  );
52
56
 
53
- /**
54
- * Render the input view
55
- */
56
57
  const renderInputView = () => {
57
58
  const disabledColor = theme.colors.text.disable;
58
59
  let textColor = theme.colors.text.default;
59
60
  let placeholderColor = theme.colors.text.hint;
61
+ let chevronStyle: ViewStyle = styles.iconViewDropDownSingle;
60
62
 
61
63
  if (disabled) {
62
64
  textColor = disabledColor;
63
65
  placeholderColor = disabledColor;
64
66
  }
67
+ if (multiline) {
68
+ chevronStyle = styles.iconViewDropDownMultiline;
69
+ }
65
70
 
66
71
  return (
67
72
  <View
@@ -111,14 +116,7 @@ const InputDropDown = ({
111
116
  </Text>
112
117
  </View>
113
118
  </View>
114
- <View
115
- style={[
116
- styles.iconViewDropDown,
117
- multiline
118
- ? styles.iconViewDropDownMultiline
119
- : styles.iconViewDropDownSingle,
120
- ]}
121
- >
119
+ <View style={[styles.iconViewDropDown, chevronStyle]}>
122
120
  <Icon source={'arrow_chevron_down_small'} />
123
121
  </View>
124
122
  </View>
@@ -16,6 +16,11 @@ import { InputMoneyView } from './InputMoneyView';
16
16
  import { findEdit, moneyFormatter } from './moneyUtils';
17
17
  import styles from './styles';
18
18
 
19
+ /**
20
+ * Money input: keeps digits only, shows them grouped with a currency suffix and
21
+ * reports raw digits to `onChangeText`. On Android it restores the caret by the
22
+ * count of digits after it, since regrouping shifts positions.
23
+ */
19
24
  const InputMoney = forwardRef(
20
25
  (
21
26
  {
@@ -88,9 +93,9 @@ const InputMoney = forwardRef(
88
93
 
89
94
  useEffect(() => {
90
95
  if (!caretSelection) return;
91
- setCaretSelection(current =>
92
- current === caretSelection ? undefined : current,
93
- );
96
+ setCaretSelection(current => {
97
+ return current === caretSelection ? undefined : current;
98
+ });
94
99
  }, [caretSelection]);
95
100
 
96
101
  const updateDisplayValue = (numeric: string) => {
@@ -142,11 +147,10 @@ const InputMoney = forwardRef(
142
147
  lastRawText.current = text;
143
148
  const formattedEdit = findEdit(lastDisplayValue, text);
144
149
  const rawEdit = findEdit(raw, text);
145
- const { old, start, suffix } =
146
- raw && rawEdit.size < formattedEdit.size ? rawEdit : formattedEdit;
147
- caretDigitsAfter.current = parseInputText(
148
- text.slice(text.length - suffix),
149
- ).length;
150
+ const isRawEditSmaller = !!raw && rawEdit.size < formattedEdit.size;
151
+ const { old, start, suffix } = isRawEditSmaller ? rawEdit : formattedEdit;
152
+ const textAfterEdit = text.slice(text.length - suffix);
153
+ caretDigitsAfter.current = parseInputText(textAfterEdit).length;
150
154
  if (text.length < old.length && digits === numericValue) {
151
155
  const index = parseInputText(text.slice(0, start)).length - 1;
152
156
  if (index >= 0) {
@@ -52,6 +52,10 @@ 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
+ */
55
59
  export const InputMoneyView = ({
56
60
  size,
57
61
  errorMessage,
@@ -98,9 +102,12 @@ export const InputMoneyView = ({
98
102
  fontFamily: inputFontFamily,
99
103
  };
100
104
  const placeholderHasCurrency = placeholder.endsWith(currency);
101
- const inputPlaceholder = placeholderHasCurrency
102
- ? placeholder.slice(0, -currency.length)
103
- : placeholder;
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
+ }
104
111
  return (
105
112
  <View
106
113
  style={[
@@ -147,7 +154,7 @@ export const InputMoneyView = ({
147
154
  keyboardType={'numeric'}
148
155
  maxLength={undefined}
149
156
  />
150
- {displayValue || placeholderHasCurrency ? (
157
+ {shouldShowCurrency && (
151
158
  <Text
152
159
  style={[
153
160
  textStyle,
@@ -157,10 +164,10 @@ export const InputMoneyView = ({
157
164
  >
158
165
  {currency}
159
166
  </Text>
160
- ) : null}
167
+ )}
161
168
  </Pressable>
162
169
  <View style={styles.iconView}>
163
- {showClearIcon && focused && displayValue ? (
170
+ {shouldShowClearIcon && (
164
171
  <TouchableOpacity
165
172
  accessibilityLabel={`${componentId}|clear-icon-touch`}
166
173
  style={styles.iconWrapper}
@@ -173,7 +180,7 @@ export const InputMoneyView = ({
173
180
  accessibilityLabel={`${componentId}|clear-icon`}
174
181
  />
175
182
  </TouchableOpacity>
176
- ) : null}
183
+ )}
177
184
  <RenderTrailing
178
185
  color={iconTintColor}
179
186
  icon={trailing}
@@ -38,6 +38,7 @@ import {
38
38
  MiniAppContext,
39
39
  } from '../Context';
40
40
 
41
+ /** Blinking caret shown in the OTP slot being typed. */
41
42
  const OTPCaret: FC<CaretProps> = ({ index, length }) => {
42
43
  const DURATION = 300;
43
44
  const { theme } = useContext(ApplicationContext);
@@ -60,9 +61,7 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
60
61
  };
61
62
  }, [opacity]);
62
63
 
63
- const animatedStyle = useAnimatedStyle(() => ({
64
- opacity: opacity.value,
65
- }));
64
+ const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
66
65
 
67
66
  const hasNextInput = !isNaN(Number(length)) && index !== Number(length) - 1;
68
67
  const spacingStyle = hasNextInput && { marginRight: Spacing.L };
@@ -86,6 +85,10 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
86
85
  );
87
86
  };
88
87
 
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
+ */
89
92
  const InputOTP = forwardRef(
90
93
  (
91
94
  {
@@ -121,14 +124,16 @@ const InputOTP = forwardRef(
121
124
  props.accessibilityLabel,
122
125
  );
123
126
 
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
- }));
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
+ });
132
137
 
133
138
  const onFocusInput = (e: TextInputFocusEvent) => {
134
139
  setFocused(true);
@@ -142,13 +147,10 @@ const InputOTP = forwardRef(
142
147
 
143
148
  const _onChangeText = (text: string) => {
144
149
  if (text.length > MAX_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;
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;
152
154
 
153
155
  setValue(text);
154
156
  onChangeText?.(text);
@@ -164,21 +166,21 @@ const InputOTP = forwardRef(
164
166
  const TextInputs: React.ReactNode[] = [];
165
167
  const textColors = theme.colors.text;
166
168
  for (let i = 0; i < inputLength; i++) {
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
- );
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>);
182
184
  }
183
185
  return TextInputs;
184
186
  };
@@ -34,7 +34,8 @@ import { Image } from '../Image';
34
34
  import { Typography } from '../Text/types';
35
35
 
36
36
  /**
37
- * Input default component
37
+ * Vietnamese phone number input with a fixed flag and `+84` prefix, number pad
38
+ * by default, plus clear and trailing icons.
38
39
  */
39
40
  const InputPhoneNumber = forwardRef(
40
41
  (
@@ -146,9 +147,6 @@ const InputPhoneNumber = forwardRef(
146
147
  typography = 'header_m_bold';
147
148
  }
148
149
 
149
- /**
150
- * Render input view
151
- */
152
150
  const renderInputView = () => {
153
151
  const disabledColor = theme.colors.text.disable;
154
152
  const secure = secureTextEntry;
@@ -156,6 +154,11 @@ const InputPhoneNumber = forwardRef(
156
154
  let placeholderColor = theme.colors.text.hint;
157
155
  let iconTintColor = trailingColor ?? theme.colors.text.default;
158
156
  const borderWidth = focused ? 1.5 : 1;
157
+ let secureTextStyle = styles.secureTextLarge;
158
+
159
+ if (size === 'small') {
160
+ secureTextStyle = styles.secureTextSmall;
161
+ }
159
162
 
160
163
  if (disabled) {
161
164
  textColor = disabledColor;
@@ -214,11 +217,7 @@ const InputPhoneNumber = forwardRef(
214
217
  color: textColor,
215
218
  },
216
219
  inputTextStyles,
217
- secure &&
218
- haveValue &&
219
- (size === 'small'
220
- ? styles.secureTextSmall
221
- : styles.secureTextLarge),
220
+ secure && haveValue && secureTextStyle,
222
221
  ]}
223
222
  defaultValue={defaultValue}
224
223
  allowFontScaling={false}
@@ -13,6 +13,7 @@ import {
13
13
  View,
14
14
  } from 'react-native';
15
15
  import { useComponentId } from '../Application';
16
+ import { HeaderBarMetrics } from '../Application/headerLayout';
16
17
  import {
17
18
  ApplicationContext,
18
19
  ComponentContext,
@@ -26,6 +27,12 @@ import styles from './styles';
26
27
  import { Styles } from '../Consts';
27
28
  import SystemTextInput from './SystemTextInput';
28
29
 
30
+ const SEARCH_ICON_SIZE = 24;
31
+
32
+ /**
33
+ * Search field with search icon, clear icon, optional trailing icon and a
34
+ * cancel button; with `typingData` it shows an animated placeholder instead.
35
+ */
29
36
  const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
30
37
  {
31
38
  placeholder,
@@ -45,6 +52,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
45
52
  backgroundColor,
46
53
  onPress,
47
54
  typingData,
55
+ size = 'default',
48
56
  ...props
49
57
  },
50
58
  ref,
@@ -55,6 +63,13 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
55
63
  const searchFontFamily = exportFontFamily('regular');
56
64
 
57
65
  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;
58
73
 
59
74
  const [focused, setFocused] = useState(false);
60
75
  const [internalText, setInternalText] = useState(defaultValue || '');
@@ -106,14 +121,15 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
106
121
  };
107
122
  });
108
123
 
109
- /**
110
- * Render the input view
111
- */
112
124
  const renderInputView = () => {
113
125
  if (typingData && typingData?.length > 0) {
114
126
  return (
115
127
  <TouchableOpacity
116
- style={[styles.searchInput, styles.searchInputTyping]}
128
+ style={[
129
+ styles.searchInput,
130
+ styles.searchInputTyping,
131
+ inputHeightStyle,
132
+ ]}
117
133
  onPress={onPress}
118
134
  activeOpacity={onPress ? 0.6 : 1}
119
135
  >
@@ -131,6 +147,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
131
147
  ref={inputRef}
132
148
  style={[
133
149
  styles.searchInput,
150
+ inputHeightStyle,
134
151
  {
135
152
  fontSize: scaledFontSize,
136
153
  color: theme.colors.text.default,
@@ -149,9 +166,6 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
149
166
  );
150
167
  };
151
168
 
152
- /**
153
- * Render trailing icon or text
154
- */
155
169
  const renderTrailing = () => {
156
170
  return (
157
171
  <View style={Styles.row}>
@@ -227,7 +241,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
227
241
  >
228
242
  <Icon
229
243
  source={'navigation_search'}
230
- size={24}
244
+ size={searchIconSize}
231
245
  color={theme.colors.text.hint}
232
246
  />
233
247
  {renderInputView()}
@@ -21,6 +21,10 @@ 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
+ */
24
28
  const InputTextArea = forwardRef(
25
29
  (
26
30
  {
@@ -4,6 +4,10 @@ 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
+ */
7
11
  export const TextTyping: FC<any> = ({
8
12
  data = [],
9
13
  timeDelayEnd = 2000,
@@ -11,11 +11,16 @@ 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
+ */
14
18
  const SystemTextInput: FC<SystemTextInputProps> = props => {
15
19
  const { editable, onPressIn, onPressOut, ref } = props;
16
20
 
17
- const needsAndroidWrapper =
18
- Platform.OS === 'android' && !editable && (onPressIn || onPressOut);
21
+ const isAndroidReadOnly = Platform.OS === 'android' && !editable;
22
+ const hasPressHandler = !!(onPressIn || onPressOut);
23
+ const needsAndroidWrapper = isAndroidReadOnly && hasPressHandler;
19
24
 
20
25
  const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
21
26
  const inputProps: TextInputProps = {
@@ -11,6 +11,10 @@ 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
+ */
14
18
  const TextTyping: FC<TextTypingProps> = ({
15
19
  data = [],
16
20
  timeDelayEnd = 2000,