@momo-kits/foundation 0.165.2-beta.2 → 0.165.2-beta.3
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 +50 -15
- package/package.json +1 -1
package/Input/InputMoney.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
useContext,
|
|
5
5
|
useEffect,
|
|
6
6
|
useImperativeHandle,
|
|
7
|
+
useLayoutEffect,
|
|
7
8
|
useRef,
|
|
8
9
|
useState,
|
|
9
10
|
} from 'react';
|
|
@@ -190,8 +191,14 @@ const InputMoney = forwardRef(
|
|
|
190
191
|
const [focused, setFocused] = useState(false);
|
|
191
192
|
const [displayValue, setDisplayValue] = useState('');
|
|
192
193
|
const [numericValue, setNumericValue] = useState('');
|
|
193
|
-
const [
|
|
194
|
+
const [commitCount, setCommitCount] = useState(0);
|
|
195
|
+
const displayRef = useRef('');
|
|
194
196
|
const selectionRef = useRef<Selection>({ start: 0, end: 0 });
|
|
197
|
+
// Caret to push to native once the committed text is rendered.
|
|
198
|
+
const pendingCaretRef = useRef<number | null>(null);
|
|
199
|
+
// Caret the native input reports for the raw, unformatted text. On iOS that
|
|
200
|
+
// event lands after onChangeText and must not override the formatted caret.
|
|
201
|
+
const rawCaretRef = useRef<number | null>(null);
|
|
195
202
|
const inputRef = useRef<TextInput>(null);
|
|
196
203
|
const componentName = 'InputMoney';
|
|
197
204
|
|
|
@@ -216,14 +223,29 @@ const InputMoney = forwardRef(
|
|
|
216
223
|
}
|
|
217
224
|
}, [value]);
|
|
218
225
|
|
|
226
|
+
// Always set the caret imperatively: a controlled `selection` prop is only
|
|
227
|
+
// sent to native when it differs from the last native selection, so it is
|
|
228
|
+
// skipped whenever a stale selection event already reported the same value.
|
|
229
|
+
useLayoutEffect(() => {
|
|
230
|
+
const caret = pendingCaretRef.current;
|
|
231
|
+
if (caret === null) return;
|
|
232
|
+
pendingCaretRef.current = null;
|
|
233
|
+
if (inputRef.current?.isFocused()) {
|
|
234
|
+
inputRef.current.setSelection(caret, caret);
|
|
235
|
+
}
|
|
236
|
+
}, [commitCount]);
|
|
237
|
+
|
|
219
238
|
const commit = (digits: string, caretDigit: number) => {
|
|
220
239
|
const display = digits ? `${groupDigits(digits)}${currency}` : '';
|
|
221
240
|
const caret = caretFromDigitIndex(display, caretDigit);
|
|
241
|
+
const changed = digits !== numericValue;
|
|
242
|
+
displayRef.current = display;
|
|
222
243
|
selectionRef.current = { start: caret, end: caret };
|
|
223
|
-
|
|
244
|
+
pendingCaretRef.current = caret;
|
|
245
|
+
setCommitCount(count => count + 1);
|
|
224
246
|
setDisplayValue(display);
|
|
225
247
|
setNumericValue(digits);
|
|
226
|
-
return
|
|
248
|
+
return changed;
|
|
227
249
|
};
|
|
228
250
|
|
|
229
251
|
const parseInputText = (text: string): string => {
|
|
@@ -241,13 +263,18 @@ const InputMoney = forwardRef(
|
|
|
241
263
|
};
|
|
242
264
|
|
|
243
265
|
const _onChangeText = (text: string) => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
266
|
+
let digits = '';
|
|
267
|
+
let caretDigit = 0;
|
|
268
|
+
rawCaretRef.current = 0;
|
|
269
|
+
if (text) {
|
|
270
|
+
const edit = detectEdit(displayRef.current, text, selectionRef.current);
|
|
271
|
+
rawCaretRef.current = edit.start + edit.inserted.length;
|
|
272
|
+
({ digits, caretDigit } = applyEdit(
|
|
273
|
+
displayRef.current,
|
|
274
|
+
edit,
|
|
275
|
+
digitLimit,
|
|
276
|
+
));
|
|
277
|
+
}
|
|
251
278
|
if (commit(digits, caretDigit)) {
|
|
252
279
|
onChangeText?.(digits);
|
|
253
280
|
}
|
|
@@ -257,11 +284,20 @@ const InputMoney = forwardRef(
|
|
|
257
284
|
const _onSelectionChange = (
|
|
258
285
|
e: NativeSyntheticEvent<TextInputSelectionChangeEventData>,
|
|
259
286
|
) => {
|
|
260
|
-
const { start, end } = e.nativeEvent.selection;
|
|
261
|
-
selectionRef.current = { start, end };
|
|
262
|
-
const max = displayValue ? displayValue.length - currency.length : 0;
|
|
263
|
-
setSelection({ start: Math.min(start, max), end: Math.min(end, max) });
|
|
264
287
|
props.onSelectionChange?.(e);
|
|
288
|
+
const { start, end } = e.nativeEvent.selection;
|
|
289
|
+
const rawCaret = rawCaretRef.current;
|
|
290
|
+
rawCaretRef.current = null;
|
|
291
|
+
if (start === end && start === rawCaret) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const display = displayRef.current;
|
|
295
|
+
const max = display ? display.length - currency.length : 0;
|
|
296
|
+
const clamped = { start: Math.min(start, max), end: Math.min(end, max) };
|
|
297
|
+
selectionRef.current = clamped;
|
|
298
|
+
if (clamped.start !== start || clamped.end !== end) {
|
|
299
|
+
inputRef.current?.setSelection(clamped.start, clamped.end);
|
|
300
|
+
}
|
|
265
301
|
};
|
|
266
302
|
|
|
267
303
|
const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
@@ -352,7 +388,6 @@ const InputMoney = forwardRef(
|
|
|
352
388
|
textBreakStrategy="highQuality"
|
|
353
389
|
value={displayValue}
|
|
354
390
|
onChangeText={_onChangeText}
|
|
355
|
-
selection={selection}
|
|
356
391
|
onSelectionChange={_onSelectionChange}
|
|
357
392
|
onFocus={_onFocus}
|
|
358
393
|
onBlur={_onBlur}
|