@momo-kits/foundation 0.168.1-beta.3 → 0.168.1-beta.31
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 +69 -62
- package/Application/BottomTab/DuoTabBar.tsx +191 -151
- package/Application/BottomTab/useLiquidSelection.ts +374 -0
- package/Application/Components/DuoGlassBackground.tsx +5 -8
- package/Application/ModalScreen.tsx +37 -16
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/device.ts +1 -9
- package/Application/index.ts +6 -0
- package/Application/modalLayout.ts +180 -0
- package/Application/utils.tsx +5 -9
- package/Input/InputMoney.tsx +98 -54
- package/Input/styles.ts +9 -0
- package/Layout/ReservedRegions.tsx +21 -4
- package/Layout/Screen.tsx +18 -39
- package/Layout/index.ts +2 -0
- package/Layout/utils.ts +1 -0
- package/Popup/PopupNotify.tsx +78 -38
- package/Popup/PopupPromotion.tsx +5 -4
- package/package.json +7 -7
- package/Popup/utils.ts +0 -32
|
@@ -39,28 +39,28 @@ import Animated, {
|
|
|
39
39
|
} from 'react-native-reanimated';
|
|
40
40
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
41
41
|
import layoutStyles from '../Layout/styles';
|
|
42
|
-
import {
|
|
42
|
+
import { isIPhoneDuo } from './device';
|
|
43
|
+
import { useModalLayout } from './modalLayout';
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
const
|
|
45
|
+
const INDICATOR_HEIGHT = 16;
|
|
46
|
+
const HEADER_HEIGHT = 56;
|
|
47
|
+
const DUO_ORIENTATIONS = ['portrait', 'landscape'];
|
|
48
|
+
// foldable spec: icon button 32, icon 20
|
|
49
|
+
const FOLDABLE_ICON_BUTTON_SIZE = 32;
|
|
50
|
+
const FOLDABLE_ICON_SIZE = 20;
|
|
46
51
|
|
|
47
52
|
const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
48
53
|
const { theme, navigator } = useContext(ApplicationContext);
|
|
49
54
|
const context = useContext<any>(MiniAppContext);
|
|
50
55
|
const windowSize = useWindowDimensions();
|
|
51
56
|
const isDuo = isIPhoneDuo();
|
|
52
|
-
const
|
|
53
|
-
const isPortrait = windowSize.height > windowSize.width;
|
|
57
|
+
const { isFoldable, placement, sheet, scrimOpacity } = useModalLayout();
|
|
54
58
|
// 'screen' is the outer display on iPhone Duo, even when unfolded
|
|
55
59
|
const screenHeight = Dimensions.get('screen').height;
|
|
56
60
|
const heightDevice = isDuo ? windowSize.height : screenHeight;
|
|
57
61
|
const action = useRef<undefined | string>(undefined);
|
|
58
62
|
const insets = useSafeAreaInsets();
|
|
59
63
|
const heightHeader = useHeaderHeight();
|
|
60
|
-
const topGap = Math.max(
|
|
61
|
-
getDuoTopInset(insets, windowSize.width, windowSize.height),
|
|
62
|
-
Spacing.XL,
|
|
63
|
-
);
|
|
64
64
|
const isIOS = Platform.OS === 'ios';
|
|
65
65
|
const bottomInset = isIOS ? Math.min(insets.bottom, 21) : insets.bottom;
|
|
66
66
|
const keyboardOffset = heightHeader - bottomInset;
|
|
@@ -83,6 +83,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
83
83
|
}: BottomSheetParams = props.route.params;
|
|
84
84
|
|
|
85
85
|
const translateY = useSharedValue(heightDevice);
|
|
86
|
+
const onDismissRef = useRef(props.route.params?.onDismiss);
|
|
87
|
+
onDismissRef.current = props.route.params?.onDismiss;
|
|
86
88
|
const [keyboardLift, setKeyboardLift] = useState(0);
|
|
87
89
|
|
|
88
90
|
// KeyboardAvoidingView lifts the whole sheet past the top on iPhone Duo,
|
|
@@ -179,15 +181,15 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
/**
|
|
182
|
-
* emit dismiss event
|
|
184
|
+
* emit dismiss event, once: folding or rotating iPhone Duo changes the
|
|
185
|
+
* window height while the sheet stays open
|
|
183
186
|
*/
|
|
184
187
|
useEffect(() => {
|
|
185
|
-
translateY.value = heightDevice;
|
|
186
188
|
openAnimation();
|
|
187
189
|
return () => {
|
|
188
|
-
|
|
190
|
+
onDismissRef.current?.(action.current);
|
|
189
191
|
};
|
|
190
|
-
}, [
|
|
192
|
+
}, [openAnimation]);
|
|
191
193
|
|
|
192
194
|
/**
|
|
193
195
|
* handler dismiss
|
|
@@ -228,13 +230,30 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
228
230
|
return () => backHandler.remove();
|
|
229
231
|
}, [barrierDismissible, onDismiss]);
|
|
230
232
|
|
|
233
|
+
const onPressBack = useCallback(() => {
|
|
234
|
+
onDismiss(true, leftOptions?.onPressIconLeft);
|
|
235
|
+
}, [leftOptions, onDismiss]);
|
|
236
|
+
|
|
237
|
+
const onPressClose = useCallback(() => {
|
|
238
|
+
action.current = 'icon_close';
|
|
239
|
+
onDismiss(true);
|
|
240
|
+
}, [onDismiss]);
|
|
241
|
+
|
|
242
|
+
const showBack = !!leftOptions?.onPressIconLeft;
|
|
243
|
+
const iconBack = leftOptions?.iconLeft ?? 'ic_back';
|
|
244
|
+
|
|
231
245
|
/**
|
|
232
246
|
* render header
|
|
233
247
|
*/
|
|
234
248
|
const renderHeader = useCallback(() => {
|
|
249
|
+
const iconButtonStyle = [
|
|
250
|
+
styles.iconButton,
|
|
251
|
+
isFoldable && styles.foldableIconButton,
|
|
252
|
+
];
|
|
253
|
+
const iconSize = isFoldable ? FOLDABLE_ICON_SIZE : undefined;
|
|
235
254
|
return (
|
|
236
255
|
<View {...panResponder.panHandlers}>
|
|
237
|
-
<View style={{ height:
|
|
256
|
+
<View style={{ height: INDICATOR_HEIGHT }}>
|
|
238
257
|
<View style={styles.indicator} />
|
|
239
258
|
</View>
|
|
240
259
|
<View
|
|
@@ -246,14 +265,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
246
265
|
},
|
|
247
266
|
]}
|
|
248
267
|
>
|
|
249
|
-
{
|
|
250
|
-
<TouchableOpacity
|
|
251
|
-
|
|
252
|
-
onPress={() => {
|
|
253
|
-
onDismiss(true, leftOptions.onPressIconLeft);
|
|
254
|
-
}}
|
|
255
|
-
>
|
|
256
|
-
<Icon source={leftOptions.iconLeft ?? 'ic_back'} />
|
|
268
|
+
{showBack ? (
|
|
269
|
+
<TouchableOpacity style={iconButtonStyle} onPress={onPressBack}>
|
|
270
|
+
<Icon source={iconBack} size={iconSize} />
|
|
257
271
|
</TouchableOpacity>
|
|
258
272
|
) : null}
|
|
259
273
|
|
|
@@ -261,9 +275,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
261
275
|
<View style={Styles.flex}>{options.header}</View>
|
|
262
276
|
) : (
|
|
263
277
|
<>
|
|
264
|
-
{!leftOptions ? <View style={
|
|
278
|
+
{!leftOptions ? <View style={iconButtonStyle} /> : null}
|
|
265
279
|
<Text
|
|
266
|
-
style={[Styles.flex,
|
|
280
|
+
style={[Styles.flex, styles.title]}
|
|
267
281
|
numberOfLines={1}
|
|
268
282
|
typography="header_default_bold"
|
|
269
283
|
>
|
|
@@ -271,24 +285,22 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
271
285
|
</Text>
|
|
272
286
|
</>
|
|
273
287
|
)}
|
|
274
|
-
<TouchableOpacity
|
|
275
|
-
|
|
276
|
-
onPress={() => {
|
|
277
|
-
action.current = 'icon_close';
|
|
278
|
-
onDismiss(true);
|
|
279
|
-
}}
|
|
280
|
-
>
|
|
281
|
-
<Icon source="navigation_close" />
|
|
288
|
+
<TouchableOpacity style={iconButtonStyle} onPress={onPressClose}>
|
|
289
|
+
<Icon source="navigation_close" size={iconSize} />
|
|
282
290
|
</TouchableOpacity>
|
|
283
291
|
</View>
|
|
284
292
|
</View>
|
|
285
293
|
);
|
|
286
294
|
}, [
|
|
287
|
-
|
|
295
|
+
iconBack,
|
|
296
|
+
isFoldable,
|
|
288
297
|
leftOptions,
|
|
298
|
+
onPressBack,
|
|
299
|
+
onPressClose,
|
|
289
300
|
options.header,
|
|
290
301
|
options.title,
|
|
291
302
|
panResponder.panHandlers,
|
|
303
|
+
showBack,
|
|
292
304
|
theme.colors.border.default,
|
|
293
305
|
useDivider,
|
|
294
306
|
]);
|
|
@@ -304,20 +316,12 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
304
316
|
opacity: interpolate(
|
|
305
317
|
translateY.value,
|
|
306
318
|
[0, heightDevice],
|
|
307
|
-
[
|
|
319
|
+
[scrimOpacity, 0],
|
|
308
320
|
Extrapolation.CLAMP,
|
|
309
321
|
),
|
|
310
322
|
};
|
|
311
323
|
});
|
|
312
324
|
|
|
313
|
-
// landscape side buttons span the full height, so inset the content; in
|
|
314
|
-
// portrait the side status bar only covers the top corner
|
|
315
|
-
const needsSideInset = isDuo && !isDuoCentered && !isPortrait;
|
|
316
|
-
const sideInsetStyle = needsSideInset && {
|
|
317
|
-
paddingLeft: insets.left,
|
|
318
|
-
paddingRight: insets.right,
|
|
319
|
-
};
|
|
320
|
-
|
|
321
325
|
const screen = (
|
|
322
326
|
<Screen
|
|
323
327
|
{...props}
|
|
@@ -335,6 +339,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
335
339
|
}}
|
|
336
340
|
style={StyleSheet.absoluteFillObject}
|
|
337
341
|
isModalKit={true}
|
|
342
|
+
supportedOrientations={isDuo ? DUO_ORIENTATIONS : undefined}
|
|
338
343
|
>
|
|
339
344
|
<KeyboardAvoidingView
|
|
340
345
|
behavior={Platform.select({
|
|
@@ -361,29 +366,27 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
361
366
|
animatedStyle,
|
|
362
367
|
{
|
|
363
368
|
backgroundColor,
|
|
364
|
-
borderTopLeftRadius:
|
|
365
|
-
borderTopRightRadius:
|
|
369
|
+
borderTopLeftRadius: sheet.radius,
|
|
370
|
+
borderTopRightRadius: sheet.radius,
|
|
366
371
|
overflow: 'hidden',
|
|
367
372
|
},
|
|
368
|
-
isDuo && {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
alignSelf: 'center',
|
|
380
|
-
},
|
|
373
|
+
isDuo && { marginBottom: keyboardLift },
|
|
374
|
+
isFoldable &&
|
|
375
|
+
sheet.width !== undefined &&
|
|
376
|
+
sheet.maxHeight !== undefined && {
|
|
377
|
+
// centered in the placement area, clear of the iPhone Duo
|
|
378
|
+
// control column and on the right half when half folded
|
|
379
|
+
width: sheet.width,
|
|
380
|
+
marginLeft: placement.x + (placement.width - sheet.width) / 2,
|
|
381
|
+
maxHeight: sheet.maxHeight - keyboardLift,
|
|
382
|
+
minHeight: sheet.minHeight,
|
|
383
|
+
},
|
|
381
384
|
showBaseLineDebug && styles.debugBaseLine,
|
|
382
385
|
]}
|
|
383
386
|
>
|
|
384
|
-
{
|
|
387
|
+
{isFoldable ? (
|
|
385
388
|
// content shrinks so a tall sheet keeps its header and footer
|
|
386
|
-
<View style={
|
|
389
|
+
<View style={styles.shrink}>
|
|
387
390
|
{renderHeader()}
|
|
388
391
|
<View style={styles.shrink}>{screen}</View>
|
|
389
392
|
</View>
|
|
@@ -401,7 +404,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
401
404
|
paddingBottom: Spacing.S,
|
|
402
405
|
backgroundColor: theme.colors.background.surface,
|
|
403
406
|
},
|
|
404
|
-
sideInsetStyle,
|
|
405
407
|
]}
|
|
406
408
|
>
|
|
407
409
|
{footerComponent}
|
|
@@ -428,7 +430,7 @@ const styles = StyleSheet.create({
|
|
|
428
430
|
},
|
|
429
431
|
overlay: {
|
|
430
432
|
flex: 1,
|
|
431
|
-
backgroundColor:
|
|
433
|
+
backgroundColor: Colors.black_20,
|
|
432
434
|
},
|
|
433
435
|
indicator: {
|
|
434
436
|
width: 40,
|
|
@@ -440,10 +442,11 @@ const styles = StyleSheet.create({
|
|
|
440
442
|
top: Spacing.S,
|
|
441
443
|
},
|
|
442
444
|
header: {
|
|
443
|
-
height:
|
|
445
|
+
height: HEADER_HEIGHT,
|
|
444
446
|
flexDirection: 'row',
|
|
445
447
|
alignItems: 'center',
|
|
446
448
|
},
|
|
449
|
+
title: { textAlign: 'center' },
|
|
447
450
|
iconButton: {
|
|
448
451
|
width: 24,
|
|
449
452
|
height: 24,
|
|
@@ -451,6 +454,10 @@ const styles = StyleSheet.create({
|
|
|
451
454
|
justifyContent: 'center',
|
|
452
455
|
marginHorizontal: Spacing.M,
|
|
453
456
|
},
|
|
457
|
+
foldableIconButton: {
|
|
458
|
+
width: FOLDABLE_ICON_BUTTON_SIZE,
|
|
459
|
+
height: FOLDABLE_ICON_BUTTON_SIZE,
|
|
460
|
+
},
|
|
454
461
|
shrink: { flexShrink: 1 },
|
|
455
462
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
456
463
|
});
|