@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.
- package/Application/BottomSheet.tsx +113 -14
- package/Application/BottomTab/BottomTabBar.tsx +6 -3
- package/Application/BottomTab/CustomBottomTabItem.tsx +3 -5
- package/Application/BottomTab/DuoBarCompression.tsx +135 -0
- package/Application/BottomTab/DuoTabBar.tsx +537 -0
- package/Application/BottomTab/index.tsx +145 -134
- package/Application/Components/DuoGlassBackground.tsx +158 -0
- package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
- package/Application/Components/HeaderAnimated.tsx +9 -3
- package/Application/Components/HeaderExtendHeader.tsx +70 -28
- package/Application/Components/HeaderLeft.tsx +38 -0
- package/Application/Components/HeaderRight.tsx +144 -9
- package/Application/Components/HeaderTitle.tsx +43 -7
- package/Application/Components/SearchHeader.tsx +20 -15
- package/Application/Components/SideBarButton.tsx +134 -0
- package/Application/ModalScreen.tsx +16 -2
- package/Application/NavigationContainer.tsx +22 -38
- package/Application/Navigator.ts +1 -3
- package/Application/StackScreen.tsx +2 -5
- package/Application/device.ts +29 -0
- package/Application/types.ts +10 -1
- package/Application/utils.tsx +10 -7
- package/Badge/Badge.tsx +2 -3
- package/Badge/BadgeDotAnimation.tsx +3 -3
- package/Button/index.tsx +11 -11
- package/Consts/theme.ts +3 -4
- package/Input/Input.tsx +3 -4
- package/Input/InputMoney.tsx +134 -196
- package/Input/InputOTP.tsx +9 -12
- package/Input/InputPhoneNumber.tsx +3 -4
- package/Input/InputSearch.tsx +17 -14
- package/Input/InputTextArea.tsx +3 -4
- package/Input/SystemTextInput.tsx +2 -3
- package/Input/TextTyping.tsx +11 -6
- package/Input/styles.ts +10 -0
- package/Input/utils.ts +2 -3
- package/Layout/Card.tsx +8 -26
- package/Layout/FloatingButton.tsx +3 -3
- package/Layout/Item.tsx +1 -10
- package/Layout/ReservedRegions.tsx +78 -0
- package/Layout/Screen.tsx +242 -48
- package/Layout/Section.tsx +5 -18
- package/Layout/index.ts +4 -4
- package/Layout/styles.ts +5 -0
- package/Layout/utils.ts +477 -5
- package/Pagination/Dot.tsx +3 -6
- package/Popup/PopupNotify.tsx +33 -22
- package/Popup/PopupPromotion.tsx +21 -8
- package/Popup/utils.ts +32 -0
- package/Radio/index.tsx +2 -3
- package/Text/utils.ts +1 -2
- package/Title/index.tsx +8 -11
- package/package.json +5 -2
- package/Layout/GridSystem.tsx +0 -137
package/Input/InputMoney.tsx
CHANGED
|
@@ -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
|
-
|
|
11
|
+
Platform,
|
|
12
|
+
Pressable,
|
|
13
|
+
Text,
|
|
13
14
|
TextInput,
|
|
14
|
-
|
|
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
|
-
|
|
40
|
-
type Edit = { start: number; end: number; inserted: string };
|
|
38
|
+
const moneyFormatter = new Intl.NumberFormat('vi-VN');
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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(
|
|
90
|
-
old[old.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
|
-
|
|
95
|
-
|
|
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 [
|
|
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
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
121
|
+
caretDigitsAfter.current = undefined;
|
|
122
|
+
setNumericValue(cleanValue);
|
|
123
|
+
updateDisplayValue(cleanValue);
|
|
197
124
|
}
|
|
198
125
|
}
|
|
199
126
|
}, [value]);
|
|
200
127
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
176
|
+
setDisplayValue('');
|
|
177
|
+
setNumericValue('');
|
|
237
178
|
onChangeText?.('');
|
|
238
179
|
};
|
|
239
180
|
|
|
240
181
|
const _onChangeText = (text: string) => {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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:
|
|
213
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
287
214
|
setFocused(true);
|
|
288
215
|
onFocus?.(e);
|
|
289
216
|
};
|
|
290
217
|
|
|
291
|
-
const _onBlur = (e:
|
|
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
|
-
|
|
226
|
+
setDisplayValue('');
|
|
227
|
+
setNumericValue('');
|
|
299
228
|
onChangeText?.('');
|
|
300
229
|
return;
|
|
301
230
|
}
|
|
302
|
-
const
|
|
303
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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={
|
|
304
|
+
placeholder={inputPlaceholder}
|
|
378
305
|
selectionColor={theme.colors.primary}
|
|
379
306
|
placeholderTextColor={placeholderColor}
|
|
380
307
|
keyboardType={'numeric'}
|
|
381
308
|
maxLength={undefined}
|
|
382
309
|
/>
|
|
383
|
-
|
|
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
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -64,8 +64,8 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
64
64
|
opacity: opacity.value,
|
|
65
65
|
}));
|
|
66
66
|
|
|
67
|
-
const
|
|
68
|
-
|
|
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
|
-
|
|
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:
|
|
95
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
97
96
|
setFocused(true);
|
|
98
97
|
onFocus?.(e);
|
|
99
98
|
};
|
|
100
99
|
|
|
101
|
-
const _onBlur = (e:
|
|
100
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
102
101
|
setFocused(false);
|
|
103
102
|
onBlur?.(e);
|
|
104
103
|
};
|
package/Input/InputSearch.tsx
CHANGED
|
@@ -10,9 +10,8 @@ import React, {
|
|
|
10
10
|
useState,
|
|
11
11
|
} from 'react';
|
|
12
12
|
import {
|
|
13
|
-
NativeSyntheticEvent,
|
|
14
13
|
TextInput,
|
|
15
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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:
|
|
173
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
170
174
|
setFocused(true);
|
|
171
175
|
onFocus?.(e);
|
|
172
176
|
};
|
|
173
177
|
|
|
174
|
-
const _onBlur = (e:
|
|
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
|
package/Input/InputTextArea.tsx
CHANGED
|
@@ -7,9 +7,8 @@ import React, {
|
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
9
|
import {
|
|
10
|
-
NativeSyntheticEvent,
|
|
11
10
|
TextInput,
|
|
12
|
-
|
|
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:
|
|
81
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
83
82
|
setFocused(true);
|
|
84
83
|
onFocus?.(e);
|
|
85
84
|
};
|
|
86
85
|
|
|
87
|
-
const _onBlur = (e:
|
|
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
|
|
package/Input/TextTyping.tsx
CHANGED
|
@@ -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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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 = () => {
|