@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.
@@ -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 formatted = numeric ? formatNumberToMoney(Number(numeric)) : '';
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 so a fast keystroke can't land in between.
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
- // 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
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 = text
203
- .slice(text.length - suffix)
204
- .replace(/\D/g, '').length;
205
- if (text.length < old.length) {
206
- const removed = old.slice(start, old.length - suffix);
207
- if (!/\d/.test(removed)) {
208
- const index = removed.includes('.')
209
- ? parseInputText(old.slice(0, start)).length - 1
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
- const newNumericValue = parseInputText(text);
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
- <View style={styles.inputView}>
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
- style={[
293
- {
294
- width: '100%',
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={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
- </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>
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.168.1-beta.6",
3
+ "version": "0.168.1-beta.7",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},