@momo-kits/foundation 0.167.1-beta.7 → 0.167.1-duo.12
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/BottomSheet.tsx +115 -14
- package/Application/BottomTab/BottomTabBar.tsx +6 -3
- package/Application/BottomTab/DuoBarCompression.tsx +135 -0
- package/Application/BottomTab/DuoTabBar.tsx +462 -0
- package/Application/BottomTab/index.tsx +155 -134
- package/Application/Components/DuoGlassBackground.tsx +179 -0
- package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
- package/Application/Components/HeaderExtendHeader.tsx +65 -28
- package/Application/Components/HeaderLeft.tsx +23 -0
- package/Application/Components/HeaderRight.tsx +104 -9
- package/Application/Components/HeaderTitle.tsx +47 -21
- package/Application/Components/OptionalGlassView.ts +11 -0
- package/Application/Components/SearchHeader.tsx +15 -14
- package/Application/Components/SideBarButton.tsx +128 -0
- package/Application/ModalScreen.tsx +16 -2
- package/Application/NavigationContainer.tsx +36 -48
- package/Application/Navigator.ts +1 -3
- package/Application/StackScreen.tsx +2 -5
- package/Application/WidgetContainer.tsx +9 -15
- package/Application/device.ts +29 -0
- package/Application/types.ts +10 -1
- package/Application/utils.tsx +10 -3
- package/Consts/index.ts +0 -1
- package/Consts/theme.ts +3 -4
- package/Input/Input.tsx +3 -4
- package/Input/InputMoney.tsx +31 -51
- package/Input/InputPhoneNumber.tsx +3 -4
- package/Input/InputSearch.tsx +3 -4
- package/Input/InputTextArea.tsx +3 -4
- package/Layout/Card.tsx +8 -26
- package/Layout/FloatingButton.tsx +3 -3
- package/Layout/Item.tsx +1 -10
- package/Layout/ReservedRegions.tsx +47 -0
- package/Layout/Screen.tsx +240 -48
- package/Layout/Section.tsx +5 -18
- package/Layout/index.ts +4 -4
- package/Layout/styles.ts +5 -0
- package/Layout/utils.ts +375 -5
- package/Popup/PopupNotify.tsx +33 -22
- package/Popup/PopupPromotion.tsx +20 -8
- package/Popup/utils.ts +32 -0
- package/Text/index.tsx +5 -15
- package/Text/utils.ts +1 -2
- package/package.json +3 -2
- package/Consts/designSystemFont.ts +0 -25
- package/Layout/GridSystem.tsx +0 -137
package/Input/InputMoney.tsx
CHANGED
|
@@ -9,11 +9,9 @@ import React, {
|
|
|
9
9
|
useState,
|
|
10
10
|
} from 'react';
|
|
11
11
|
import {
|
|
12
|
-
NativeSyntheticEvent,
|
|
13
|
-
Platform,
|
|
14
12
|
TextInput,
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
TextInputFocusEvent,
|
|
14
|
+
TextInputSelectionChangeEvent,
|
|
17
15
|
TouchableOpacity,
|
|
18
16
|
View,
|
|
19
17
|
} from 'react-native';
|
|
@@ -57,13 +55,10 @@ const caretFromDigitIndex = (display: string, digitIndex: number) => {
|
|
|
57
55
|
return 0;
|
|
58
56
|
};
|
|
59
57
|
|
|
60
|
-
// Finds which range of `old` was replaced to produce `next`. `selection` is
|
|
61
|
-
// the pre-edit selection on iOS and already the post-edit caret on Android,
|
|
62
|
-
// where onSelectionChange fires before onChangeText.
|
|
63
58
|
const detectEdit = (old: string, next: string, selection: Selection): Edit => {
|
|
64
59
|
const { start, end } = selection;
|
|
65
60
|
const removed = old.length - next.length;
|
|
66
|
-
const
|
|
61
|
+
const candidates: Edit[] = [
|
|
67
62
|
{
|
|
68
63
|
start,
|
|
69
64
|
end,
|
|
@@ -72,26 +67,6 @@ const detectEdit = (old: string, next: string, selection: Selection): Edit => {
|
|
|
72
67
|
{ start: start - removed, end: start, inserted: '' },
|
|
73
68
|
{ start, end: start + removed, inserted: '' },
|
|
74
69
|
];
|
|
75
|
-
const postEdit: Edit[] = [];
|
|
76
|
-
const tail = next.length - start;
|
|
77
|
-
if (start === end && tail <= old.length) {
|
|
78
|
-
let prefix = 0;
|
|
79
|
-
while (
|
|
80
|
-
prefix < Math.min(start, old.length - tail) &&
|
|
81
|
-
old[prefix] === next[prefix]
|
|
82
|
-
) {
|
|
83
|
-
prefix += 1;
|
|
84
|
-
}
|
|
85
|
-
postEdit.push({
|
|
86
|
-
start: prefix,
|
|
87
|
-
end: old.length - tail,
|
|
88
|
-
inserted: next.slice(prefix, start),
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
const candidates =
|
|
92
|
-
Platform.OS === 'android'
|
|
93
|
-
? [...postEdit, ...preEdit]
|
|
94
|
-
: [...preEdit, ...postEdit];
|
|
95
70
|
const match = candidates.find(
|
|
96
71
|
e =>
|
|
97
72
|
e.start >= 0 &&
|
|
@@ -196,9 +171,8 @@ const InputMoney = forwardRef(
|
|
|
196
171
|
const selectionRef = useRef<Selection>({ start: 0, end: 0 });
|
|
197
172
|
// Caret to push to native once the committed text is rendered.
|
|
198
173
|
const pendingCaretRef = useRef<number | null>(null);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const rawCaretRef = useRef<number | null>(null);
|
|
174
|
+
const pendingSelectionRef = useRef<Selection | null>(null);
|
|
175
|
+
const editingRef = useRef(false);
|
|
202
176
|
const inputRef = useRef<TextInput>(null);
|
|
203
177
|
const componentName = 'InputMoney';
|
|
204
178
|
|
|
@@ -265,10 +239,13 @@ const InputMoney = forwardRef(
|
|
|
265
239
|
const _onChangeText = (text: string) => {
|
|
266
240
|
let digits = '';
|
|
267
241
|
let caretDigit = 0;
|
|
268
|
-
|
|
242
|
+
pendingSelectionRef.current = null;
|
|
243
|
+
editingRef.current = true;
|
|
244
|
+
setTimeout(() => {
|
|
245
|
+
editingRef.current = false;
|
|
246
|
+
});
|
|
269
247
|
if (text) {
|
|
270
248
|
const edit = detectEdit(displayRef.current, text, selectionRef.current);
|
|
271
|
-
rawCaretRef.current = edit.start + edit.inserted.length;
|
|
272
249
|
({ digits, caretDigit } = applyEdit(
|
|
273
250
|
displayRef.current,
|
|
274
251
|
edit,
|
|
@@ -281,31 +258,34 @@ const InputMoney = forwardRef(
|
|
|
281
258
|
};
|
|
282
259
|
|
|
283
260
|
// Keeps the caret out of the currency suffix.
|
|
284
|
-
const _onSelectionChange = (
|
|
285
|
-
e: NativeSyntheticEvent<TextInputSelectionChangeEventData>,
|
|
286
|
-
) => {
|
|
261
|
+
const _onSelectionChange = (e: TextInputSelectionChangeEvent) => {
|
|
287
262
|
props.onSelectionChange?.(e);
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
263
|
+
if (editingRef.current) return;
|
|
264
|
+
const selection = e.nativeEvent.selection;
|
|
265
|
+
pendingSelectionRef.current = selection;
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
if (pendingSelectionRef.current !== selection) return;
|
|
268
|
+
pendingSelectionRef.current = null;
|
|
269
|
+
const { start, end } = selection;
|
|
270
|
+
const display = displayRef.current;
|
|
271
|
+
const max = display ? display.length - currency.length : 0;
|
|
272
|
+
const clamped = {
|
|
273
|
+
start: Math.min(start, max),
|
|
274
|
+
end: Math.min(end, max),
|
|
275
|
+
};
|
|
276
|
+
selectionRef.current = clamped;
|
|
277
|
+
if (clamped.start !== start || clamped.end !== end) {
|
|
278
|
+
inputRef.current?.setSelection(clamped.start, clamped.end);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
301
281
|
};
|
|
302
282
|
|
|
303
|
-
const _onFocus = (e:
|
|
283
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
304
284
|
setFocused(true);
|
|
305
285
|
onFocus?.(e);
|
|
306
286
|
};
|
|
307
287
|
|
|
308
|
-
const _onBlur = (e:
|
|
288
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
309
289
|
setFocused(false);
|
|
310
290
|
onBlur?.(e);
|
|
311
291
|
};
|
|
@@ -6,10 +6,9 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import {
|
|
9
|
-
NativeSyntheticEvent,
|
|
10
9
|
StyleSheet,
|
|
11
10
|
TextInput,
|
|
12
|
-
|
|
11
|
+
TextInputFocusEvent,
|
|
13
12
|
TouchableOpacity,
|
|
14
13
|
View,
|
|
15
14
|
} from 'react-native';
|
|
@@ -93,12 +92,12 @@ const InputPhoneNumber = forwardRef(
|
|
|
93
92
|
onChangeText?.(text);
|
|
94
93
|
};
|
|
95
94
|
|
|
96
|
-
const _onFocus = (e:
|
|
95
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
97
96
|
setFocused(true);
|
|
98
97
|
onFocus?.(e);
|
|
99
98
|
};
|
|
100
99
|
|
|
101
|
-
const _onBlur = (e:
|
|
100
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
102
101
|
setFocused(false);
|
|
103
102
|
onBlur?.(e);
|
|
104
103
|
};
|
package/Input/InputSearch.tsx
CHANGED
|
@@ -10,9 +10,8 @@ import React, {
|
|
|
10
10
|
useState,
|
|
11
11
|
} from 'react';
|
|
12
12
|
import {
|
|
13
|
-
NativeSyntheticEvent,
|
|
14
13
|
TextInput,
|
|
15
|
-
|
|
14
|
+
TextInputFocusEvent,
|
|
16
15
|
TouchableOpacity,
|
|
17
16
|
View,
|
|
18
17
|
} from 'react-native';
|
|
@@ -166,12 +165,12 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
|
|
|
166
165
|
props.accessibilityLabel,
|
|
167
166
|
);
|
|
168
167
|
|
|
169
|
-
const _onFocus = (e:
|
|
168
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
170
169
|
setFocused(true);
|
|
171
170
|
onFocus?.(e);
|
|
172
171
|
};
|
|
173
172
|
|
|
174
|
-
const _onBlur = (e:
|
|
173
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
175
174
|
setFocused(false);
|
|
176
175
|
onBlur?.(e);
|
|
177
176
|
};
|
package/Input/InputTextArea.tsx
CHANGED
|
@@ -7,9 +7,8 @@ import React, {
|
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
9
|
import {
|
|
10
|
-
NativeSyntheticEvent,
|
|
11
10
|
TextInput,
|
|
12
|
-
|
|
11
|
+
TextInputFocusEvent,
|
|
13
12
|
TouchableOpacity,
|
|
14
13
|
View,
|
|
15
14
|
} from 'react-native';
|
|
@@ -79,12 +78,12 @@ const InputTextArea = forwardRef(
|
|
|
79
78
|
setInputValue(text);
|
|
80
79
|
};
|
|
81
80
|
|
|
82
|
-
const _onFocus = (e:
|
|
81
|
+
const _onFocus = (e: TextInputFocusEvent) => {
|
|
83
82
|
setFocused(true);
|
|
84
83
|
onFocus?.(e);
|
|
85
84
|
};
|
|
86
85
|
|
|
87
|
-
const _onBlur = (e:
|
|
86
|
+
const _onBlur = (e: TextInputFocusEvent) => {
|
|
88
87
|
setFocused(false);
|
|
89
88
|
onBlur?.(e);
|
|
90
89
|
};
|
package/Layout/Card.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { useGridSystem } from './utils';
|
|
2
|
+
import { View, ViewProps } from 'react-native';
|
|
3
|
+
import { useGridSystem, useScreenLayout } from './utils';
|
|
4
4
|
import { GridContextProps } from './types';
|
|
5
|
-
import { GridContext
|
|
5
|
+
import { GridContext } from './index';
|
|
6
6
|
import { Image } from '../Image';
|
|
7
|
-
import { ApplicationContext
|
|
7
|
+
import { ApplicationContext } from '../Context';
|
|
8
8
|
import styles from './styles';
|
|
9
|
-
import {
|
|
9
|
+
import { Radius, Shadow, Spacing } from '../Consts';
|
|
10
10
|
|
|
11
11
|
export interface CardProps extends ViewProps {
|
|
12
12
|
/**
|
|
@@ -27,12 +27,13 @@ const Card: React.FC<CardProps> = ({
|
|
|
27
27
|
useShadow = false,
|
|
28
28
|
...props
|
|
29
29
|
}) => {
|
|
30
|
-
const {
|
|
30
|
+
const { theme } = useContext(ApplicationContext);
|
|
31
31
|
const { numberOfColumns } = useGridSystem();
|
|
32
|
+
const { contentWidth } = useScreenLayout();
|
|
32
33
|
|
|
33
34
|
const gutterSize = 8;
|
|
34
35
|
const margin = 12;
|
|
35
|
-
const widthSection =
|
|
36
|
+
const widthSection = contentWidth - margin * 2;
|
|
36
37
|
let sizePerSpan =
|
|
37
38
|
(widthSection - margin * 2 - gutterSize * (numberOfColumns - 1)) /
|
|
38
39
|
numberOfColumns -
|
|
@@ -47,23 +48,6 @@ const Card: React.FC<CardProps> = ({
|
|
|
47
48
|
},
|
|
48
49
|
};
|
|
49
50
|
|
|
50
|
-
/**
|
|
51
|
-
* render overlay only dev mode
|
|
52
|
-
*/
|
|
53
|
-
const renderOverlay = () => {
|
|
54
|
-
return (
|
|
55
|
-
<>
|
|
56
|
-
<GridSystem
|
|
57
|
-
width={widthSection}
|
|
58
|
-
gutterSize={Spacing.S}
|
|
59
|
-
screenGrid={false}
|
|
60
|
-
gridColor={Colors.red_01}
|
|
61
|
-
/>
|
|
62
|
-
<View pointerEvents={'none'} style={styles.boxGridCard} />
|
|
63
|
-
</>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
51
|
return (
|
|
68
52
|
<GridContext.Provider value={gridContext}>
|
|
69
53
|
<View
|
|
@@ -95,9 +79,7 @@ const Card: React.FC<CardProps> = ({
|
|
|
95
79
|
]}
|
|
96
80
|
/>
|
|
97
81
|
)}
|
|
98
|
-
{/* Render only allowed children as valid React elements */}
|
|
99
82
|
{children}
|
|
100
|
-
{showGrid && renderOverlay()}
|
|
101
83
|
</View>
|
|
102
84
|
</GridContext.Provider>
|
|
103
85
|
);
|
|
@@ -10,8 +10,8 @@ import Animated, {
|
|
|
10
10
|
useAnimatedStyle,
|
|
11
11
|
useSharedValue,
|
|
12
12
|
withTiming,
|
|
13
|
-
runOnJS,
|
|
14
13
|
} from 'react-native-reanimated';
|
|
14
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
15
15
|
import { ApplicationContext } from '../Context';
|
|
16
16
|
import { Icon } from '../Icon';
|
|
17
17
|
import { useScaleSize, exportFontFamily } from '../Text';
|
|
@@ -78,7 +78,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
78
78
|
minWidth,
|
|
79
79
|
{ duration: 100 },
|
|
80
80
|
finished => {
|
|
81
|
-
if (finished)
|
|
81
|
+
if (finished) scheduleOnRN(setShowText, false);
|
|
82
82
|
},
|
|
83
83
|
);
|
|
84
84
|
} else {
|
|
@@ -87,7 +87,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
87
87
|
maxWidth.value,
|
|
88
88
|
{ duration: 100 },
|
|
89
89
|
finished => {
|
|
90
|
-
if (finished)
|
|
90
|
+
if (finished) scheduleOnRN(setShowText, true);
|
|
91
91
|
},
|
|
92
92
|
);
|
|
93
93
|
}
|
package/Layout/Item.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import { View, ViewProps } from 'react-native';
|
|
3
3
|
import { GridContext } from './index';
|
|
4
|
-
import {
|
|
4
|
+
import { MiniAppContext } from '../Context';
|
|
5
5
|
import styles from './styles';
|
|
6
6
|
import { SpanNumber } from './types';
|
|
7
7
|
|
|
@@ -23,19 +23,11 @@ const Item: React.FC<ItemProps> = ({
|
|
|
23
23
|
children,
|
|
24
24
|
style,
|
|
25
25
|
}) => {
|
|
26
|
-
const { showGrid } = useContext(ApplicationContext);
|
|
27
26
|
const context = useContext<any>(MiniAppContext);
|
|
28
27
|
const grid = useContext(GridContext);
|
|
29
28
|
|
|
30
29
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
31
30
|
|
|
32
|
-
/**
|
|
33
|
-
* render overlay view only dev mode
|
|
34
|
-
*/
|
|
35
|
-
const renderOverlay = () => {
|
|
36
|
-
return <View pointerEvents={'none'} style={styles.boxGrid} />;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
31
|
return (
|
|
40
32
|
<View
|
|
41
33
|
style={[
|
|
@@ -49,7 +41,6 @@ const Item: React.FC<ItemProps> = ({
|
|
|
49
41
|
]}
|
|
50
42
|
>
|
|
51
43
|
{children}
|
|
52
|
-
{showGrid && renderOverlay()}
|
|
53
44
|
</View>
|
|
54
45
|
);
|
|
55
46
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { createContext, ReactNode, useState } from 'react';
|
|
2
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export type ReservedRegion = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// native probe is optional so apps without @momo-kits/reserved-regions still bundle
|
|
12
|
+
let ReservedRegionsView: React.ComponentType<any> | undefined;
|
|
13
|
+
try {
|
|
14
|
+
ReservedRegionsView =
|
|
15
|
+
require('@momo-kits/reserved-regions').ReservedRegionsView;
|
|
16
|
+
} catch {
|
|
17
|
+
ReservedRegionsView = undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* screen regions the system currently occludes, in window coordinates
|
|
22
|
+
*/
|
|
23
|
+
const ReservedRegionsContext = createContext<ReservedRegion[]>([]);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* tracks the system occlusion regions (iOS 27.1+), e.g. the iPhone Duo status bar
|
|
27
|
+
* cluster growing for a call or a Live Activity
|
|
28
|
+
*/
|
|
29
|
+
const ReservedRegionsProvider: React.FC<{ children: ReactNode }> = ({
|
|
30
|
+
children,
|
|
31
|
+
}) => {
|
|
32
|
+
const [regions, setRegions] = useState<ReservedRegion[]>([]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<ReservedRegionsContext.Provider value={regions}>
|
|
36
|
+
{children}
|
|
37
|
+
{Platform.OS === 'ios' && !!ReservedRegionsView && (
|
|
38
|
+
<ReservedRegionsView
|
|
39
|
+
style={StyleSheet.absoluteFill}
|
|
40
|
+
onRegionsChange={setRegions}
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
43
|
+
</ReservedRegionsContext.Provider>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { ReservedRegionsContext, ReservedRegionsProvider };
|