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