@momo-kits/foundation 0.165.2-beta.2 → 0.167.1-beta.1
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 +53 -160
- package/package.json +1 -1
package/Input/InputMoney.tsx
CHANGED
|
@@ -9,10 +9,8 @@ import React, {
|
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
11
|
NativeSyntheticEvent,
|
|
12
|
-
Platform,
|
|
13
12
|
TextInput,
|
|
14
13
|
TextInputFocusEventData,
|
|
15
|
-
TextInputSelectionChangeEventData,
|
|
16
14
|
TouchableOpacity,
|
|
17
15
|
View,
|
|
18
16
|
} from 'react-native';
|
|
@@ -34,124 +32,9 @@ import {
|
|
|
34
32
|
} from './common';
|
|
35
33
|
import { InputMoneyProps } from './index';
|
|
36
34
|
import styles from './styles';
|
|
35
|
+
import { formatNumberToMoney } from './utils';
|
|
37
36
|
import SystemTextInput from './SystemTextInput';
|
|
38
37
|
|
|
39
|
-
type Selection = { start: number; end: number };
|
|
40
|
-
type Edit = { start: number; end: number; inserted: string };
|
|
41
|
-
|
|
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
|
-
// Finds which range of `old` was replaced to produce `next`. `selection` is
|
|
60
|
-
// the pre-edit selection on iOS and already the post-edit caret on Android,
|
|
61
|
-
// where onSelectionChange fires before onChangeText.
|
|
62
|
-
const detectEdit = (old: string, next: string, selection: Selection): Edit => {
|
|
63
|
-
const { start, end } = selection;
|
|
64
|
-
const removed = old.length - next.length;
|
|
65
|
-
const preEdit: Edit[] = [
|
|
66
|
-
{
|
|
67
|
-
start,
|
|
68
|
-
end,
|
|
69
|
-
inserted: next.slice(start, next.length - (old.length - end)),
|
|
70
|
-
},
|
|
71
|
-
{ start: start - removed, end: start, inserted: '' },
|
|
72
|
-
{ start, end: start + removed, inserted: '' },
|
|
73
|
-
];
|
|
74
|
-
const postEdit: Edit[] = [];
|
|
75
|
-
const tail = next.length - start;
|
|
76
|
-
if (start === end && tail <= old.length) {
|
|
77
|
-
let prefix = 0;
|
|
78
|
-
while (
|
|
79
|
-
prefix < Math.min(start, old.length - tail) &&
|
|
80
|
-
old[prefix] === next[prefix]
|
|
81
|
-
) {
|
|
82
|
-
prefix += 1;
|
|
83
|
-
}
|
|
84
|
-
postEdit.push({
|
|
85
|
-
start: prefix,
|
|
86
|
-
end: old.length - tail,
|
|
87
|
-
inserted: next.slice(prefix, start),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
const candidates =
|
|
91
|
-
Platform.OS === 'android'
|
|
92
|
-
? [...postEdit, ...preEdit]
|
|
93
|
-
: [...preEdit, ...postEdit];
|
|
94
|
-
const match = candidates.find(
|
|
95
|
-
e =>
|
|
96
|
-
e.start >= 0 &&
|
|
97
|
-
e.end >= e.start &&
|
|
98
|
-
e.end <= old.length &&
|
|
99
|
-
old.slice(0, e.start) + e.inserted + old.slice(e.end) === next,
|
|
100
|
-
);
|
|
101
|
-
if (match) return match;
|
|
102
|
-
|
|
103
|
-
let prefix = 0;
|
|
104
|
-
while (
|
|
105
|
-
prefix < Math.min(old.length, next.length) &&
|
|
106
|
-
old[prefix] === next[prefix]
|
|
107
|
-
) {
|
|
108
|
-
prefix += 1;
|
|
109
|
-
}
|
|
110
|
-
let suffix = 0;
|
|
111
|
-
while (
|
|
112
|
-
suffix < Math.min(old.length, next.length) - prefix &&
|
|
113
|
-
old[old.length - 1 - suffix] === next[next.length - 1 - suffix]
|
|
114
|
-
) {
|
|
115
|
-
suffix += 1;
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
start: prefix,
|
|
119
|
-
end: old.length - suffix,
|
|
120
|
-
inserted: next.slice(prefix, next.length - suffix),
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Applies `edit` to the digits of `display`. Separators and currency are never
|
|
125
|
-
// deleted: removing a separator deletes the digit before it instead, removing
|
|
126
|
-
// only the currency leaves the digits untouched.
|
|
127
|
-
const applyEdit = (display: string, edit: Edit, digitLimit: number) => {
|
|
128
|
-
const digits = display.replace(/\D/g, '');
|
|
129
|
-
let left = countDigits(display.slice(0, edit.start));
|
|
130
|
-
let removed = countDigits(display.slice(edit.start, edit.end));
|
|
131
|
-
const inserted = edit.inserted.replace(/\D/g, '');
|
|
132
|
-
if (
|
|
133
|
-
!removed &&
|
|
134
|
-
!inserted &&
|
|
135
|
-
left > 0 &&
|
|
136
|
-
display.slice(edit.start, edit.end).includes('.')
|
|
137
|
-
) {
|
|
138
|
-
left -= 1;
|
|
139
|
-
removed = 1;
|
|
140
|
-
}
|
|
141
|
-
const next = digits.slice(0, left) + inserted + digits.slice(left + removed);
|
|
142
|
-
const trimmed = next.replace(/^0+(?=\d)/, '');
|
|
143
|
-
if ((!removed && !inserted) || trimmed.length > digitLimit) {
|
|
144
|
-
return { digits, caretDigit: left };
|
|
145
|
-
}
|
|
146
|
-
return {
|
|
147
|
-
digits: trimmed,
|
|
148
|
-
caretDigit: Math.max(
|
|
149
|
-
0,
|
|
150
|
-
left + inserted.length - (next.length - trimmed.length),
|
|
151
|
-
),
|
|
152
|
-
};
|
|
153
|
-
};
|
|
154
|
-
|
|
155
38
|
const InputMoney = forwardRef(
|
|
156
39
|
(
|
|
157
40
|
{
|
|
@@ -190,8 +73,7 @@ const InputMoney = forwardRef(
|
|
|
190
73
|
const [focused, setFocused] = useState(false);
|
|
191
74
|
const [displayValue, setDisplayValue] = useState('');
|
|
192
75
|
const [numericValue, setNumericValue] = useState('');
|
|
193
|
-
const [
|
|
194
|
-
const selectionRef = useRef<Selection>({ start: 0, end: 0 });
|
|
76
|
+
const [lastDisplayValue, setLastDisplayValue] = useState('');
|
|
195
77
|
const inputRef = useRef<TextInput>(null);
|
|
196
78
|
const componentName = 'InputMoney';
|
|
197
79
|
|
|
@@ -209,21 +91,25 @@ const InputMoney = forwardRef(
|
|
|
209
91
|
|
|
210
92
|
useEffect(() => {
|
|
211
93
|
if (value !== undefined && typeof value === 'string') {
|
|
212
|
-
const cleanValue = value.replace(/\D/g, '')
|
|
94
|
+
const cleanValue = value.replace(/\D/g, '');
|
|
213
95
|
if (cleanValue !== numericValue) {
|
|
214
|
-
|
|
96
|
+
setNumericValue(cleanValue);
|
|
97
|
+
updateDisplayValue(cleanValue);
|
|
215
98
|
}
|
|
216
99
|
}
|
|
217
100
|
}, [value]);
|
|
218
101
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
setLastDisplayValue(displayValue);
|
|
104
|
+
}, [displayValue]);
|
|
105
|
+
|
|
106
|
+
const updateDisplayValue = (numeric: string) => {
|
|
107
|
+
if (!numeric) {
|
|
108
|
+
setDisplayValue('');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const formatted = formatNumberToMoney(Number(numeric));
|
|
112
|
+
setDisplayValue(formatted ? `${formatted}${currency}` : '');
|
|
227
113
|
};
|
|
228
114
|
|
|
229
115
|
const parseInputText = (text: string): string => {
|
|
@@ -233,35 +119,47 @@ const InputMoney = forwardRef(
|
|
|
233
119
|
return textWithoutCurrency.replace(/[^\d]/g, '');
|
|
234
120
|
};
|
|
235
121
|
|
|
122
|
+
const updateNumericValue = (val: string) => {
|
|
123
|
+
let next = val.replace(/[^\d]/g, '');
|
|
124
|
+
if (next.length > digitLimit) next = next.slice(0, digitLimit);
|
|
125
|
+
if (/^\d*$/.test(next)) {
|
|
126
|
+
setNumericValue(next);
|
|
127
|
+
updateDisplayValue(next);
|
|
128
|
+
onChangeText?.(next);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
236
132
|
const onClearText = () => {
|
|
237
133
|
inputRef?.current?.clear();
|
|
238
134
|
inputRef.current?.setNativeProps({ text: '' });
|
|
239
|
-
|
|
135
|
+
setDisplayValue('');
|
|
136
|
+
setNumericValue('');
|
|
240
137
|
onChangeText?.('');
|
|
241
138
|
};
|
|
242
139
|
|
|
243
140
|
const _onChangeText = (text: string) => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
)
|
|
250
|
-
: { digits: '', caretDigit: 0 };
|
|
251
|
-
if (commit(digits, caretDigit)) {
|
|
252
|
-
onChangeText?.(digits);
|
|
141
|
+
if (!text) {
|
|
142
|
+
setDisplayValue('');
|
|
143
|
+
setNumericValue('');
|
|
144
|
+
onChangeText?.('');
|
|
145
|
+
return;
|
|
253
146
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
147
|
+
if (text.length < lastDisplayValue.length) {
|
|
148
|
+
const lastChar = lastDisplayValue.charAt(lastDisplayValue.length - 1);
|
|
149
|
+
const isRemovingCurrency =
|
|
150
|
+
lastDisplayValue.endsWith(currency) || lastChar === ' ';
|
|
151
|
+
const isRemovingDot = lastChar === '.';
|
|
152
|
+
if (isRemovingCurrency || isRemovingDot) {
|
|
153
|
+
const currentNumericValue = numericValue;
|
|
154
|
+
if (currentNumericValue.length > 0) {
|
|
155
|
+
const newNumericValue = currentNumericValue.slice(0, -1);
|
|
156
|
+
updateNumericValue(newNumericValue);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const newNumericValue = parseInputText(text);
|
|
162
|
+
updateNumericValue(newNumericValue);
|
|
265
163
|
};
|
|
266
164
|
|
|
267
165
|
const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
@@ -276,16 +174,13 @@ const InputMoney = forwardRef(
|
|
|
276
174
|
|
|
277
175
|
const _setText = (text: string) => {
|
|
278
176
|
if (!text) {
|
|
279
|
-
|
|
177
|
+
setDisplayValue('');
|
|
178
|
+
setNumericValue('');
|
|
280
179
|
onChangeText?.('');
|
|
281
180
|
return;
|
|
282
181
|
}
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
.slice(0, digitLimit);
|
|
286
|
-
if (commit(digits, digits.length)) {
|
|
287
|
-
onChangeText?.(digits);
|
|
288
|
-
}
|
|
182
|
+
const newNumericValue = parseInputText(text);
|
|
183
|
+
updateNumericValue(newNumericValue);
|
|
289
184
|
};
|
|
290
185
|
|
|
291
186
|
useImperativeHandle(ref, () => {
|
|
@@ -352,8 +247,6 @@ const InputMoney = forwardRef(
|
|
|
352
247
|
textBreakStrategy="highQuality"
|
|
353
248
|
value={displayValue}
|
|
354
249
|
onChangeText={_onChangeText}
|
|
355
|
-
selection={selection}
|
|
356
|
-
onSelectionChange={_onSelectionChange}
|
|
357
250
|
onFocus={_onFocus}
|
|
358
251
|
onBlur={_onBlur}
|
|
359
252
|
placeholder={placeholder}
|