@momo-kits/foundation 0.168.1-beta.14 → 0.168.1-beta.15
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 +37 -142
- package/Application/ModalScreen.tsx +8 -12
- package/Application/device.ts +1 -24
- package/Layout/utils.ts +0 -1
- package/Popup/PopupNotify.tsx +10 -20
- package/Popup/utils.ts +15 -9
- package/package.json +1 -1
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
PanResponder,
|
|
17
17
|
Platform,
|
|
18
18
|
Pressable,
|
|
19
|
-
StatusBar,
|
|
20
19
|
StyleSheet,
|
|
21
20
|
TouchableOpacity,
|
|
22
21
|
useWindowDimensions,
|
|
@@ -40,37 +39,18 @@ import Animated, {
|
|
|
40
39
|
} from 'react-native-reanimated';
|
|
41
40
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
42
41
|
import layoutStyles from '../Layout/styles';
|
|
43
|
-
import { DuoMetrics, InnerEdgeStyles, useScreenLayout } from '../Layout/utils';
|
|
44
42
|
import { getDuoTopInset, isIPhoneDuo } from './device';
|
|
45
|
-
import { SideBarButton } from './Components/SideBarButton';
|
|
46
43
|
|
|
47
44
|
// iPhone Duo inner display: center the sheet instead of spanning the width
|
|
48
45
|
const DUO_REGULAR_MIN_WIDTH = 600;
|
|
49
46
|
|
|
50
|
-
const INDICATOR_HEIGHT = 16;
|
|
51
|
-
const HEADER_HEIGHT = 56;
|
|
52
|
-
const DUO_ORIENTATIONS = ['portrait', 'landscape'];
|
|
53
|
-
const SIDE_COLUMN_OFFSET =
|
|
54
|
-
INDICATOR_HEIGHT + (HEADER_HEIGHT - DuoMetrics.controlSize) / 2;
|
|
55
|
-
|
|
56
47
|
const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
57
48
|
const { theme, navigator } = useContext(ApplicationContext);
|
|
58
49
|
const context = useContext<any>(MiniAppContext);
|
|
59
50
|
const windowSize = useWindowDimensions();
|
|
60
51
|
const isDuo = isIPhoneDuo();
|
|
61
|
-
const isDuoCentered =
|
|
62
|
-
|
|
63
|
-
Math.min(windowSize.width, windowSize.height) >= DUO_REGULAR_MIN_WIDTH;
|
|
64
|
-
const isDuoFloating = isDuo && !isDuoCentered;
|
|
65
|
-
const { sideBar } = useScreenLayout();
|
|
66
|
-
const sideColumnWidth = (sideBar?.width ?? 0) - Spacing.M;
|
|
67
|
-
const sideColumn =
|
|
68
|
-
isDuoFloating &&
|
|
69
|
-
sideColumnWidth >= DuoMetrics.controlSize + Spacing.M &&
|
|
70
|
-
sideBar
|
|
71
|
-
? sideBar
|
|
72
|
-
: undefined;
|
|
73
|
-
const hasSideColumn = !!sideColumn;
|
|
52
|
+
const isDuoCentered = isDuo && windowSize.width >= DUO_REGULAR_MIN_WIDTH;
|
|
53
|
+
const isPortrait = windowSize.height > windowSize.width;
|
|
74
54
|
// 'screen' is the outer display on iPhone Duo, even when unfolded
|
|
75
55
|
const screenHeight = Dimensions.get('screen').height;
|
|
76
56
|
const heightDevice = isDuo ? windowSize.height : screenHeight;
|
|
@@ -78,9 +58,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
78
58
|
const insets = useSafeAreaInsets();
|
|
79
59
|
const heightHeader = useHeaderHeight();
|
|
80
60
|
const topGap = Math.max(
|
|
81
|
-
|
|
82
|
-
? insets.top
|
|
83
|
-
: getDuoTopInset(insets, windowSize.width, windowSize.height),
|
|
61
|
+
getDuoTopInset(insets, windowSize.width, windowSize.height),
|
|
84
62
|
Spacing.XL,
|
|
85
63
|
);
|
|
86
64
|
const isIOS = Platform.OS === 'ios';
|
|
@@ -105,10 +83,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
105
83
|
}: BottomSheetParams = props.route.params;
|
|
106
84
|
|
|
107
85
|
const translateY = useSharedValue(heightDevice);
|
|
108
|
-
const onDismissRef = useRef(props.route.params?.onDismiss);
|
|
109
|
-
onDismissRef.current = props.route.params?.onDismiss;
|
|
110
86
|
const [keyboardLift, setKeyboardLift] = useState(0);
|
|
111
|
-
const [sheetTop, setSheetTop] = useState(windowSize.height);
|
|
112
87
|
|
|
113
88
|
// KeyboardAvoidingView lifts the whole sheet past the top on iPhone Duo,
|
|
114
89
|
// so lift it here and shrink maxHeight by the same amount instead
|
|
@@ -204,15 +179,15 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
204
179
|
}
|
|
205
180
|
|
|
206
181
|
/**
|
|
207
|
-
* emit dismiss event
|
|
208
|
-
* window height while the sheet stays open
|
|
182
|
+
* emit dismiss event
|
|
209
183
|
*/
|
|
210
184
|
useEffect(() => {
|
|
185
|
+
translateY.value = heightDevice;
|
|
211
186
|
openAnimation();
|
|
212
187
|
return () => {
|
|
213
|
-
|
|
188
|
+
props.route.params?.onDismiss?.(action.current);
|
|
214
189
|
};
|
|
215
|
-
}, [openAnimation]);
|
|
190
|
+
}, [heightDevice, openAnimation, props.route.params, translateY]);
|
|
216
191
|
|
|
217
192
|
/**
|
|
218
193
|
* handler dismiss
|
|
@@ -253,42 +228,13 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
253
228
|
return () => backHandler.remove();
|
|
254
229
|
}, [barrierDismissible, onDismiss]);
|
|
255
230
|
|
|
256
|
-
const onPressBack = useCallback(() => {
|
|
257
|
-
onDismiss(true, leftOptions?.onPressIconLeft);
|
|
258
|
-
}, [leftOptions, onDismiss]);
|
|
259
|
-
|
|
260
|
-
const onPressClose = useCallback(() => {
|
|
261
|
-
action.current = 'icon_close';
|
|
262
|
-
onDismiss(true);
|
|
263
|
-
}, [onDismiss]);
|
|
264
|
-
|
|
265
|
-
const showBack = !!leftOptions?.onPressIconLeft;
|
|
266
|
-
const iconBack = leftOptions?.iconLeft ?? 'ic_back';
|
|
267
|
-
const sideColumnCount = showBack ? 2 : 1;
|
|
268
|
-
const sideColumnHeight =
|
|
269
|
-
sideColumnCount * DuoMetrics.controlSize +
|
|
270
|
-
(sideColumnCount - 1) * Spacing.M;
|
|
271
|
-
const coversStatusBar = !!sideColumn && sheetTop < sideColumn.top;
|
|
272
|
-
|
|
273
|
-
useEffect(() => {
|
|
274
|
-
if (!coversStatusBar) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
const entry = StatusBar.pushStackEntry({
|
|
278
|
-
barStyle: theme.dark ? 'light-content' : 'dark-content',
|
|
279
|
-
animated: true,
|
|
280
|
-
});
|
|
281
|
-
return () => StatusBar.popStackEntry(entry);
|
|
282
|
-
}, [coversStatusBar, theme.dark]);
|
|
283
|
-
|
|
284
231
|
/**
|
|
285
232
|
* render header
|
|
286
233
|
*/
|
|
287
234
|
const renderHeader = useCallback(() => {
|
|
288
|
-
const showButtons = !hasSideColumn;
|
|
289
235
|
return (
|
|
290
236
|
<View {...panResponder.panHandlers}>
|
|
291
|
-
<View style={{ height:
|
|
237
|
+
<View style={{ height: 16 }}>
|
|
292
238
|
<View style={styles.indicator} />
|
|
293
239
|
</View>
|
|
294
240
|
<View
|
|
@@ -300,9 +246,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
300
246
|
},
|
|
301
247
|
]}
|
|
302
248
|
>
|
|
303
|
-
{
|
|
304
|
-
<TouchableOpacity
|
|
305
|
-
|
|
249
|
+
{leftOptions?.onPressIconLeft ? (
|
|
250
|
+
<TouchableOpacity
|
|
251
|
+
style={styles.iconButton}
|
|
252
|
+
onPress={() => {
|
|
253
|
+
onDismiss(true, leftOptions.onPressIconLeft);
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
<Icon source={leftOptions.iconLeft ?? 'ic_back'} />
|
|
306
257
|
</TouchableOpacity>
|
|
307
258
|
) : null}
|
|
308
259
|
|
|
@@ -310,15 +261,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
310
261
|
<View style={Styles.flex}>{options.header}</View>
|
|
311
262
|
) : (
|
|
312
263
|
<>
|
|
313
|
-
{
|
|
314
|
-
<View style={styles.iconButton} />
|
|
315
|
-
) : null}
|
|
264
|
+
{!leftOptions ? <View style={styles.iconButton} /> : null}
|
|
316
265
|
<Text
|
|
317
|
-
style={[
|
|
318
|
-
Styles.flex,
|
|
319
|
-
styles.title,
|
|
320
|
-
!showButtons && styles.titleInset,
|
|
321
|
-
]}
|
|
266
|
+
style={[Styles.flex, { textAlign: 'center' }]}
|
|
322
267
|
numberOfLines={1}
|
|
323
268
|
typography="header_default_bold"
|
|
324
269
|
>
|
|
@@ -326,63 +271,28 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
326
271
|
</Text>
|
|
327
272
|
</>
|
|
328
273
|
)}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
274
|
+
<TouchableOpacity
|
|
275
|
+
style={styles.iconButton}
|
|
276
|
+
onPress={() => {
|
|
277
|
+
action.current = 'icon_close';
|
|
278
|
+
onDismiss(true);
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
<Icon source="navigation_close" />
|
|
282
|
+
</TouchableOpacity>
|
|
334
283
|
</View>
|
|
335
284
|
</View>
|
|
336
285
|
);
|
|
337
286
|
}, [
|
|
338
|
-
|
|
339
|
-
iconBack,
|
|
287
|
+
onDismiss,
|
|
340
288
|
leftOptions,
|
|
341
|
-
onPressBack,
|
|
342
|
-
onPressClose,
|
|
343
289
|
options.header,
|
|
344
290
|
options.title,
|
|
345
291
|
panResponder.panHandlers,
|
|
346
|
-
showBack,
|
|
347
292
|
theme.colors.border.default,
|
|
348
293
|
useDivider,
|
|
349
294
|
]);
|
|
350
295
|
|
|
351
|
-
/**
|
|
352
|
-
* iPhone Duo folded: back and close move to the side bar column, drawn on
|
|
353
|
-
* the part of the sheet under the side safe area. They line up with the
|
|
354
|
-
* header row, stay below the status bar cluster on a tall sheet and above a
|
|
355
|
-
* camera at the bottom end of the strip.
|
|
356
|
-
*/
|
|
357
|
-
const renderSideColumn = () => {
|
|
358
|
-
if (!sideColumn) {
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
const lowestTop =
|
|
362
|
-
windowSize.height - sideColumn.bottom - sideColumnHeight - sheetTop;
|
|
363
|
-
const top = Math.min(
|
|
364
|
-
Math.max(sideColumn.top - sheetTop, SIDE_COLUMN_OFFSET),
|
|
365
|
-
lowestTop,
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
return (
|
|
369
|
-
<View
|
|
370
|
-
pointerEvents={'box-none'}
|
|
371
|
-
style={[
|
|
372
|
-
styles.sideColumn,
|
|
373
|
-
sideColumn.edge === 'right' ? styles.alignRight : styles.alignLeft,
|
|
374
|
-
InnerEdgeStyles[sideColumn.edge],
|
|
375
|
-
{ top, width: sideColumnWidth },
|
|
376
|
-
]}
|
|
377
|
-
>
|
|
378
|
-
{showBack ? (
|
|
379
|
-
<SideBarButton icon={iconBack} onPress={onPressBack} />
|
|
380
|
-
) : null}
|
|
381
|
-
<SideBarButton icon="navigation_close" onPress={onPressClose} />
|
|
382
|
-
</View>
|
|
383
|
-
);
|
|
384
|
-
};
|
|
385
|
-
|
|
386
296
|
const animatedStyle = useAnimatedStyle(() => {
|
|
387
297
|
return {
|
|
388
298
|
transform: [{ translateY: translateY.value }],
|
|
@@ -400,9 +310,12 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
400
310
|
};
|
|
401
311
|
});
|
|
402
312
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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,
|
|
406
319
|
};
|
|
407
320
|
|
|
408
321
|
const screen = (
|
|
@@ -422,7 +335,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
422
335
|
}}
|
|
423
336
|
style={StyleSheet.absoluteFillObject}
|
|
424
337
|
isModalKit={true}
|
|
425
|
-
supportedOrientations={isDuo ? DUO_ORIENTATIONS : undefined}
|
|
426
338
|
>
|
|
427
339
|
<KeyboardAvoidingView
|
|
428
340
|
behavior={Platform.select({
|
|
@@ -445,26 +357,18 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
445
357
|
</Pressable>
|
|
446
358
|
|
|
447
359
|
<Animated.View
|
|
448
|
-
onLayout={
|
|
449
|
-
isDuoFloating ? e => setSheetTop(e.nativeEvent.layout.y) : undefined
|
|
450
|
-
}
|
|
451
360
|
style={[
|
|
452
361
|
animatedStyle,
|
|
453
362
|
{
|
|
454
363
|
backgroundColor,
|
|
455
|
-
borderTopLeftRadius:
|
|
456
|
-
borderTopRightRadius:
|
|
364
|
+
borderTopLeftRadius: Radius.M,
|
|
365
|
+
borderTopRightRadius: Radius.M,
|
|
457
366
|
overflow: 'hidden',
|
|
458
367
|
},
|
|
459
368
|
isDuo && {
|
|
460
369
|
maxHeight: windowSize.height - topGap - keyboardLift,
|
|
461
370
|
marginBottom: keyboardLift,
|
|
462
371
|
},
|
|
463
|
-
isDuoFloating && { marginHorizontal: Spacing.M },
|
|
464
|
-
sideColumn && {
|
|
465
|
-
minHeight:
|
|
466
|
-
SIDE_COLUMN_OFFSET + sideColumnHeight + sideColumn.bottom,
|
|
467
|
-
},
|
|
468
372
|
isDuoCentered && {
|
|
469
373
|
// keeps the portrait width in landscape, like system sheets
|
|
470
374
|
width: Math.min(
|
|
@@ -511,7 +415,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
511
415
|
}}
|
|
512
416
|
/>
|
|
513
417
|
)}
|
|
514
|
-
{renderSideColumn()}
|
|
515
418
|
</Animated.View>
|
|
516
419
|
</KeyboardAvoidingView>
|
|
517
420
|
</Container>
|
|
@@ -537,12 +440,10 @@ const styles = StyleSheet.create({
|
|
|
537
440
|
top: Spacing.S,
|
|
538
441
|
},
|
|
539
442
|
header: {
|
|
540
|
-
height:
|
|
443
|
+
height: 56,
|
|
541
444
|
flexDirection: 'row',
|
|
542
445
|
alignItems: 'center',
|
|
543
446
|
},
|
|
544
|
-
title: { textAlign: 'center' },
|
|
545
|
-
titleInset: { paddingHorizontal: Spacing.M },
|
|
546
447
|
iconButton: {
|
|
547
448
|
width: 24,
|
|
548
449
|
height: 24,
|
|
@@ -551,12 +452,6 @@ const styles = StyleSheet.create({
|
|
|
551
452
|
marginHorizontal: Spacing.M,
|
|
552
453
|
},
|
|
553
454
|
shrink: { flexShrink: 1 },
|
|
554
|
-
sideColumn: {
|
|
555
|
-
position: 'absolute',
|
|
556
|
-
gap: Spacing.M,
|
|
557
|
-
},
|
|
558
|
-
alignRight: { right: 0 },
|
|
559
|
-
alignLeft: { left: 0 },
|
|
560
455
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
561
456
|
});
|
|
562
457
|
|
|
@@ -18,7 +18,7 @@ import { Styles } from '../Consts';
|
|
|
18
18
|
import Navigation from './Navigation';
|
|
19
19
|
import { ModalParams } from './types';
|
|
20
20
|
import BottomSheet from './BottomSheet';
|
|
21
|
-
import {
|
|
21
|
+
import { getDuoTopInset, isIPhoneDuo } from './device';
|
|
22
22
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
23
23
|
|
|
24
24
|
const ModalScreen: React.FC<any> = props => {
|
|
@@ -42,7 +42,6 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
42
42
|
const Component = useRef(screen).current;
|
|
43
43
|
const insets = useSafeAreaInsets();
|
|
44
44
|
const windowSize = useWindowDimensions();
|
|
45
|
-
const isDuo = isIPhoneDuo();
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
47
|
* Fire auto_popup_displayed tracking event on mount
|
|
@@ -136,7 +135,6 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
136
135
|
onRequestClose={onDismiss}
|
|
137
136
|
style={StyleSheet.absoluteFillObject}
|
|
138
137
|
isModalKit={true}
|
|
139
|
-
supportedOrientations={isDuo ? DUO_ORIENTATIONS : undefined}
|
|
140
138
|
>
|
|
141
139
|
<KeyboardAvoidingView
|
|
142
140
|
style={Styles.flexCenter}
|
|
@@ -154,13 +152,14 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
154
152
|
opacity,
|
|
155
153
|
transform: [{ scale }],
|
|
156
154
|
},
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
marginTop:
|
|
155
|
+
// center below the side status bar and above the home indicator
|
|
156
|
+
isIPhoneDuo() && {
|
|
157
|
+
marginTop: getDuoTopInset(
|
|
158
|
+
insets,
|
|
159
|
+
windowSize.width,
|
|
160
|
+
windowSize.height,
|
|
161
|
+
),
|
|
160
162
|
marginBottom: insets.bottom,
|
|
161
|
-
marginLeft: insets.left,
|
|
162
|
-
marginRight: insets.right,
|
|
163
|
-
...getDuoModalBounds(insets, windowSize.width, windowSize.height),
|
|
164
163
|
},
|
|
165
164
|
modalStyle,
|
|
166
165
|
]}
|
|
@@ -172,14 +171,11 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
172
171
|
);
|
|
173
172
|
};
|
|
174
173
|
|
|
175
|
-
const DUO_ORIENTATIONS = ['portrait', 'landscape'];
|
|
176
|
-
|
|
177
174
|
const styles = StyleSheet.create({
|
|
178
175
|
overlayContent: {
|
|
179
176
|
flex: 1,
|
|
180
177
|
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
181
178
|
},
|
|
182
|
-
shrink: { flexShrink: 1 },
|
|
183
179
|
});
|
|
184
180
|
|
|
185
181
|
export default ModalScreen;
|
package/Application/device.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { Dimensions, Platform, ScaledSize } from 'react-native';
|
|
2
2
|
import type { EdgeInsets } from 'react-native-safe-area-context';
|
|
3
|
-
import { Spacing } from '../Consts';
|
|
4
3
|
|
|
5
4
|
// widest short side of any single-display iPhone
|
|
6
5
|
const IPHONE_MAX_SHORT_SIDE = 440;
|
|
7
6
|
|
|
8
|
-
const DUO_MODAL_MAX_WIDTH = 440;
|
|
9
|
-
const DUO_MODAL_MAX_HEIGHT = 640;
|
|
10
|
-
|
|
11
7
|
// iPhone Duo reports idiom "phone"; 'screen' stays on the outer display, so it
|
|
12
8
|
// still matches when Split View narrows the window
|
|
13
9
|
const isIPhoneDuo = () => {
|
|
@@ -30,23 +26,4 @@ const getDuoTopInset = (insets: EdgeInsets, width: number, height: number) =>
|
|
|
30
26
|
height > width ? Math.max(insets.left, insets.right) * 2 : 0,
|
|
31
27
|
);
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
* Max modal size on iPhone Duo: inside the safe area, so clear of the side
|
|
35
|
-
* toolbar strip, and capped so it stays a dialog on the inner display.
|
|
36
|
-
*/
|
|
37
|
-
const getDuoModalBounds = (
|
|
38
|
-
insets: EdgeInsets,
|
|
39
|
-
width: number,
|
|
40
|
-
height: number,
|
|
41
|
-
) => ({
|
|
42
|
-
maxWidth: Math.min(
|
|
43
|
-
width - insets.left - insets.right - Spacing.XL * 2,
|
|
44
|
-
DUO_MODAL_MAX_WIDTH,
|
|
45
|
-
),
|
|
46
|
-
maxHeight: Math.min(
|
|
47
|
-
height - insets.top - insets.bottom - Spacing.XL * 2,
|
|
48
|
-
DUO_MODAL_MAX_HEIGHT,
|
|
49
|
-
),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
export { isIPhoneDuo, getDuoTopInset, getDuoModalBounds };
|
|
29
|
+
export { isIPhoneDuo, getDuoTopInset };
|
package/Layout/utils.ts
CHANGED
package/Popup/PopupNotify.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { Image } from '../Image';
|
|
|
8
8
|
import { Text, useScaleSize } from '../Text';
|
|
9
9
|
import { PopupNotifyProps } from './types';
|
|
10
10
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
11
|
-
import {
|
|
11
|
+
import { usePopupMaxHeight, usePopupWidth } from './utils';
|
|
12
12
|
|
|
13
13
|
const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
14
14
|
image,
|
|
@@ -26,7 +26,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
26
26
|
const [scrollContent, setScrollContent] = useState(false);
|
|
27
27
|
const popupWidth = usePopupWidth();
|
|
28
28
|
const maxHeight = usePopupMaxHeight();
|
|
29
|
-
const radius = getPopupRadius();
|
|
30
29
|
const scaledMaxHeight = useScaleSize(172);
|
|
31
30
|
|
|
32
31
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
@@ -171,15 +170,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
171
170
|
|
|
172
171
|
const body = (
|
|
173
172
|
<>
|
|
174
|
-
{!!image &&
|
|
175
|
-
<Image
|
|
176
|
-
source={{ uri: image }}
|
|
177
|
-
style={[
|
|
178
|
-
styles.image,
|
|
179
|
-
{ borderTopLeftRadius: radius, borderTopRightRadius: radius },
|
|
180
|
-
]}
|
|
181
|
-
/>
|
|
182
|
-
)}
|
|
173
|
+
{!!image && <Image source={{ uri: image }} style={styles.image} />}
|
|
183
174
|
<View style={styles.content}>
|
|
184
175
|
<Text
|
|
185
176
|
typography={'header_default_bold'}
|
|
@@ -227,9 +218,9 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
227
218
|
<View
|
|
228
219
|
accessibilityLabel={'popup_notify'}
|
|
229
220
|
style={[
|
|
221
|
+
styles.container,
|
|
230
222
|
{
|
|
231
223
|
backgroundColor: theme.colors.background.surface,
|
|
232
|
-
borderRadius: radius,
|
|
233
224
|
width: popupWidth,
|
|
234
225
|
maxHeight,
|
|
235
226
|
},
|
|
@@ -240,13 +231,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
240
231
|
body
|
|
241
232
|
) : (
|
|
242
233
|
// scrolls when iPhone Duo landscape is too short; actions stay visible
|
|
243
|
-
<ScrollView
|
|
244
|
-
style={[
|
|
245
|
-
styles.body,
|
|
246
|
-
{ borderTopLeftRadius: radius, borderTopRightRadius: radius },
|
|
247
|
-
]}
|
|
248
|
-
bounces={false}
|
|
249
|
-
>
|
|
234
|
+
<ScrollView style={styles.body} bounces={false}>
|
|
250
235
|
{body}
|
|
251
236
|
</ScrollView>
|
|
252
237
|
)}
|
|
@@ -257,12 +242,17 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
257
242
|
};
|
|
258
243
|
|
|
259
244
|
const styles = StyleSheet.create({
|
|
245
|
+
container: {
|
|
246
|
+
borderRadius: Radius.L,
|
|
247
|
+
},
|
|
260
248
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
261
249
|
image: {
|
|
250
|
+
borderTopLeftRadius: Radius.L,
|
|
251
|
+
borderTopRightRadius: Radius.L,
|
|
262
252
|
aspectRatio: 1.7777,
|
|
263
253
|
overflow: 'hidden',
|
|
264
254
|
},
|
|
265
|
-
body: { flexShrink: 1
|
|
255
|
+
body: { flexShrink: 1 },
|
|
266
256
|
content: { padding: Spacing.XL },
|
|
267
257
|
description: {
|
|
268
258
|
marginTop: Spacing.S,
|
package/Popup/utils.ts
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import { Dimensions, useWindowDimensions } from 'react-native';
|
|
2
2
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Spacing } from '../Consts';
|
|
4
|
+
import { getDuoTopInset, isIPhoneDuo } from '../Application/device';
|
|
5
|
+
|
|
6
|
+
// width of a system alert on the iPhone Duo inner display, in both orientations
|
|
7
|
+
const DUO_POPUP_MAX_WIDTH = 440;
|
|
5
8
|
|
|
6
9
|
const usePopupWidth = () => {
|
|
7
|
-
const
|
|
8
|
-
const { width, height } = useWindowDimensions();
|
|
10
|
+
const windowSize = useWindowDimensions();
|
|
9
11
|
if (!isIPhoneDuo()) {
|
|
10
12
|
return Dimensions.get('window').width - Spacing.XL * 2;
|
|
11
13
|
}
|
|
12
|
-
return
|
|
14
|
+
return Math.min(windowSize.width - Spacing.XL * 2, DUO_POPUP_MAX_WIDTH);
|
|
13
15
|
};
|
|
14
16
|
|
|
17
|
+
// room left for a popup on iPhone Duo, where landscape is too short for it
|
|
15
18
|
const usePopupMaxHeight = () => {
|
|
16
19
|
const insets = useSafeAreaInsets();
|
|
17
20
|
const { width, height } = useWindowDimensions();
|
|
18
21
|
if (!isIPhoneDuo()) {
|
|
19
22
|
return undefined;
|
|
20
23
|
}
|
|
21
|
-
return
|
|
24
|
+
return (
|
|
25
|
+
height -
|
|
26
|
+
getDuoTopInset(insets, width, height) -
|
|
27
|
+
insets.bottom -
|
|
28
|
+
Spacing.XL * 2
|
|
29
|
+
);
|
|
22
30
|
};
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export { usePopupWidth, usePopupMaxHeight, getPopupRadius };
|
|
32
|
+
export { usePopupWidth, usePopupMaxHeight };
|