@momo-kits/foundation 0.168.1-beta.6 → 0.168.1-beta.7
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/Input/InputMoney.tsx +47 -44
- package/Input/styles.ts +10 -0
- package/package.json +1 -1
package/Input/InputMoney.tsx
CHANGED
|
@@ -9,6 +9,8 @@ import React, {
|
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
11
|
Platform,
|
|
12
|
+
Pressable,
|
|
13
|
+
Text,
|
|
12
14
|
TextInput,
|
|
13
15
|
TextInputFocusEvent,
|
|
14
16
|
TouchableOpacity,
|
|
@@ -22,7 +24,6 @@ import {
|
|
|
22
24
|
} from '../Context';
|
|
23
25
|
import { Icon } from '../Icon';
|
|
24
26
|
import { useScaleSize, exportFontFamily } from '../Text';
|
|
25
|
-
import { Spacing } from '../Consts';
|
|
26
27
|
import {
|
|
27
28
|
ErrorView,
|
|
28
29
|
FloatingView,
|
|
@@ -32,9 +33,10 @@ import {
|
|
|
32
33
|
} from './common';
|
|
33
34
|
import { InputMoneyProps } from './index';
|
|
34
35
|
import styles from './styles';
|
|
35
|
-
import { formatNumberToMoney } from './utils';
|
|
36
36
|
import SystemTextInput from './SystemTextInput';
|
|
37
37
|
|
|
38
|
+
const moneyFormatter = new Intl.NumberFormat('vi-VN');
|
|
39
|
+
|
|
38
40
|
// Finds the common prefix and suffix of `old` and `text`; `size` counts the
|
|
39
41
|
// characters removed plus inserted between them.
|
|
40
42
|
const findEdit = (old: string, text: string) => {
|
|
@@ -138,13 +140,12 @@ const InputMoney = forwardRef(
|
|
|
138
140
|
}, [caretSelection]);
|
|
139
141
|
|
|
140
142
|
const updateDisplayValue = (numeric: string) => {
|
|
141
|
-
const
|
|
142
|
-
const next = formatted ? `${formatted}${currency}` : '';
|
|
143
|
+
const next = numeric ? moneyFormatter.format(Number(numeric)) : '';
|
|
143
144
|
setDisplayValue(next);
|
|
144
145
|
|
|
145
146
|
// Android moves the caret proportionally when the text is replaced, so
|
|
146
147
|
// it lands before the typed digit once a separator is added. Send the
|
|
147
|
-
// caret together with the text
|
|
148
|
+
// caret together with the text, keeping the same digits after it.
|
|
148
149
|
const digitsAfter = caretDigitsAfter.current;
|
|
149
150
|
caretDigitsAfter.current = undefined;
|
|
150
151
|
if (Platform.OS !== 'android' || digitsAfter === undefined) return;
|
|
@@ -157,12 +158,7 @@ const InputMoney = forwardRef(
|
|
|
157
158
|
setCaretSelection({ start: caret, end: caret });
|
|
158
159
|
};
|
|
159
160
|
|
|
160
|
-
const parseInputText = (text: string): string =>
|
|
161
|
-
if (!text) return '';
|
|
162
|
-
const currencyRegex = new RegExp(` ?${currency}$`, 'g');
|
|
163
|
-
const textWithoutCurrency = text.replace(currencyRegex, '').trim();
|
|
164
|
-
return textWithoutCurrency.replace(/[^\d]/g, '');
|
|
165
|
-
};
|
|
161
|
+
const parseInputText = (text: string): string => text.replace(/\D/g, '');
|
|
166
162
|
|
|
167
163
|
const updateNumericValue = (val: string) => {
|
|
168
164
|
let next = val.replace(/[^\d]/g, '').replace(/^0+(?=\d)/, '');
|
|
@@ -190,7 +186,8 @@ const InputMoney = forwardRef(
|
|
|
190
186
|
onChangeText?.('');
|
|
191
187
|
return;
|
|
192
188
|
}
|
|
193
|
-
|
|
189
|
+
const digits = parseInputText(text);
|
|
190
|
+
// When typing outpaces JS (slow devices) native rejects the stale
|
|
194
191
|
// formatted text and keeps the raw one, so the edit may apply to either.
|
|
195
192
|
// Pick the one that differs from the new text by a single edit.
|
|
196
193
|
const raw = lastRawText.current;
|
|
@@ -199,25 +196,18 @@ const InputMoney = forwardRef(
|
|
|
199
196
|
const rawEdit = findEdit(raw, text);
|
|
200
197
|
const { old, start, suffix } =
|
|
201
198
|
raw && rawEdit.size < formattedEdit.size ? rawEdit : formattedEdit;
|
|
202
|
-
caretDigitsAfter.current =
|
|
203
|
-
.slice(text.length - suffix)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
: numericValue.length - 1;
|
|
211
|
-
if (index >= 0) {
|
|
212
|
-
updateNumericValue(
|
|
213
|
-
numericValue.slice(0, index) + numericValue.slice(index + 1),
|
|
214
|
-
);
|
|
215
|
-
}
|
|
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));
|
|
216
207
|
return;
|
|
217
208
|
}
|
|
218
209
|
}
|
|
219
|
-
|
|
220
|
-
updateNumericValue(newNumericValue);
|
|
210
|
+
updateNumericValue(digits);
|
|
221
211
|
};
|
|
222
212
|
|
|
223
213
|
const _onFocus = (e: TextInputFocusEvent) => {
|
|
@@ -261,6 +251,14 @@ const InputMoney = forwardRef(
|
|
|
261
251
|
placeholderColor = disabledColor;
|
|
262
252
|
iconTintColor = disabledColor;
|
|
263
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;
|
|
264
262
|
return (
|
|
265
263
|
<View
|
|
266
264
|
style={[
|
|
@@ -279,7 +277,11 @@ const InputMoney = forwardRef(
|
|
|
279
277
|
onPress={onPressFloatingIcon}
|
|
280
278
|
componentId={componentId}
|
|
281
279
|
/>
|
|
282
|
-
<
|
|
280
|
+
<Pressable
|
|
281
|
+
style={[styles.inputView, styles.moneyInputView]}
|
|
282
|
+
onPress={() => inputRef.current?.focus()}
|
|
283
|
+
disabled={disabled}
|
|
284
|
+
>
|
|
283
285
|
<SystemTextInput
|
|
284
286
|
{...props}
|
|
285
287
|
accessibilityLabel={`${componentId}`}
|
|
@@ -289,19 +291,9 @@ const InputMoney = forwardRef(
|
|
|
289
291
|
disabled: disabled,
|
|
290
292
|
...props.accessibilityState,
|
|
291
293
|
}}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
paddingLeft: Spacing.M,
|
|
296
|
-
height: '100%',
|
|
297
|
-
fontSize: scaledFontSize,
|
|
298
|
-
paddingVertical: Spacing.S,
|
|
299
|
-
fontFamily: inputFontFamily,
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
color: textColor,
|
|
303
|
-
},
|
|
304
|
-
]}
|
|
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 }]}
|
|
305
297
|
allowFontScaling={false}
|
|
306
298
|
textBreakStrategy="highQuality"
|
|
307
299
|
value={displayValue}
|
|
@@ -309,13 +301,24 @@ const InputMoney = forwardRef(
|
|
|
309
301
|
onChangeText={_onChangeText}
|
|
310
302
|
onFocus={_onFocus}
|
|
311
303
|
onBlur={_onBlur}
|
|
312
|
-
placeholder={
|
|
304
|
+
placeholder={inputPlaceholder}
|
|
313
305
|
selectionColor={theme.colors.primary}
|
|
314
306
|
placeholderTextColor={placeholderColor}
|
|
315
307
|
keyboardType={'numeric'}
|
|
316
308
|
maxLength={undefined}
|
|
317
309
|
/>
|
|
318
|
-
|
|
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>
|
|
319
322
|
<View style={styles.iconView}>
|
|
320
323
|
{showClearIcon && focused && displayValue ? (
|
|
321
324
|
<TouchableOpacity
|
package/Input/styles.ts
CHANGED
|
@@ -218,4 +218,14 @@ export default StyleSheet.create({
|
|
|
218
218
|
marginLeft: Spacing.M,
|
|
219
219
|
},
|
|
220
220
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
221
|
+
moneyInputView: {
|
|
222
|
+
paddingLeft: Spacing.M,
|
|
223
|
+
},
|
|
224
|
+
moneyTextInput: {
|
|
225
|
+
flexShrink: 1,
|
|
226
|
+
height: '100%',
|
|
227
|
+
paddingLeft: 0,
|
|
228
|
+
paddingRight: Spacing.XXS,
|
|
229
|
+
paddingVertical: Spacing.S,
|
|
230
|
+
},
|
|
221
231
|
});
|