@momo-kits/foundation 0.168.1-rn862.1 → 0.168.2-rn.88.0
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/Application/StackScreen.tsx +7 -2
- package/Input/InputMoney.tsx +186 -128
- package/Input/styles.ts +0 -10
- package/package.json +15 -15
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useContext, useEffect, useLayoutEffect, useRef } from 'react';
|
|
2
|
-
import { Alert
|
|
2
|
+
import { Alert } from 'react-native';
|
|
3
3
|
import { useHeaderHeight } from '@react-navigation/elements';
|
|
4
4
|
import { ScreenTrackingParams } from './types';
|
|
5
5
|
import Navigation from './Navigation';
|
|
@@ -8,7 +8,12 @@ import { version } from '../package.json';
|
|
|
8
8
|
import { SwipeBackHaptic, useAppState } from './utils';
|
|
9
9
|
import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// RN 0.87+: InteractionManager đã bị xoá khỏi react-native → thay bằng requestIdleCallback.
|
|
12
|
+
// Giữ nguyên shape trả về { cancel } để code bên dưới (timeoutInteraction?.cancel?.()) không đổi.
|
|
13
|
+
const runAfterInteractions = (task: () => void) => {
|
|
14
|
+
const handle = requestIdleCallback(() => task());
|
|
15
|
+
return { cancel: () => cancelIdleCallback(handle) };
|
|
16
|
+
};
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* container for stack screen
|
package/Input/InputMoney.tsx
CHANGED
|
@@ -4,15 +4,14 @@ import React, {
|
|
|
4
4
|
useContext,
|
|
5
5
|
useEffect,
|
|
6
6
|
useImperativeHandle,
|
|
7
|
+
useLayoutEffect,
|
|
7
8
|
useRef,
|
|
8
9
|
useState,
|
|
9
10
|
} from 'react';
|
|
10
11
|
import {
|
|
11
|
-
Platform,
|
|
12
|
-
Pressable,
|
|
13
|
-
Text,
|
|
14
12
|
TextInput,
|
|
15
13
|
TextInputFocusEvent,
|
|
14
|
+
TextInputSelectionChangeEvent,
|
|
16
15
|
TouchableOpacity,
|
|
17
16
|
View,
|
|
18
17
|
} from 'react-native';
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
} from '../Context';
|
|
25
24
|
import { Icon } from '../Icon';
|
|
26
25
|
import { useScaleSize, exportFontFamily } from '../Text';
|
|
26
|
+
import { Spacing } from '../Consts';
|
|
27
27
|
import {
|
|
28
28
|
ErrorView,
|
|
29
29
|
FloatingView,
|
|
@@ -35,24 +35,97 @@ import { InputMoneyProps } from './index';
|
|
|
35
35
|
import styles from './styles';
|
|
36
36
|
import SystemTextInput from './SystemTextInput';
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
type Selection = { start: number; end: number };
|
|
39
|
+
type Edit = { start: number; end: number; inserted: string };
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
const countDigits = (text: string) => text.replace(/\D/g, '').length;
|
|
42
|
+
|
|
43
|
+
const groupDigits = (digits: string) =>
|
|
44
|
+
digits.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
|
45
|
+
|
|
46
|
+
// Caret position right after the `digitIndex`-th digit of `display`.
|
|
47
|
+
const caretFromDigitIndex = (display: string, digitIndex: number) => {
|
|
48
|
+
let count = 0;
|
|
49
|
+
for (let i = 0; i < display.length && count < digitIndex; i++) {
|
|
50
|
+
if (/\d/.test(display[i])) {
|
|
51
|
+
count += 1;
|
|
52
|
+
if (count === digitIndex) return i + 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const detectEdit = (old: string, next: string, selection: Selection): Edit => {
|
|
59
|
+
const { start, end } = selection;
|
|
60
|
+
const removed = old.length - next.length;
|
|
61
|
+
const candidates: Edit[] = [
|
|
62
|
+
{
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
inserted: next.slice(start, next.length - (old.length - end)),
|
|
66
|
+
},
|
|
67
|
+
{ start: start - removed, end: start, inserted: '' },
|
|
68
|
+
{ start, end: start + removed, inserted: '' },
|
|
69
|
+
];
|
|
70
|
+
const match = candidates.find(
|
|
71
|
+
e =>
|
|
72
|
+
e.start >= 0 &&
|
|
73
|
+
e.end >= e.start &&
|
|
74
|
+
e.end <= old.length &&
|
|
75
|
+
old.slice(0, e.start) + e.inserted + old.slice(e.end) === next,
|
|
76
|
+
);
|
|
77
|
+
if (match) return match;
|
|
78
|
+
|
|
79
|
+
let prefix = 0;
|
|
80
|
+
while (
|
|
81
|
+
prefix < Math.min(old.length, next.length) &&
|
|
82
|
+
old[prefix] === next[prefix]
|
|
83
|
+
) {
|
|
84
|
+
prefix += 1;
|
|
46
85
|
}
|
|
47
86
|
let suffix = 0;
|
|
48
87
|
while (
|
|
49
|
-
suffix < Math.min(
|
|
50
|
-
old[old.length - 1 - suffix] ===
|
|
88
|
+
suffix < Math.min(old.length, next.length) - prefix &&
|
|
89
|
+
old[old.length - 1 - suffix] === next[next.length - 1 - suffix]
|
|
51
90
|
) {
|
|
52
91
|
suffix += 1;
|
|
53
92
|
}
|
|
54
|
-
|
|
55
|
-
|
|
93
|
+
return {
|
|
94
|
+
start: prefix,
|
|
95
|
+
end: old.length - suffix,
|
|
96
|
+
inserted: next.slice(prefix, next.length - suffix),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Applies `edit` to the digits of `display`. Separators and currency are never
|
|
101
|
+
// deleted: removing a separator deletes the digit before it instead, removing
|
|
102
|
+
// only the currency leaves the digits untouched.
|
|
103
|
+
const applyEdit = (display: string, edit: Edit, digitLimit: number) => {
|
|
104
|
+
const digits = display.replace(/\D/g, '');
|
|
105
|
+
let left = countDigits(display.slice(0, edit.start));
|
|
106
|
+
let removed = countDigits(display.slice(edit.start, edit.end));
|
|
107
|
+
const inserted = edit.inserted.replace(/\D/g, '');
|
|
108
|
+
if (
|
|
109
|
+
!removed &&
|
|
110
|
+
!inserted &&
|
|
111
|
+
left > 0 &&
|
|
112
|
+
display.slice(edit.start, edit.end).includes('.')
|
|
113
|
+
) {
|
|
114
|
+
left -= 1;
|
|
115
|
+
removed = 1;
|
|
116
|
+
}
|
|
117
|
+
const next = digits.slice(0, left) + inserted + digits.slice(left + removed);
|
|
118
|
+
const trimmed = next.replace(/^0+(?=\d)/, '');
|
|
119
|
+
if ((!removed && !inserted) || trimmed.length > digitLimit) {
|
|
120
|
+
return { digits, caretDigit: left };
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
digits: trimmed,
|
|
124
|
+
caretDigit: Math.max(
|
|
125
|
+
0,
|
|
126
|
+
left + inserted.length - (next.length - trimmed.length),
|
|
127
|
+
),
|
|
128
|
+
};
|
|
56
129
|
};
|
|
57
130
|
|
|
58
131
|
const InputMoney = forwardRef(
|
|
@@ -93,14 +166,14 @@ const InputMoney = forwardRef(
|
|
|
93
166
|
const [focused, setFocused] = useState(false);
|
|
94
167
|
const [displayValue, setDisplayValue] = useState('');
|
|
95
168
|
const [numericValue, setNumericValue] = useState('');
|
|
96
|
-
const [
|
|
169
|
+
const [commitCount, setCommitCount] = useState(0);
|
|
170
|
+
const displayRef = useRef('');
|
|
171
|
+
const selectionRef = useRef<Selection>({ start: 0, end: 0 });
|
|
172
|
+
// Caret to push to native once the committed text is rendered.
|
|
173
|
+
const pendingCaretRef = useRef<number | null>(null);
|
|
174
|
+
const pendingSelectionRef = useRef<Selection | null>(null);
|
|
175
|
+
const editingRef = useRef(false);
|
|
97
176
|
const inputRef = useRef<TextInput>(null);
|
|
98
|
-
const caretDigitsAfter = useRef<number | undefined>(undefined);
|
|
99
|
-
const lastRawText = useRef('');
|
|
100
|
-
const [caretSelection, setCaretSelection] = useState<{
|
|
101
|
-
start: number;
|
|
102
|
-
end: number;
|
|
103
|
-
}>();
|
|
104
177
|
const componentName = 'InputMoney';
|
|
105
178
|
|
|
106
179
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
@@ -118,96 +191,92 @@ const InputMoney = forwardRef(
|
|
|
118
191
|
if (value !== undefined && typeof value === 'string') {
|
|
119
192
|
const cleanValue = value.replace(/\D/g, '').replace(/^0+(?=\d)/, '');
|
|
120
193
|
if (cleanValue !== numericValue) {
|
|
121
|
-
|
|
122
|
-
setNumericValue(cleanValue);
|
|
123
|
-
updateDisplayValue(cleanValue);
|
|
194
|
+
commit(cleanValue, cleanValue.length);
|
|
124
195
|
}
|
|
125
196
|
}
|
|
126
197
|
}, [value]);
|
|
127
198
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
setCaretSelection(current =>
|
|
138
|
-
current === caretSelection ? undefined : current,
|
|
139
|
-
);
|
|
140
|
-
}, [caretSelection]);
|
|
141
|
-
|
|
142
|
-
const updateDisplayValue = (numeric: string) => {
|
|
143
|
-
const next = numeric ? moneyFormatter.format(Number(numeric)) : '';
|
|
144
|
-
setDisplayValue(next);
|
|
145
|
-
|
|
146
|
-
// Android moves the caret proportionally when the text is replaced, so
|
|
147
|
-
// it lands before the typed digit once a separator is added. Send the
|
|
148
|
-
// caret together with the text, keeping the same digits after it.
|
|
149
|
-
const digitsAfter = caretDigitsAfter.current;
|
|
150
|
-
caretDigitsAfter.current = undefined;
|
|
151
|
-
if (Platform.OS !== 'android' || digitsAfter === undefined) return;
|
|
152
|
-
let before = parseInputText(next).length - digitsAfter;
|
|
153
|
-
let caret = 0;
|
|
154
|
-
while (before > 0 && caret < next.length) {
|
|
155
|
-
if (/\d/.test(next[caret])) before -= 1;
|
|
156
|
-
caret += 1;
|
|
199
|
+
// Always set the caret imperatively: a controlled `selection` prop is only
|
|
200
|
+
// sent to native when it differs from the last native selection, so it is
|
|
201
|
+
// skipped whenever a stale selection event already reported the same value.
|
|
202
|
+
useLayoutEffect(() => {
|
|
203
|
+
const caret = pendingCaretRef.current;
|
|
204
|
+
if (caret === null) return;
|
|
205
|
+
pendingCaretRef.current = null;
|
|
206
|
+
if (inputRef.current?.isFocused()) {
|
|
207
|
+
inputRef.current.setSelection(caret, caret);
|
|
157
208
|
}
|
|
158
|
-
|
|
159
|
-
};
|
|
209
|
+
}, [commitCount]);
|
|
160
210
|
|
|
161
|
-
const
|
|
211
|
+
const commit = (digits: string, caretDigit: number) => {
|
|
212
|
+
const display = digits ? `${groupDigits(digits)}${currency}` : '';
|
|
213
|
+
const caret = caretFromDigitIndex(display, caretDigit);
|
|
214
|
+
const changed = digits !== numericValue;
|
|
215
|
+
displayRef.current = display;
|
|
216
|
+
selectionRef.current = { start: caret, end: caret };
|
|
217
|
+
pendingCaretRef.current = caret;
|
|
218
|
+
setCommitCount(count => count + 1);
|
|
219
|
+
setDisplayValue(display);
|
|
220
|
+
setNumericValue(digits);
|
|
221
|
+
return changed;
|
|
222
|
+
};
|
|
162
223
|
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
updateDisplayValue(next);
|
|
169
|
-
onChangeText?.(next);
|
|
170
|
-
}
|
|
224
|
+
const parseInputText = (text: string): string => {
|
|
225
|
+
if (!text) return '';
|
|
226
|
+
const currencyRegex = new RegExp(` ?${currency}$`, 'g');
|
|
227
|
+
const textWithoutCurrency = text.replace(currencyRegex, '').trim();
|
|
228
|
+
return textWithoutCurrency.replace(/[^\d]/g, '');
|
|
171
229
|
};
|
|
172
230
|
|
|
173
231
|
const onClearText = () => {
|
|
174
232
|
inputRef?.current?.clear();
|
|
175
233
|
inputRef.current?.setNativeProps({ text: '' });
|
|
176
|
-
|
|
177
|
-
setNumericValue('');
|
|
234
|
+
commit('', 0);
|
|
178
235
|
onChangeText?.('');
|
|
179
236
|
};
|
|
180
237
|
|
|
181
238
|
const _onChangeText = (text: string) => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
239
|
+
let digits = '';
|
|
240
|
+
let caretDigit = 0;
|
|
241
|
+
pendingSelectionRef.current = null;
|
|
242
|
+
editingRef.current = true;
|
|
243
|
+
setTimeout(() => {
|
|
244
|
+
editingRef.current = false;
|
|
245
|
+
});
|
|
246
|
+
if (text) {
|
|
247
|
+
const edit = detectEdit(displayRef.current, text, selectionRef.current);
|
|
248
|
+
({ digits, caretDigit } = applyEdit(
|
|
249
|
+
displayRef.current,
|
|
250
|
+
edit,
|
|
251
|
+
digitLimit,
|
|
252
|
+
));
|
|
188
253
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// formatted text and keeps the raw one, so the edit may apply to either.
|
|
192
|
-
// Pick the one that differs from the new text by a single edit.
|
|
193
|
-
const raw = lastRawText.current;
|
|
194
|
-
lastRawText.current = text;
|
|
195
|
-
const formattedEdit = findEdit(lastDisplayValue, text);
|
|
196
|
-
const rawEdit = findEdit(raw, text);
|
|
197
|
-
const { old, start, suffix } =
|
|
198
|
-
raw && rawEdit.size < formattedEdit.size ? rawEdit : formattedEdit;
|
|
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));
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
254
|
+
if (commit(digits, caretDigit)) {
|
|
255
|
+
onChangeText?.(digits);
|
|
209
256
|
}
|
|
210
|
-
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Keeps the caret out of the currency suffix.
|
|
260
|
+
const _onSelectionChange = (e: TextInputSelectionChangeEvent) => {
|
|
261
|
+
props.onSelectionChange?.(e);
|
|
262
|
+
if (editingRef.current) return;
|
|
263
|
+
const selection = e.nativeEvent.selection;
|
|
264
|
+
pendingSelectionRef.current = selection;
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
if (pendingSelectionRef.current !== selection) return;
|
|
267
|
+
pendingSelectionRef.current = null;
|
|
268
|
+
const { start, end } = selection;
|
|
269
|
+
const display = displayRef.current;
|
|
270
|
+
const max = display ? display.length - currency.length : 0;
|
|
271
|
+
const clamped = {
|
|
272
|
+
start: Math.min(start, max),
|
|
273
|
+
end: Math.min(end, max),
|
|
274
|
+
};
|
|
275
|
+
selectionRef.current = clamped;
|
|
276
|
+
if (clamped.start !== start || clamped.end !== end) {
|
|
277
|
+
inputRef.current?.setSelection(clamped.start, clamped.end);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
211
280
|
};
|
|
212
281
|
|
|
213
282
|
const _onFocus = (e: TextInputFocusEvent) => {
|
|
@@ -221,15 +290,17 @@ const InputMoney = forwardRef(
|
|
|
221
290
|
};
|
|
222
291
|
|
|
223
292
|
const _setText = (text: string) => {
|
|
224
|
-
caretDigitsAfter.current = undefined;
|
|
225
293
|
if (!text) {
|
|
226
|
-
|
|
227
|
-
setNumericValue('');
|
|
294
|
+
commit('', 0);
|
|
228
295
|
onChangeText?.('');
|
|
229
296
|
return;
|
|
230
297
|
}
|
|
231
|
-
const
|
|
232
|
-
|
|
298
|
+
const digits = parseInputText(text)
|
|
299
|
+
.replace(/^0+(?=\d)/, '')
|
|
300
|
+
.slice(0, digitLimit);
|
|
301
|
+
if (commit(digits, digits.length)) {
|
|
302
|
+
onChangeText?.(digits);
|
|
303
|
+
}
|
|
233
304
|
};
|
|
234
305
|
|
|
235
306
|
useImperativeHandle(ref, () => {
|
|
@@ -251,14 +322,6 @@ const InputMoney = forwardRef(
|
|
|
251
322
|
placeholderColor = disabledColor;
|
|
252
323
|
iconTintColor = disabledColor;
|
|
253
324
|
}
|
|
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;
|
|
262
325
|
return (
|
|
263
326
|
<View
|
|
264
327
|
style={[
|
|
@@ -277,11 +340,7 @@ const InputMoney = forwardRef(
|
|
|
277
340
|
onPress={onPressFloatingIcon}
|
|
278
341
|
componentId={componentId}
|
|
279
342
|
/>
|
|
280
|
-
<
|
|
281
|
-
style={[styles.inputView, styles.moneyInputView]}
|
|
282
|
-
onPress={() => inputRef.current?.focus()}
|
|
283
|
-
disabled={disabled}
|
|
284
|
-
>
|
|
343
|
+
<View style={styles.inputView}>
|
|
285
344
|
<SystemTextInput
|
|
286
345
|
{...props}
|
|
287
346
|
accessibilityLabel={`${componentId}`}
|
|
@@ -291,34 +350,33 @@ const InputMoney = forwardRef(
|
|
|
291
350
|
disabled: disabled,
|
|
292
351
|
...props.accessibilityState,
|
|
293
352
|
}}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
353
|
+
style={[
|
|
354
|
+
{
|
|
355
|
+
width: '100%',
|
|
356
|
+
paddingLeft: Spacing.M,
|
|
357
|
+
height: '100%',
|
|
358
|
+
fontSize: scaledFontSize,
|
|
359
|
+
paddingVertical: Spacing.S,
|
|
360
|
+
fontFamily: inputFontFamily,
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
color: textColor,
|
|
364
|
+
},
|
|
365
|
+
]}
|
|
297
366
|
allowFontScaling={false}
|
|
298
367
|
textBreakStrategy="highQuality"
|
|
299
368
|
value={displayValue}
|
|
300
|
-
selection={caretSelection ?? props.selection}
|
|
301
369
|
onChangeText={_onChangeText}
|
|
370
|
+
onSelectionChange={_onSelectionChange}
|
|
302
371
|
onFocus={_onFocus}
|
|
303
372
|
onBlur={_onBlur}
|
|
304
|
-
placeholder={
|
|
373
|
+
placeholder={placeholder}
|
|
305
374
|
selectionColor={theme.colors.primary}
|
|
306
375
|
placeholderTextColor={placeholderColor}
|
|
307
376
|
keyboardType={'numeric'}
|
|
308
377
|
maxLength={undefined}
|
|
309
378
|
/>
|
|
310
|
-
|
|
311
|
-
<Text
|
|
312
|
-
style={[
|
|
313
|
-
textStyle,
|
|
314
|
-
{ color: displayValue ? textColor : placeholderColor },
|
|
315
|
-
]}
|
|
316
|
-
allowFontScaling={false}
|
|
317
|
-
>
|
|
318
|
-
{currency}
|
|
319
|
-
</Text>
|
|
320
|
-
) : null}
|
|
321
|
-
</Pressable>
|
|
379
|
+
</View>
|
|
322
380
|
<View style={styles.iconView}>
|
|
323
381
|
{showClearIcon && focused && displayValue ? (
|
|
324
382
|
<TouchableOpacity
|
package/Input/styles.ts
CHANGED
|
@@ -218,14 +218,4 @@ 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
|
-
},
|
|
231
221
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momo-kits/foundation",
|
|
3
|
-
"version": "0.168.
|
|
3
|
+
"version": "0.168.2-rn.88.0",
|
|
4
4
|
"description": "React Native Component Kits",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"@momo-kits/foundation"
|
|
@@ -12,29 +12,29 @@
|
|
|
12
12
|
},
|
|
13
13
|
"scripts": {},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@react-navigation/bottom-tabs": "7.
|
|
16
|
-
"@react-navigation/core": "7.
|
|
17
|
-
"@react-navigation/elements": "2.
|
|
18
|
-
"@react-navigation/native": "7.1
|
|
19
|
-
"@react-navigation/routers": "7.
|
|
20
|
-
"@react-navigation/stack": "7.
|
|
15
|
+
"@react-navigation/bottom-tabs": "7.21.0",
|
|
16
|
+
"@react-navigation/core": "7.24.0",
|
|
17
|
+
"@react-navigation/elements": "2.9.45",
|
|
18
|
+
"@react-navigation/native": "7.5.1",
|
|
19
|
+
"@react-navigation/routers": "7.6.5",
|
|
20
|
+
"@react-navigation/stack": "7.12.1",
|
|
21
21
|
"@shopify/flash-list": "2.1.0",
|
|
22
22
|
"lottie-react-native": "7.3.6",
|
|
23
23
|
"react-native-fast-image": "git+https://oauth2:k_r5y_gV6sX9-TqQxgcd@gitlab.mservice.com.vn/momo-platform/public/react-native-fast-image.git#v8.11.0",
|
|
24
|
-
"react-native-gesture-handler": "
|
|
24
|
+
"react-native-gesture-handler": "3.4.0",
|
|
25
25
|
"react-native-linear-gradient": "git+https://oauth2:k_r5y_gV6sX9-TqQxgcd@gitlab.mservice.com.vn/momo-platform/public/react-native-linear-gradient#v3.0.0",
|
|
26
|
-
"react-native-reanimated": "4.
|
|
27
|
-
"react-native-
|
|
28
|
-
"react-native-
|
|
26
|
+
"react-native-reanimated": "4.7.1",
|
|
27
|
+
"react-native-safe-area-context": "5.7.0",
|
|
28
|
+
"react-native-reserved-regions": "0.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/color": "3.0.6",
|
|
32
|
-
"react-native-reanimated": "4.
|
|
33
|
-
"react-native-worklets": "0.
|
|
32
|
+
"react-native-reanimated": "4.7.1",
|
|
33
|
+
"react-native-worklets": "0.13.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"react-native": "*",
|
|
37
|
-
"react-native-reanimated": ">=4.
|
|
38
|
-
"react-native-worklets": ">=0.
|
|
37
|
+
"react-native-reanimated": ">=4.7.0",
|
|
38
|
+
"react-native-worklets": ">=0.13.0"
|
|
39
39
|
}
|
|
40
40
|
}
|