@momo-kits/foundation 0.167.2-beta.1 → 0.168.1-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 (54) hide show
  1. package/Application/BottomSheet.tsx +113 -14
  2. package/Application/BottomTab/BottomTabBar.tsx +6 -3
  3. package/Application/BottomTab/CustomBottomTabItem.tsx +3 -5
  4. package/Application/BottomTab/DuoBarCompression.tsx +135 -0
  5. package/Application/BottomTab/DuoTabBar.tsx +537 -0
  6. package/Application/BottomTab/index.tsx +145 -134
  7. package/Application/Components/DuoGlassBackground.tsx +158 -0
  8. package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
  9. package/Application/Components/HeaderAnimated.tsx +9 -3
  10. package/Application/Components/HeaderExtendHeader.tsx +70 -28
  11. package/Application/Components/HeaderLeft.tsx +38 -0
  12. package/Application/Components/HeaderRight.tsx +144 -9
  13. package/Application/Components/HeaderTitle.tsx +43 -7
  14. package/Application/Components/SearchHeader.tsx +20 -15
  15. package/Application/Components/SideBarButton.tsx +134 -0
  16. package/Application/ModalScreen.tsx +16 -2
  17. package/Application/NavigationContainer.tsx +22 -38
  18. package/Application/Navigator.ts +1 -3
  19. package/Application/StackScreen.tsx +2 -5
  20. package/Application/device.ts +29 -0
  21. package/Application/types.ts +10 -1
  22. package/Application/utils.tsx +10 -7
  23. package/Badge/Badge.tsx +2 -3
  24. package/Badge/BadgeDotAnimation.tsx +3 -3
  25. package/Button/index.tsx +11 -11
  26. package/Consts/theme.ts +3 -4
  27. package/Input/Input.tsx +3 -4
  28. package/Input/InputMoney.tsx +134 -196
  29. package/Input/InputOTP.tsx +9 -12
  30. package/Input/InputPhoneNumber.tsx +3 -4
  31. package/Input/InputSearch.tsx +17 -14
  32. package/Input/InputTextArea.tsx +3 -4
  33. package/Input/SystemTextInput.tsx +2 -3
  34. package/Input/TextTyping.tsx +11 -6
  35. package/Input/styles.ts +10 -0
  36. package/Input/utils.ts +2 -3
  37. package/Layout/Card.tsx +8 -26
  38. package/Layout/FloatingButton.tsx +3 -3
  39. package/Layout/Item.tsx +1 -10
  40. package/Layout/ReservedRegions.tsx +78 -0
  41. package/Layout/Screen.tsx +242 -48
  42. package/Layout/Section.tsx +5 -18
  43. package/Layout/index.ts +4 -4
  44. package/Layout/styles.ts +5 -0
  45. package/Layout/utils.ts +477 -5
  46. package/Pagination/Dot.tsx +3 -6
  47. package/Popup/PopupNotify.tsx +33 -22
  48. package/Popup/PopupPromotion.tsx +21 -8
  49. package/Popup/utils.ts +32 -0
  50. package/Radio/index.tsx +2 -3
  51. package/Text/utils.ts +1 -2
  52. package/Title/index.tsx +8 -11
  53. package/package.json +5 -2
  54. package/Layout/GridSystem.tsx +0 -137
@@ -4,15 +4,15 @@ import React, {
4
4
  useContext,
5
5
  useEffect,
6
6
  useImperativeHandle,
7
- useLayoutEffect,
8
7
  useRef,
9
8
  useState,
10
9
  } from 'react';
11
10
  import {
12
- NativeSyntheticEvent,
11
+ Platform,
12
+ Pressable,
13
+ Text,
13
14
  TextInput,
14
- TextInputFocusEventData,
15
- TextInputSelectionChangeEventData,
15
+ TextInputFocusEvent,
16
16
  TouchableOpacity,
17
17
  View,
18
18
  } from 'react-native';
@@ -24,7 +24,6 @@ import {
24
24
  } from '../Context';
25
25
  import { Icon } from '../Icon';
26
26
  import { useScaleSize, exportFontFamily } from '../Text';
