@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.
@@ -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 [selection, setSelection] = useState<Selection>();
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
- setSelection({ start: caret, end: caret });
244
+ pendingCaretRef.current = caret;
245
+ setCommitCount(count => count + 1);
224
246
  setDisplayValue(display);
225
247
  setNumericValue(digits);
226
- return digits !== numericValue;
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
- const { digits, caretDigit } = text
245
- ? applyEdit(
246
- displayValue,
247
- detectEdit(displayValue, text, selectionRef.current),
248
- digitLimit,
249
- )
250
- : { digits: '', caretDigit: 0 };
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}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.165.2-beta.2",
3
+ "version": "0.165.2-beta.3",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},