27
- import { Spacing } from '../Consts';
28
27
  import {
29
28
  ErrorView,
30
29
  FloatingView,
@@ -36,97 +35,24 @@ import { InputMoneyProps } from './index';
36
35
  import styles from './styles';
37
36
  import SystemTextInput from './SystemTextInput';
38
37
 
39
- type Selection = { start: number; end: number };
40
- type Edit = { start: number; end: number; inserted: string };
38
+ const moneyFormatter = new Intl.NumberFormat('vi-VN');
41
39
 
42
- const countDigits = (text: string) => text.replace(/\D/g, '').length;
43
-
44
- const groupDigits = (digits: string) =>
45
- digits.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
46
-
47
- // Caret position right after the `digitIndex`-th digit of `display`.
48
- const caretFromDigitIndex = (display: string, digitIndex: number) => {
49
- let count = 0;
50
- for (let i = 0; i < display.length && count < digitIndex; i++) {
51
- if (/\d/.test(display[i])) {
52
- count += 1;
53
- if (count === digitIndex) return i + 1;
54
- }
55
- }
56
- return 0;
57
- };
58
-
59
- const detectEdit = (old: string, next: string, selection: Selection): Edit => {
60
- const { start, end } = selection;
61
- const removed = old.length - next.length;
62
- const candidates: Edit[] = [
63
- {
64
- start,
65
- end,
66
- inserted: next.slice(start, next.length - (old.length - end)),
67
- },
68
- { start: start - removed, end: start, inserted: '' },
69
- { start, end: start + removed, inserted: '' },
70
- ];
71
- const match = candidates.find(
72
- e =>
73
- e.start >= 0 &&
74
- e.end >= e.start &&
75
- e.end <= old.length &&
76
- old.slice(0, e.start) + e.inserted + old.slice(e.end) === next,
77
- );
78
- if (match) return match;
79
-
80
- let prefix = 0;
81
- while (
82
- prefix < Math.min(old.length, next.length) &&
83
- old[prefix] === next[prefix]
84
- ) {
85
- prefix += 1;
40
+ // Finds the common prefix and suffix of `old` and `text`; `size` counts the
41
+ // characters removed plus inserted between them.
42
+ const findEdit = (old: string, text: string) => {
43
+ let start = 0;
44
+ while (start < text.length && old[start] === text[start]) {
45
+ start += 1;
86
46
  }
87
47
  let suffix = 0;
88
48
  while (
89
- suffix < Math.min(old.length, next.length) - prefix &&
90
- old[old.length - 1 - suffix] === next[next.length - 1 - suffix]
49
+ suffix < Math.min(text.length, old.length) - start &&
50
+ old[old.length - 1 - suffix] === text[text.length - 1 - suffix]
91
51
  ) {
92
52
  suffix += 1;
93
53
  }
94
- return {
95
- start: prefix,
96
- end: old.length - suffix,
97
- inserted: next.slice(prefix, next.length - suffix),
98
- };
99
- };
100
-
101
- // Applies `edit` to the digits of `display`. Separators and currency are never
102
- // deleted: removing a separator deletes the digit before it instead, removing
103
- // only the currency leaves the digits untouched.
104
- const applyEdit = (display: string, edit: Edit, digitLimit: number) => {
105
- const digits = display.replace(/\D/g, '');
106
- let left = countDigits(display.slice(0, edit.start));
107
- let removed = countDigits(display.slice(edit.start, edit.end));
108
- const inserted = edit.inserted.replace(/\D/g, '');
109
- if (
110
- !removed &&
111
- !inserted &&
112
- left > 0 &&
113
- display.slice(edit.start, edit.end).includes('.')
114
- ) {
115
- left -= 1;
116
- removed = 1;
117
- }
118
- const next = digits.slice(0, left) + inserted + digits.slice(left + removed);
119
- const trimmed = next.replace(/^0+(?=\d)/, '');
120
- if ((!removed && !inserted) || trimmed.length > digitLimit) {
121
- return { digits, caretDigit: left };
122
- }
123
- return {
124
- digits: trimmed,
125
- caretDigit: Math.max(
126
- 0,
127
- left + inserted.length - (next.length - trimmed.length),
128
- ),
129
- };
54
+ const size = old.length + text.length - 2 * (start + suffix);
55
+ return { old, start, suffix, size };
130
56
  };
131
57
 
132
58
  const InputMoney = forwardRef(
@@ -167,14 +93,14 @@ const InputMoney = forwardRef(
167
93
  const [focused, setFocused] = useState(false);
168
94
  const [displayValue, setDisplayValue] = useState('');
169
95
  const [numericValue, setNumericValue] = useState('');
170
- const [commitCount, setCommitCount] = useState(0);
171
- const displayRef = useRef('');
172
- const selectionRef = useRef<Selection>({ start: 0, end: 0 });
173
- // Caret to push to native once the committed text is rendered.
174
- const pendingCaretRef = useRef<number | null>(null);
175
- const pendingSelectionRef = useRef<Selection | null>(null);
176
- const editingRef = useRef(false);
96
+ const [lastDisplayValue, setLastDisplayValue] = useState('');
177
97
  const inputRef = useRef<TextInput>(null);
98
+ const caretDigitsAfter = useRef<number | undefined>(undefined);
99
+ const lastRawText = useRef('');
100
+ const [caretSelection, setCaretSelection] = useState<{
101
+ start: number;
102
+ end: number;
103
+ }>();
178
104
  const componentName = 'InputMoney';
179
105
 
180
106
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
@@ -184,127 +110,126 @@ const InputMoney = forwardRef(
184
110
  props.accessibilityLabel,
185
111
  );
186
112
 
187
- const digitLimit =
188
- typeof (props as any)?.maxLength === 'number'
189
- ? (props as any).maxLength
190
- : Number.POSITIVE_INFINITY;
113
+ const maxLength = (props as any)?.maxLength;
114
+ const hasMaxLength = typeof maxLength === 'number';
115
+ const digitLimit = hasMaxLength ? maxLength : Number.POSITIVE_INFINITY;
191
116
 
192
117
  useEffect(() => {
193
118
  if (value !== undefined && typeof value === 'string') {
194
119
  const cleanValue = value.replace(/\D/g, '').replace(/^0+(?=\d)/, '');
195
120
  if (cleanValue !== numericValue) {
196
- commit(cleanValue, cleanValue.length);
121
+ caretDigitsAfter.current = undefined;
122
+ setNumericValue(cleanValue);
123
+ updateDisplayValue(cleanValue);
197
124
  }
198
125
  }
199
126
  }, [value]);
200
127
 
201
- // Always set the caret imperatively: a controlled `selection` prop is only
202
- // sent to native when it differs from the last native selection, so it is
203
- // skipped whenever a stale selection event already reported the same value.
204
- useLayoutEffect(() => {
205
- const caret = pendingCaretRef.current;
206
- if (caret === null) return;
207
- pendingCaretRef.current = null;
208
- if (inputRef.current?.isFocused()) {
209
- inputRef.current.setSelection(caret, caret);
210
- }
211
- }, [commitCount]);
128
+ useEffect(() => {
129
+ setLastDisplayValue(displayValue);
130
+ }, [displayValue]);
212
131
 
213
- const commit = (digits: string, caretDigit: number) => {
214
- const display = digits ? `${groupDigits(digits)}${currency}` : '';
215
- const caret = caretFromDigitIndex(display, caretDigit);
216
- const changed = digits !== numericValue;
217
- displayRef.current = display;
218
- selectionRef.current = { start: caret, end: caret };
219
- pendingCaretRef.current = caret;
220
- setCommitCount(count => count + 1);
221
- setDisplayValue(display);
222
- setNumericValue(digits);
223
- return changed;
132
+ // The selection only rides along with the render that reformats the text,
133
+ // then it is released so the user can move the caret freely.
134
+ useEffect(() => {
135
+ if (!caretSelection) return;
136
+ // A newer keystroke may already have queued its own selection; keep it.
137
+ setCaretSelection(current =>
138
+ current === caretSelection ? undefined : current,
139
+ );
140
+ }, [caretSelection]);
141
+
142
+ const updateDisplayValue = (numeric: string) => {
143
+ const next = numeric ? moneyFormatter.format(Number(numeric)) : '';
144
+ setDisplayValue(next);
145
+
146
+ // Android moves the caret proportionally when the text is replaced, so
147
+ // it lands before the typed digit once a separator is added. Send the
148
+ // caret together with the text, keeping the same digits after it.
149
+ const digitsAfter = caretDigitsAfter.current;
150
+ caretDigitsAfter.current = undefined;
151
+ if (Platform.OS !== 'android' || digitsAfter === undefined) return;
152
+ let before = parseInputText(next).length - digitsAfter;
153
+ let caret = 0;
154
+ while (before > 0 && caret < next.length) {
155
+ if (/\d/.test(next[caret])) before -= 1;
156
+ caret += 1;
157
+ }
158
+ setCaretSelection({ start: caret, end: caret });
224
159
  };
225
160
 
226
- const parseInputText = (text: string): string => {
227
- if (!text) return '';
228
- const currencyRegex = new RegExp(` ?${currency}$`, 'g');
229
- const textWithoutCurrency = text.replace(currencyRegex, '').trim();
230
- return textWithoutCurrency.replace(/[^\d]/g, '');
161
+ const parseInputText = (text: string): string => text.replace(/\D/g, '');
162
+
163
+ const updateNumericValue = (val: string) => {
164
+ let next = val.replace(/[^\d]/g, '').replace(/^0+(?=\d)/, '');
165
+ if (next.length > digitLimit) next = next.slice(0, digitLimit);
166
+ if (/^\d*$/.test(next)) {
167
+ setNumericValue(next);
168
+ updateDisplayValue(next);
169
+ onChangeText?.(next);
170
+ }
231
171
  };
232
172
 
233
173
  const onClearText = () => {
234
174
  inputRef?.current?.clear();
235
175
  inputRef.current?.setNativeProps({ text: '' });
236
- commit('', 0);
176
+ setDisplayValue('');
177
+ setNumericValue('');
237
178
  onChangeText?.('');
238
179
  };
239
180
 
240
181
  const _onChangeText = (text: string) => {
241
- let digits = '';
242
- let caretDigit = 0;
243
- pendingSelectionRef.current = null;
244
- editingRef.current = true;
245
- setTimeout(() => {
246
- editingRef.current = false;
247
- });
248
- if (text) {
249
- const edit = detectEdit(displayRef.current, text, selectionRef.current);
250
- ({ digits, caretDigit } = applyEdit(
251
- displayRef.current,
252
- edit,
253
- digitLimit,
254
- ));
255
- }
256
- if (commit(digits, caretDigit)) {
257
- onChangeText?.(digits);
182
+ if (!text) {
183
+ lastRawText.current = '';
184
+ setDisplayValue('');
185
+ setNumericValue('');
186
+ onChangeText?.('');
187
+ return;
258
188
  }
259
- };
260
-
261
- // Keeps the caret out of the currency suffix.
262
- const _onSelectionChange = (
263
- e: NativeSyntheticEvent<TextInputSelectionChangeEventData>,
264
- ) => {
265
- props.onSelectionChange?.(e);
266
- if (editingRef.current) return;
267
- const selection = e.nativeEvent.selection;
268
- pendingSelectionRef.current = selection;
269
- setTimeout(() => {
270
- if (pendingSelectionRef.current !== selection) return;
271
- pendingSelectionRef.current = null;
272
- const { start, end } = selection;
273
- const display = displayRef.current;
274
- const max = display ? display.length - currency.length : 0;
275
- const clamped = {
276
- start: Math.min(start, max),
277
- end: Math.min(end, max),
278
- };
279
- selectionRef.current = clamped;
280
- if (clamped.start !== start || clamped.end !== end) {
281
- inputRef.current?.setSelection(clamped.start, clamped.end);
189
+ const digits = parseInputText(text);
190
+ // When typing outpaces JS (slow devices) native rejects the stale
191
+ // formatted text and keeps the raw one, so the edit may apply to either.
192
+ // Pick the one that differs from the new text by a single edit.
193
+ const raw = lastRawText.current;
194
+ lastRawText.current = text;
195
+ const formattedEdit = findEdit(lastDisplayValue, text);
196
+ const rawEdit = findEdit(raw, text);
197
+ const { old, start, suffix } =
198
+ raw && rawEdit.size < formattedEdit.size ? rawEdit : formattedEdit;
199
+ caretDigitsAfter.current = parseInputText(
200
+ text.slice(text.length - suffix),
201
+ ).length;
202
+ // Deleting a separator removes the digit before it.
203
+ if (text.length < old.length && digits === numericValue) {
204
+ const index = parseInputText(text.slice(0, start)).length - 1;
205
+ if (index >= 0) {
206
+ updateNumericValue(digits.slice(0, index) + digits.slice(index + 1));
207
+ return;
282
208
  }
283
- });
209
+ }
210
+ updateNumericValue(digits);
284
211
  };
285
212
 
286
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
213
+ const _onFocus = (e: TextInputFocusEvent) => {
287
214
  setFocused(true);
288
215
  onFocus?.(e);
289
216
  };
290
217
 
291
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
218
+ const _onBlur = (e: TextInputFocusEvent) => {
292
219
  setFocused(false);
293
220
  onBlur?.(e);
294
221
  };
295
222
 
296
223
  const _setText = (text: string) => {
224
+ caretDigitsAfter.current = undefined;
297
225
  if (!text) {
298
- commit('', 0);
226
+ setDisplayValue('');
227
+ setNumericValue('');
299
228
  onChangeText?.('');
300
229
  return;
301
230
  }
302
- const digits = parseInputText(text)
303
- .replace(/^0+(?=\d)/, '')
304
- .slice(0, digitLimit);
305
- if (commit(digits, digits.length)) {
306
- onChangeText?.(digits);
307
- }
231
+ const newNumericValue = parseInputText(text);
232
+ updateNumericValue(newNumericValue);
308
233
  };
309
234
 
310
235
  useImperativeHandle(ref, () => {
@@ -326,6 +251,14 @@ const InputMoney = forwardRef(
326
251
  placeholderColor = disabledColor;
327
252
  iconTintColor = disabledColor;
328
253
  }
254
+ const textStyle = {
255
+ fontSize: scaledFontSize,
256
+ fontFamily: inputFontFamily,
257
+ };
258
+ const placeholderHasCurrency = placeholder.endsWith(currency);
259
+ const inputPlaceholder = placeholderHasCurrency
260
+ ? placeholder.slice(0, -currency.length)
261
+ : placeholder;
329
262
  return (
330
263
  <View
331
264
  style={[
@@ -344,7 +277,11 @@ const InputMoney = forwardRef(
344
277
  onPress={onPressFloatingIcon}
345
278
  componentId={componentId}
346
279
  />
347
- <View style={styles.inputView}>
280
+ <Pressable
281
+ style={[styles.inputView, styles.moneyInputView]}
282
+ onPress={() => inputRef.current?.focus()}
283
+ disabled={disabled}
284
+ >
348
285
  <SystemTextInput
349
286
  {...props}
350
287
  accessibilityLabel={`${componentId}`}
@@ -354,33 +291,34 @@ const InputMoney = forwardRef(
354
291
  disabled: disabled,
355
292
  ...props.accessibilityState,
356
293
  }}
357
- style={[
358
- {
359
- width: '100%',
360
- paddingLeft: Spacing.M,
361
- height: '100%',
362
- fontSize: scaledFontSize,
363
- paddingVertical: Spacing.S,
364
- fontFamily: inputFontFamily,
365
- },
366
- {
367
- color: textColor,
368
- },
369
- ]}
294
+ // No width: the input sizes to its text natively, so the
295
+ // currency follows it without waiting for JS.
296
+ style={[textStyle, styles.moneyTextInput, { color: textColor }]}
370
297
  allowFontScaling={false}
371
298
  textBreakStrategy="highQuality"
372
299
  value={displayValue}
300
+ selection={caretSelection ?? props.selection}
373
301
  onChangeText={_onChangeText}
374
- onSelectionChange={_onSelectionChange}
375
302
  onFocus={_onFocus}
376
303
  onBlur={_onBlur}
377
- placeholder={placeholder}
304
+ placeholder={inputPlaceholder}
378
305
  selectionColor={theme.colors.primary}
379
306
  placeholderTextColor={placeholderColor}
380
307
  keyboardType={'numeric'}
381
308
  maxLength={undefined}
382
309
  />
383
- </View>
310
+ {displayValue || placeholderHasCurrency ? (
311
+ <Text
312
+ style={[
313
+ textStyle,
314
+ { color: displayValue ? textColor : placeholderColor },
315
+ ]}
316
+ allowFontScaling={false}
317
+ >
318
+ {currency}
319
+ </Text>
320
+ ) : null}
321
+ </Pressable>
384
322
  <View style={styles.iconView}>
385
323
  {showClearIcon && focused && displayValue ? (
386
324
  <TouchableOpacity
@@ -64,8 +64,8 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
64
64
  opacity: opacity.value,
65
65
  }));
66
66
 
67
- const spacingStyle = !isNaN(Number(length)) &&
68
- index !== Number(length) - 1 && { marginRight: Spacing.L };
67
+ const hasNextInput = !isNaN(Number(length)) && index !== Number(length) - 1;
68
+ const spacingStyle = hasNextInput && { marginRight: Spacing.L };
69
69
 
70
70
  return (
71
71
  <View style={[Styles.rowCenter, spacingStyle]}>
@@ -162,6 +162,7 @@ const InputOTP = forwardRef(
162
162
 
163
163
  const renderInputs = (inputLength: number) => {
164
164
  const TextInputs: React.ReactNode[] = [];
165
+ const textColors = theme.colors.text;
165
166
  for (let i = 0; i < inputLength; i++) {
166
167
  TextInputs.push(
167
168
  <View key={`otp-input-${i}`}>
@@ -170,9 +171,7 @@ const InputOTP = forwardRef(
170
171
  ) : (
171
172
  <Text
172
173
  style={i !== inputLength - 1 && { marginRight: Spacing.L }}
173
- color={
174
- value[i] ? theme.colors.text.default : theme.colors.text.hint
175
- }
174
+ color={value[i] ? textColors.default : textColors.hint}
176
175
  typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
177
176
  >
178
177
  {value[i] || '-'}
@@ -201,16 +200,14 @@ const InputOTP = forwardRef(
201
200
  return (
202
201
  <View style={Styles.row}>
203
202
  {splitValue.map((letter, index) => {
203
+ const isLast = index === value.length - 1;
204
+ const isTyping = focused && value.length !== MAX_LENGTH;
205
+ const isCompact = fontScale > 1 && value.length > 6;
206
+ const marginRight = isCompact ? Spacing.M : Spacing.L;
204
207
  return (
205
208
  <Text
206
209
  key={`otp-letter-${index}`}
207
- style={
208
- (index !== value.length - 1 ||
209
- (focused && value.length !== MAX_LENGTH)) && {
210
- marginRight:
211
- fontScale > 1 && value.length > 6 ? Spacing.M : Spacing.L,
212
- }
213
- }
210
+ style={(!isLast || isTyping) && { marginRight }}
214
211
  typography={'header_m_bold'}
215
212
  >
216
213
  {letter}
@@ -6,10 +6,9 @@ import React, {
6
6
  useState,
7
7
  } from 'react';
8
8
  import {
9
- NativeSyntheticEvent,
10
9
  StyleSheet,
11
10
  TextInput,
12
- TextInputFocusEventData,
11
+ TextInputFocusEvent,
13
12
  TouchableOpacity,
14
13
  View,
15
14
  } from 'react-native';
@@ -93,12 +92,12 @@ const InputPhoneNumber = forwardRef(
93
92
  onChangeText?.(text);
94
93
  };
95
94
 
96
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
95
+ const _onFocus = (e: TextInputFocusEvent) => {
97
96
  setFocused(true);
98
97
  onFocus?.(e);
99
98
  };
100
99
 
101
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
100
+ const _onBlur = (e: TextInputFocusEvent) => {
102
101
  setFocused(false);
103
102
  onBlur?.(e);
104
103
  };
@@ -10,9 +10,8 @@ import React, {
10
10
  useState,
11
11
  } from 'react';
12
12
  import {
13
- NativeSyntheticEvent,
14
13
  TextInput,
15
- TextInputFocusEventData,
14
+ TextInputFocusEvent,
16
15
  TouchableOpacity,
17
16
  View,
18
17
  } from 'react-native';
@@ -59,9 +58,11 @@ const TextTyping: FC<any> = ({
59
58
  listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
60
59
  };
61
60
 
62
- currentIndex.current >= data?.length
63
- ? handleResetList()
64
- : handleAnimation();
61
+ if (currentIndex.current >= data?.length) {
62
+ handleResetList();
63
+ } else {
64
+ handleAnimation();
65
+ }
65
66
  };
66
67
 
67
68
  const showChar = (char?: string) => {
@@ -82,9 +83,12 @@ const TextTyping: FC<any> = ({
82
83
  }, timeDelayNextChar);
83
84
  };
84
85
 
85
- currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
86
- ? handleResetString()
87
- : handleShowChar();
86
+ const textLength = data?.[currentIndex.current]?.length || 0;
87
+ if (currentCharIndex.current >= textLength) {
88
+ handleResetString();
89
+ } else {
90
+ handleShowChar();
91
+ }
88
92
  };
89
93
 
90
94
  const reset = () => {
@@ -166,12 +170,12 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
166
170
  props.accessibilityLabel,
167
171
  );
168
172
 
169
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
173
+ const _onFocus = (e: TextInputFocusEvent) => {
170
174
  setFocused(true);
171
175
  onFocus?.(e);
172
176
  };
173
177
 
174
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
178
+ const _onBlur = (e: TextInputFocusEvent) => {
175
179
  setFocused(false);
176
180
  onBlur?.(e);
177
181
  };
@@ -303,6 +307,8 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
303
307
  inputState = 'filled';
304
308
  }
305
309
 
310
+ const inputBackground = backgroundColor ?? theme.colors.background.surface;
311
+
306
312
  return (
307
313
  <ComponentContext.Provider
308
314
  value={{
@@ -322,10 +328,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
322
328
  <View
323
329
  style={[
324
330
  styles.searchInputWrapper,
325
- {
326
- backgroundColor:
327
- backgroundColor ?? theme.colors.background.surface,
328
- },
331
+ { backgroundColor: inputBackground },
329
332
  ]}
330
333
  >
331
334
  <Icon
@@ -7,9 +7,8 @@ import React, {
7
7
  useState,
8
8
  } from 'react';
9
9
  import {
10
- NativeSyntheticEvent,
11
10
  TextInput,
12
- TextInputFocusEventData,
11
+ TextInputFocusEvent,
13
12
  TouchableOpacity,
14
13
  View,
15
14
  } from 'react-native';
@@ -79,12 +78,12 @@ const InputTextArea = forwardRef(
79
78
  setInputValue(text);
80
79
  };
81
80
 
82
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
81
+ const _onFocus = (e: TextInputFocusEvent) => {
83
82
  setFocused(true);
84
83
  onFocus?.(e);
85
84
  };
86
85
 
87
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
86
+ const _onBlur = (e: TextInputFocusEvent) => {
88
87
  setFocused(false);
89
88
  onBlur?.(e);
90
89
  };
@@ -17,11 +17,10 @@ const SystemTextInput: FC<SystemTextInputProps> = props => {
17
17
  const needsAndroidWrapper =
18
18
  Platform.OS === 'android' && !editable && (onPressIn || onPressOut);
19
19
 
20
+ const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
20
21
  const inputProps: TextInputProps = {
21
22
  ...props,
22
- ...(needsAndroidWrapper
23
- ? { onPressIn: undefined, onPressOut: undefined }
24
- : null),
23
+ ...(needsAndroidWrapper ? noPressHandlers : null),
25
24
  ...(editable === false ? { accessibilityState: undefined } : null),
26
25
  };
27
26
 
@@ -36,9 +36,11 @@ const TextTyping: FC<TextTypingProps> = ({
36
36
  listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
37
37
  };
38
38
 
39
- currentIndex.current >= data?.length
40
- ? handleResetList()
41
- : handleAnimation();
39
+ if (currentIndex.current >= data?.length) {
40
+ handleResetList();
41
+ } else {
42
+ handleAnimation();
43
+ }
42
44
  };
43
45
 
44
46
  const showChar = (char?: string) => {
@@ -59,9 +61,12 @@ const TextTyping: FC<TextTypingProps> = ({
59
61
  }, timeDelayNextChar);
60
62
  };
61
63
 
62
- currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
63
- ? handleResetString()
64
- : handleShowChar();
64
+ const textLength = data?.[currentIndex.current]?.length || 0;
65
+ if (currentCharIndex.current >= textLength) {
66
+ handleResetString();
67
+ } else {
68
+ handleShowChar();
69
+ }
65
70
  };
66
71
 
67
72
  const reset = () => {