rnnovaui 0.12.2 → 0.12.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.
- package/package.json +1 -1
- package/src/components/UIModal/UIModal.js +315 -205
package/package.json
CHANGED
|
@@ -12,7 +12,6 @@ import React, {
|
|
|
12
12
|
import {
|
|
13
13
|
Animated as RNAnimated,
|
|
14
14
|
BackHandler,
|
|
15
|
-
Dimensions,
|
|
16
15
|
KeyboardAvoidingView,
|
|
17
16
|
Modal,
|
|
18
17
|
Platform,
|
|
@@ -24,7 +23,6 @@ import {
|
|
|
24
23
|
} from "react-native";
|
|
25
24
|
|
|
26
25
|
import { Ionicons } from "@expo/vector-icons";
|
|
27
|
-
|
|
28
26
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
29
27
|
|
|
30
28
|
import Animated, {
|
|
@@ -36,8 +34,6 @@ import Animated, {
|
|
|
36
34
|
|
|
37
35
|
import { useUITheme } from "../../theme/UIProvider";
|
|
38
36
|
|
|
39
|
-
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
40
|
-
|
|
41
37
|
/* -------------------------------------------------------------------------- */
|
|
42
38
|
/* Constants */
|
|
43
39
|
/* -------------------------------------------------------------------------- */
|
|
@@ -70,9 +66,6 @@ export const MODAL_ANIMATIONS = [
|
|
|
70
66
|
/* Helpers */
|
|
71
67
|
/* -------------------------------------------------------------------------- */
|
|
72
68
|
|
|
73
|
-
const isValidNumber = (value) =>
|
|
74
|
-
typeof value === "number" && Number.isFinite(value);
|
|
75
|
-
|
|
76
69
|
const getSafeValue = (value, fallback) => {
|
|
77
70
|
return value !== undefined && value !== null ? value : fallback;
|
|
78
71
|
};
|
|
@@ -95,7 +88,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
95
88
|
onClose,
|
|
96
89
|
|
|
97
90
|
/* ------------------------------------------------------------------ */
|
|
98
|
-
/* Variant
|
|
91
|
+
/* Variant / Position */
|
|
99
92
|
/* ------------------------------------------------------------------ */
|
|
100
93
|
|
|
101
94
|
variant = "default",
|
|
@@ -291,7 +284,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
291
284
|
const themeAnimation = theme?.animation ?? {};
|
|
292
285
|
|
|
293
286
|
/* -------------------------------------------------------------------- */
|
|
294
|
-
/* Controlled
|
|
287
|
+
/* Controlled / Uncontrolled */
|
|
295
288
|
/* -------------------------------------------------------------------- */
|
|
296
289
|
|
|
297
290
|
const controlled = visible !== undefined;
|
|
@@ -307,7 +300,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
307
300
|
const previousVisibleRef = useRef(isVisible);
|
|
308
301
|
|
|
309
302
|
/* -------------------------------------------------------------------- */
|
|
310
|
-
/*
|
|
303
|
+
/* Resolved variant */
|
|
311
304
|
/* -------------------------------------------------------------------- */
|
|
312
305
|
|
|
313
306
|
const resolvedVariant = MODAL_VARIANTS.includes(variant)
|
|
@@ -331,65 +324,83 @@ const UIModal = forwardRef(function UIModal(
|
|
|
331
324
|
: "fade";
|
|
332
325
|
|
|
333
326
|
/* -------------------------------------------------------------------- */
|
|
334
|
-
/* Theme
|
|
327
|
+
/* Theme values */
|
|
335
328
|
/* -------------------------------------------------------------------- */
|
|
336
329
|
|
|
337
|
-
const resolvedBackgroundColor =
|
|
338
|
-
backgroundColor
|
|
339
|
-
colors.card ??
|
|
340
|
-
|
|
341
|
-
colors.background ??
|
|
342
|
-
"#FFFFFF";
|
|
343
|
-
|
|
344
|
-
const resolvedBorderColor = borderColor ?? colors.border ?? "#E5E5E5";
|
|
330
|
+
const resolvedBackgroundColor = getSafeValue(
|
|
331
|
+
backgroundColor,
|
|
332
|
+
colors.card ?? colors.surface ?? colors.background ?? "#FFFFFF",
|
|
333
|
+
);
|
|
345
334
|
|
|
346
|
-
const
|
|
335
|
+
const resolvedBorderColor = getSafeValue(
|
|
336
|
+
borderColor,
|
|
337
|
+
colors.border ?? "#E5E5E5",
|
|
338
|
+
);
|
|
347
339
|
|
|
348
|
-
const
|
|
340
|
+
const resolvedTitleColor = getSafeValue(titleColor, colors.text ?? "#111111");
|
|
349
341
|
|
|
350
|
-
const
|
|
342
|
+
const resolvedCloseIconColor = getSafeValue(
|
|
343
|
+
closeIconColor,
|
|
344
|
+
colors.text ?? "#111111",
|
|
345
|
+
);
|
|
351
346
|
|
|
352
|
-
|
|
353
|
-
/* Animation duration */
|
|
354
|
-
/* -------------------------------------------------------------------- */
|
|
347
|
+
const resolvedBorderRadius = getSafeValue(borderRadius, radius.xl ?? 18);
|
|
355
348
|
|
|
356
|
-
const resolvedAnimationDuration =
|
|
357
|
-
animationDuration
|
|
349
|
+
const resolvedAnimationDuration = getSafeValue(
|
|
350
|
+
animationDuration,
|
|
351
|
+
themeAnimation.normal ?? 280,
|
|
352
|
+
);
|
|
358
353
|
|
|
359
354
|
/* -------------------------------------------------------------------- */
|
|
360
355
|
/* Padding */
|
|
361
356
|
/* -------------------------------------------------------------------- */
|
|
362
357
|
|
|
363
|
-
const
|
|
358
|
+
const finalHorizontalPadding = paddingHorizontal ?? padding;
|
|
364
359
|
|
|
365
|
-
const
|
|
360
|
+
const finalVerticalPadding = paddingVertical ?? padding;
|
|
366
361
|
|
|
367
|
-
const
|
|
362
|
+
const finalPaddingTop = paddingTop ?? finalVerticalPadding;
|
|
368
363
|
|
|
369
|
-
const
|
|
364
|
+
const finalPaddingBottom = paddingBottom ?? finalVerticalPadding;
|
|
370
365
|
|
|
371
|
-
const
|
|
366
|
+
const finalPaddingLeft = paddingLeft ?? finalHorizontalPadding;
|
|
372
367
|
|
|
373
|
-
const
|
|
368
|
+
const finalPaddingRight = paddingRight ?? finalHorizontalPadding;
|
|
374
369
|
|
|
375
370
|
/* -------------------------------------------------------------------- */
|
|
376
371
|
/* Margin */
|
|
377
372
|
/* -------------------------------------------------------------------- */
|
|
378
373
|
|
|
379
|
-
const
|
|
374
|
+
const finalHorizontalMargin = marginHorizontal ?? margin;
|
|
375
|
+
|
|
376
|
+
const finalVerticalMargin = marginVertical ?? margin;
|
|
377
|
+
|
|
378
|
+
const finalMarginTop = marginTop ?? finalVerticalMargin;
|
|
380
379
|
|
|
381
|
-
const
|
|
380
|
+
const finalMarginBottom = marginBottom ?? finalVerticalMargin;
|
|
382
381
|
|
|
383
|
-
const
|
|
382
|
+
const finalMarginLeft = marginLeft ?? finalHorizontalMargin;
|
|
384
383
|
|
|
385
|
-
const
|
|
384
|
+
const finalMarginRight = marginRight ?? finalHorizontalMargin;
|
|
386
385
|
|
|
387
|
-
|
|
386
|
+
/* -------------------------------------------------------------------- */
|
|
387
|
+
/* Spring */
|
|
388
|
+
/* -------------------------------------------------------------------- */
|
|
389
|
+
|
|
390
|
+
const resolvedSpringConfig = useMemo(
|
|
391
|
+
() => ({
|
|
392
|
+
damping: springConfig?.damping ?? themeAnimation?.spring?.damping ?? 18,
|
|
393
|
+
|
|
394
|
+
stiffness:
|
|
395
|
+
springConfig?.stiffness ?? themeAnimation?.spring?.stiffness ?? 180,
|
|
388
396
|
|
|
389
|
-
|
|
397
|
+
mass: springConfig?.mass ?? themeAnimation?.spring?.mass ?? 0.8,
|
|
398
|
+
}),
|
|
399
|
+
[springConfig, themeAnimation],
|
|
400
|
+
);
|
|
390
401
|
|
|
391
402
|
/* -------------------------------------------------------------------- */
|
|
392
|
-
/*
|
|
403
|
+
/* Reanimated values */
|
|
393
404
|
/* -------------------------------------------------------------------- */
|
|
394
405
|
|
|
395
406
|
const opacity = useSharedValue(0);
|
|
@@ -412,22 +423,6 @@ const UIModal = forwardRef(function UIModal(
|
|
|
412
423
|
|
|
413
424
|
const nativeTranslateY = useRef(new RNAnimated.Value(0)).current;
|
|
414
425
|
|
|
415
|
-
/* -------------------------------------------------------------------- */
|
|
416
|
-
/* Spring configuration */
|
|
417
|
-
/* -------------------------------------------------------------------- */
|
|
418
|
-
|
|
419
|
-
const resolvedSpringConfig = useMemo(
|
|
420
|
-
() => ({
|
|
421
|
-
damping: springConfig?.damping ?? themeAnimation?.spring?.damping ?? 18,
|
|
422
|
-
|
|
423
|
-
stiffness:
|
|
424
|
-
springConfig?.stiffness ?? themeAnimation?.spring?.stiffness ?? 180,
|
|
425
|
-
|
|
426
|
-
mass: springConfig?.mass ?? themeAnimation?.spring?.mass ?? 0.8,
|
|
427
|
-
}),
|
|
428
|
-
[springConfig, themeAnimation],
|
|
429
|
-
);
|
|
430
|
-
|
|
431
426
|
/* -------------------------------------------------------------------- */
|
|
432
427
|
/* Visibility setter */
|
|
433
428
|
/* -------------------------------------------------------------------- */
|
|
@@ -444,10 +439,10 @@ const UIModal = forwardRef(function UIModal(
|
|
|
444
439
|
);
|
|
445
440
|
|
|
446
441
|
/* -------------------------------------------------------------------- */
|
|
447
|
-
/* Initial animation values
|
|
442
|
+
/* Initial animation values */
|
|
448
443
|
/* -------------------------------------------------------------------- */
|
|
449
444
|
|
|
450
|
-
const
|
|
445
|
+
const getInitialValues = useCallback(() => {
|
|
451
446
|
switch (resolvedAnimation) {
|
|
452
447
|
case "none":
|
|
453
448
|
return {
|
|
@@ -525,11 +520,11 @@ const UIModal = forwardRef(function UIModal(
|
|
|
525
520
|
}, [resolvedAnimation]);
|
|
526
521
|
|
|
527
522
|
/* -------------------------------------------------------------------- */
|
|
528
|
-
/* Reanimated
|
|
523
|
+
/* Reanimated OPEN */
|
|
529
524
|
/* -------------------------------------------------------------------- */
|
|
530
525
|
|
|
531
526
|
const animateReanimatedOpen = useCallback(() => {
|
|
532
|
-
const initial =
|
|
527
|
+
const initial = getInitialValues();
|
|
533
528
|
|
|
534
529
|
opacity.value = initial.opacity;
|
|
535
530
|
|
|
@@ -569,7 +564,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
569
564
|
duration: resolvedAnimationDuration,
|
|
570
565
|
});
|
|
571
566
|
}, [
|
|
572
|
-
|
|
567
|
+
getInitialValues,
|
|
573
568
|
opacity,
|
|
574
569
|
scale,
|
|
575
570
|
translateX,
|
|
@@ -580,7 +575,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
580
575
|
]);
|
|
581
576
|
|
|
582
577
|
/* -------------------------------------------------------------------- */
|
|
583
|
-
/* Reanimated
|
|
578
|
+
/* Reanimated CLOSE */
|
|
584
579
|
/* -------------------------------------------------------------------- */
|
|
585
580
|
|
|
586
581
|
const animateReanimatedClose = useCallback(() => {
|
|
@@ -606,9 +601,11 @@ const UIModal = forwardRef(function UIModal(
|
|
|
606
601
|
duration: resolvedAnimationDuration,
|
|
607
602
|
});
|
|
608
603
|
|
|
609
|
-
setTimeout(() => {
|
|
604
|
+
const timer = setTimeout(() => {
|
|
610
605
|
setModalVisible(false);
|
|
611
606
|
}, resolvedAnimationDuration);
|
|
607
|
+
|
|
608
|
+
return () => clearTimeout(timer);
|
|
612
609
|
}, [
|
|
613
610
|
resolvedAnimation,
|
|
614
611
|
opacity,
|
|
@@ -619,11 +616,11 @@ const UIModal = forwardRef(function UIModal(
|
|
|
619
616
|
]);
|
|
620
617
|
|
|
621
618
|
/* -------------------------------------------------------------------- */
|
|
622
|
-
/* Native
|
|
619
|
+
/* Native OPEN */
|
|
623
620
|
/* -------------------------------------------------------------------- */
|
|
624
621
|
|
|
625
622
|
const animateNativeOpen = useCallback(() => {
|
|
626
|
-
const initial =
|
|
623
|
+
const initial = getInitialValues();
|
|
627
624
|
|
|
628
625
|
nativeOpacity.setValue(initial.opacity);
|
|
629
626
|
|
|
@@ -637,6 +634,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
637
634
|
|
|
638
635
|
if (resolvedAnimation === "none") {
|
|
639
636
|
nativeOpacity.setValue(1);
|
|
637
|
+
|
|
640
638
|
return;
|
|
641
639
|
}
|
|
642
640
|
|
|
@@ -666,7 +664,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
666
664
|
}),
|
|
667
665
|
]).start();
|
|
668
666
|
}, [
|
|
669
|
-
|
|
667
|
+
getInitialValues,
|
|
670
668
|
nativeOpacity,
|
|
671
669
|
nativeScale,
|
|
672
670
|
nativeTranslateX,
|
|
@@ -677,17 +675,19 @@ const UIModal = forwardRef(function UIModal(
|
|
|
677
675
|
]);
|
|
678
676
|
|
|
679
677
|
/* -------------------------------------------------------------------- */
|
|
680
|
-
/* Native
|
|
678
|
+
/* Native CLOSE */
|
|
681
679
|
/* -------------------------------------------------------------------- */
|
|
682
680
|
|
|
683
681
|
const finishNativeClose = useCallback(() => {
|
|
684
682
|
setModalVisible(false);
|
|
683
|
+
|
|
685
684
|
onClose?.();
|
|
686
685
|
}, [onClose]);
|
|
687
686
|
|
|
688
687
|
const animateNativeClose = useCallback(() => {
|
|
689
688
|
if (resolvedAnimation === "none") {
|
|
690
689
|
finishNativeClose();
|
|
690
|
+
|
|
691
691
|
return;
|
|
692
692
|
}
|
|
693
693
|
|
|
@@ -717,7 +717,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
717
717
|
]);
|
|
718
718
|
|
|
719
719
|
/* -------------------------------------------------------------------- */
|
|
720
|
-
/* Open
|
|
720
|
+
/* Open animation */
|
|
721
721
|
/* -------------------------------------------------------------------- */
|
|
722
722
|
|
|
723
723
|
const animateOpen = useCallback(() => {
|
|
@@ -729,7 +729,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
729
729
|
}, [reanimated, animateReanimatedOpen, animateNativeOpen]);
|
|
730
730
|
|
|
731
731
|
/* -------------------------------------------------------------------- */
|
|
732
|
-
/* Close
|
|
732
|
+
/* Close animation */
|
|
733
733
|
/* -------------------------------------------------------------------- */
|
|
734
734
|
|
|
735
735
|
const animateClose = useCallback(() => {
|
|
@@ -741,7 +741,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
741
741
|
}, [reanimated, animateReanimatedClose, animateNativeClose]);
|
|
742
742
|
|
|
743
743
|
/* -------------------------------------------------------------------- */
|
|
744
|
-
/* Public
|
|
744
|
+
/* Public OPEN */
|
|
745
745
|
/* -------------------------------------------------------------------- */
|
|
746
746
|
|
|
747
747
|
const open = useCallback(() => {
|
|
@@ -757,12 +757,13 @@ const UIModal = forwardRef(function UIModal(
|
|
|
757
757
|
}, [isVisible, setVisibility, animateOpen, onOpen]);
|
|
758
758
|
|
|
759
759
|
/* -------------------------------------------------------------------- */
|
|
760
|
-
/* Public
|
|
760
|
+
/* Public CLOSE */
|
|
761
761
|
/* -------------------------------------------------------------------- */
|
|
762
762
|
|
|
763
763
|
const close = useCallback(() => {
|
|
764
764
|
if (!modalVisible) {
|
|
765
765
|
setVisibility(false);
|
|
766
|
+
|
|
766
767
|
return;
|
|
767
768
|
}
|
|
768
769
|
|
|
@@ -784,7 +785,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
784
785
|
}, [isVisible, close, open]);
|
|
785
786
|
|
|
786
787
|
/* -------------------------------------------------------------------- */
|
|
787
|
-
/*
|
|
788
|
+
/* Ref API */
|
|
788
789
|
/* -------------------------------------------------------------------- */
|
|
789
790
|
|
|
790
791
|
useImperativeHandle(
|
|
@@ -800,7 +801,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
800
801
|
);
|
|
801
802
|
|
|
802
803
|
/* -------------------------------------------------------------------- */
|
|
803
|
-
/* Controlled visibility
|
|
804
|
+
/* Controlled visibility */
|
|
804
805
|
/* -------------------------------------------------------------------- */
|
|
805
806
|
|
|
806
807
|
useEffect(() => {
|
|
@@ -854,6 +855,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
854
855
|
"hardwareBackPress",
|
|
855
856
|
() => {
|
|
856
857
|
close();
|
|
858
|
+
|
|
857
859
|
return true;
|
|
858
860
|
},
|
|
859
861
|
);
|
|
@@ -862,7 +864,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
862
864
|
}, [modalVisible, enableBackHandler, close]);
|
|
863
865
|
|
|
864
866
|
/* -------------------------------------------------------------------- */
|
|
865
|
-
/*
|
|
867
|
+
/* Animated styles */
|
|
866
868
|
/* -------------------------------------------------------------------- */
|
|
867
869
|
|
|
868
870
|
const animatedModalStyle = useAnimatedStyle(
|
|
@@ -886,6 +888,17 @@ const UIModal = forwardRef(function UIModal(
|
|
|
886
888
|
[],
|
|
887
889
|
);
|
|
888
890
|
|
|
891
|
+
/*
|
|
892
|
+
* IMPORTANT:
|
|
893
|
+
* The backdrop uses the same animation progress but applies
|
|
894
|
+
* backdropOpacity to it.
|
|
895
|
+
*
|
|
896
|
+
* Therefore:
|
|
897
|
+
*
|
|
898
|
+
* Modal opacity = 1
|
|
899
|
+
* Overlay opacity = backdropOpacity
|
|
900
|
+
*/
|
|
901
|
+
|
|
889
902
|
const animatedBackdropStyle = useAnimatedStyle(
|
|
890
903
|
() => ({
|
|
891
904
|
opacity: opacity.value * backdropOpacity,
|
|
@@ -918,6 +931,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
918
931
|
const nativeBackdropStyle = {
|
|
919
932
|
opacity: nativeOpacity.interpolate({
|
|
920
933
|
inputRange: [0, 1],
|
|
934
|
+
|
|
921
935
|
outputRange: [0, backdropOpacity],
|
|
922
936
|
}),
|
|
923
937
|
};
|
|
@@ -955,7 +969,9 @@ const UIModal = forwardRef(function UIModal(
|
|
|
955
969
|
return {
|
|
956
970
|
width: "100%",
|
|
957
971
|
height: "100%",
|
|
972
|
+
|
|
958
973
|
margin: 0,
|
|
974
|
+
|
|
959
975
|
borderRadius: 0,
|
|
960
976
|
};
|
|
961
977
|
}
|
|
@@ -1008,10 +1024,10 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1008
1024
|
]);
|
|
1009
1025
|
|
|
1010
1026
|
/* -------------------------------------------------------------------- */
|
|
1011
|
-
/* Modal
|
|
1027
|
+
/* Modal card */
|
|
1012
1028
|
/* -------------------------------------------------------------------- */
|
|
1013
1029
|
|
|
1014
|
-
const
|
|
1030
|
+
const modalCardStyle = [
|
|
1015
1031
|
modalStyles.modal,
|
|
1016
1032
|
|
|
1017
1033
|
{
|
|
@@ -1024,13 +1040,13 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1024
1040
|
minHeight,
|
|
1025
1041
|
maxHeight,
|
|
1026
1042
|
|
|
1027
|
-
marginTop: resolvedVariant === "fullscreen" ? 0 :
|
|
1043
|
+
marginTop: resolvedVariant === "fullscreen" ? 0 : finalMarginTop,
|
|
1028
1044
|
|
|
1029
|
-
marginBottom: resolvedVariant === "fullscreen" ? 0 :
|
|
1045
|
+
marginBottom: resolvedVariant === "fullscreen" ? 0 : finalMarginBottom,
|
|
1030
1046
|
|
|
1031
|
-
marginLeft: resolvedVariant === "fullscreen" ? 0 :
|
|
1047
|
+
marginLeft: resolvedVariant === "fullscreen" ? 0 : finalMarginLeft,
|
|
1032
1048
|
|
|
1033
|
-
marginRight: resolvedVariant === "fullscreen" ? 0 :
|
|
1049
|
+
marginRight: resolvedVariant === "fullscreen" ? 0 : finalMarginRight,
|
|
1034
1050
|
|
|
1035
1051
|
backgroundColor: resolvedBackgroundColor,
|
|
1036
1052
|
|
|
@@ -1049,16 +1065,16 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1049
1065
|
borderBottomRightRadius: borderBottomRightRadius ?? resolvedBorderRadius,
|
|
1050
1066
|
|
|
1051
1067
|
paddingTop:
|
|
1052
|
-
|
|
1068
|
+
finalPaddingTop +
|
|
1053
1069
|
(safeArea && resolvedPosition === "top" ? insets.top : 0),
|
|
1054
1070
|
|
|
1055
1071
|
paddingBottom:
|
|
1056
|
-
|
|
1072
|
+
finalPaddingBottom +
|
|
1057
1073
|
(safeArea && resolvedPosition === "bottom" ? insets.bottom : 0),
|
|
1058
1074
|
|
|
1059
|
-
paddingLeft:
|
|
1075
|
+
paddingLeft: finalPaddingLeft,
|
|
1060
1076
|
|
|
1061
|
-
paddingRight:
|
|
1077
|
+
paddingRight: finalPaddingRight,
|
|
1062
1078
|
|
|
1063
1079
|
...(shadow ? (shadows?.md ?? {}) : {}),
|
|
1064
1080
|
},
|
|
@@ -1081,16 +1097,57 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1081
1097
|
return null;
|
|
1082
1098
|
}
|
|
1083
1099
|
|
|
1084
|
-
/*
|
|
1085
|
-
|
|
1086
|
-
|
|
1100
|
+
/*
|
|
1101
|
+
* Header structure:
|
|
1102
|
+
*
|
|
1103
|
+
* ┌──────────────────────────────────┐
|
|
1104
|
+
* │ │
|
|
1105
|
+
* │ CENTER TITLE X │
|
|
1106
|
+
* │ │
|
|
1107
|
+
* └──────────────────────────────────┘
|
|
1108
|
+
*
|
|
1109
|
+
* The center title is absolute.
|
|
1110
|
+
* The close button has its own right-side
|
|
1111
|
+
* slot, so it never sits on top of the title.
|
|
1112
|
+
*/
|
|
1087
1113
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
>
|
|
1093
|
-
{typeof title === "string" ? (
|
|
1114
|
+
return (
|
|
1115
|
+
<View style={[modalStyles.header, headerStyle]}>
|
|
1116
|
+
{/* LEFT SIDE -------------------------------------------------- */}
|
|
1117
|
+
|
|
1118
|
+
<View style={modalStyles.headerLeft}>
|
|
1119
|
+
{resolvedTitlePosition === "left" && typeof title === "string" ? (
|
|
1120
|
+
<Text
|
|
1121
|
+
numberOfLines={1}
|
|
1122
|
+
style={[
|
|
1123
|
+
modalStyles.title,
|
|
1124
|
+
|
|
1125
|
+
{
|
|
1126
|
+
fontSize: titleFontSize,
|
|
1127
|
+
|
|
1128
|
+
color: resolvedTitleColor,
|
|
1129
|
+
|
|
1130
|
+
fontWeight: titleFontWeight,
|
|
1131
|
+
|
|
1132
|
+
...(titleLineHeight
|
|
1133
|
+
? {
|
|
1134
|
+
lineHeight: titleLineHeight,
|
|
1135
|
+
}
|
|
1136
|
+
: {}),
|
|
1137
|
+
},
|
|
1138
|
+
|
|
1139
|
+
titleStyle,
|
|
1140
|
+
]}
|
|
1141
|
+
>
|
|
1142
|
+
{title}
|
|
1143
|
+
</Text>
|
|
1144
|
+
) : null}
|
|
1145
|
+
</View>
|
|
1146
|
+
|
|
1147
|
+
{/* CENTER ----------------------------------------------------- */}
|
|
1148
|
+
|
|
1149
|
+
<View pointerEvents="none" style={modalStyles.headerCenter}>
|
|
1150
|
+
{resolvedTitlePosition === "center" && typeof title === "string" ? (
|
|
1094
1151
|
<Text
|
|
1095
1152
|
numberOfLines={1}
|
|
1096
1153
|
style={[
|
|
@@ -1117,9 +1174,40 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1117
1174
|
>
|
|
1118
1175
|
{title}
|
|
1119
1176
|
</Text>
|
|
1120
|
-
) :
|
|
1121
|
-
|
|
1122
|
-
|
|
1177
|
+
) : null}
|
|
1178
|
+
</View>
|
|
1179
|
+
|
|
1180
|
+
{/* RIGHT SIDE ------------------------------------------------- */}
|
|
1181
|
+
|
|
1182
|
+
<View style={modalStyles.headerRight}>
|
|
1183
|
+
{resolvedTitlePosition === "right" && typeof title === "string" ? (
|
|
1184
|
+
<Text
|
|
1185
|
+
numberOfLines={1}
|
|
1186
|
+
style={[
|
|
1187
|
+
modalStyles.title,
|
|
1188
|
+
|
|
1189
|
+
{
|
|
1190
|
+
fontSize: titleFontSize,
|
|
1191
|
+
|
|
1192
|
+
color: resolvedTitleColor,
|
|
1193
|
+
|
|
1194
|
+
fontWeight: titleFontWeight,
|
|
1195
|
+
|
|
1196
|
+
...(titleLineHeight
|
|
1197
|
+
? {
|
|
1198
|
+
lineHeight: titleLineHeight,
|
|
1199
|
+
}
|
|
1200
|
+
: {}),
|
|
1201
|
+
|
|
1202
|
+
textAlign: "right",
|
|
1203
|
+
},
|
|
1204
|
+
|
|
1205
|
+
titleStyle,
|
|
1206
|
+
]}
|
|
1207
|
+
>
|
|
1208
|
+
{title}
|
|
1209
|
+
</Text>
|
|
1210
|
+
) : null}
|
|
1123
1211
|
|
|
1124
1212
|
{showClose ? (
|
|
1125
1213
|
<Pressable
|
|
@@ -1127,7 +1215,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1127
1215
|
accessibilityLabel="Close"
|
|
1128
1216
|
hitSlop={10}
|
|
1129
1217
|
onPress={() => {
|
|
1130
|
-
if (onClosePress) {
|
|
1218
|
+
if (typeof onClosePress === "function") {
|
|
1131
1219
|
onClosePress();
|
|
1132
1220
|
} else {
|
|
1133
1221
|
close();
|
|
@@ -1143,77 +1231,6 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1143
1231
|
</Pressable>
|
|
1144
1232
|
) : null}
|
|
1145
1233
|
</View>
|
|
1146
|
-
);
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
/* ---------------------------------------------------------------- */
|
|
1150
|
-
/* Left / Right */
|
|
1151
|
-
/* ---------------------------------------------------------------- */
|
|
1152
|
-
|
|
1153
|
-
return (
|
|
1154
|
-
<View
|
|
1155
|
-
style={[
|
|
1156
|
-
modalStyles.header,
|
|
1157
|
-
|
|
1158
|
-
resolvedTitlePosition === "right" && modalStyles.headerRight,
|
|
1159
|
-
|
|
1160
|
-
headerStyle,
|
|
1161
|
-
]}
|
|
1162
|
-
>
|
|
1163
|
-
{typeof title === "string" ? (
|
|
1164
|
-
<Text
|
|
1165
|
-
numberOfLines={1}
|
|
1166
|
-
style={[
|
|
1167
|
-
modalStyles.title,
|
|
1168
|
-
|
|
1169
|
-
{
|
|
1170
|
-
fontSize: titleFontSize,
|
|
1171
|
-
|
|
1172
|
-
color: resolvedTitleColor,
|
|
1173
|
-
|
|
1174
|
-
fontWeight: titleFontWeight,
|
|
1175
|
-
|
|
1176
|
-
...(titleLineHeight
|
|
1177
|
-
? {
|
|
1178
|
-
lineHeight: titleLineHeight,
|
|
1179
|
-
}
|
|
1180
|
-
: {}),
|
|
1181
|
-
},
|
|
1182
|
-
|
|
1183
|
-
resolvedTitlePosition === "right" && {
|
|
1184
|
-
textAlign: "right",
|
|
1185
|
-
},
|
|
1186
|
-
|
|
1187
|
-
titleStyle,
|
|
1188
|
-
]}
|
|
1189
|
-
>
|
|
1190
|
-
{title}
|
|
1191
|
-
</Text>
|
|
1192
|
-
) : (
|
|
1193
|
-
title
|
|
1194
|
-
)}
|
|
1195
|
-
|
|
1196
|
-
{showClose ? (
|
|
1197
|
-
<Pressable
|
|
1198
|
-
accessibilityRole="button"
|
|
1199
|
-
accessibilityLabel="Close"
|
|
1200
|
-
hitSlop={10}
|
|
1201
|
-
onPress={() => {
|
|
1202
|
-
if (onClosePress) {
|
|
1203
|
-
onClosePress();
|
|
1204
|
-
} else {
|
|
1205
|
-
close();
|
|
1206
|
-
}
|
|
1207
|
-
}}
|
|
1208
|
-
style={[modalStyles.closeButton, closeButtonStyle]}
|
|
1209
|
-
>
|
|
1210
|
-
<Ionicons
|
|
1211
|
-
name={closeIcon}
|
|
1212
|
-
size={closeIconSize}
|
|
1213
|
-
color={resolvedCloseIconColor}
|
|
1214
|
-
/>
|
|
1215
|
-
</Pressable>
|
|
1216
|
-
) : null}
|
|
1217
1234
|
</View>
|
|
1218
1235
|
);
|
|
1219
1236
|
};
|
|
@@ -1248,22 +1265,6 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1248
1265
|
);
|
|
1249
1266
|
};
|
|
1250
1267
|
|
|
1251
|
-
/* -------------------------------------------------------------------- */
|
|
1252
|
-
/* Card content */
|
|
1253
|
-
/* -------------------------------------------------------------------- */
|
|
1254
|
-
|
|
1255
|
-
const cardContent = (
|
|
1256
|
-
<>
|
|
1257
|
-
{renderHeader()}
|
|
1258
|
-
|
|
1259
|
-
{renderContent()}
|
|
1260
|
-
|
|
1261
|
-
{footer ? (
|
|
1262
|
-
<View style={[modalStyles.footer, footerStyle]}>{footer}</View>
|
|
1263
|
-
) : null}
|
|
1264
|
-
</>
|
|
1265
|
-
);
|
|
1266
|
-
|
|
1267
1268
|
/* -------------------------------------------------------------------- */
|
|
1268
1269
|
/* Backdrop */
|
|
1269
1270
|
/* -------------------------------------------------------------------- */
|
|
@@ -1273,15 +1274,26 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1273
1274
|
return null;
|
|
1274
1275
|
}
|
|
1275
1276
|
|
|
1276
|
-
const
|
|
1277
|
+
const handleBackdropPress = () => {
|
|
1277
1278
|
if (closeOnBackdropPress) {
|
|
1278
1279
|
close();
|
|
1279
1280
|
}
|
|
1280
1281
|
};
|
|
1281
1282
|
|
|
1283
|
+
/*
|
|
1284
|
+
* IMPORTANT:
|
|
1285
|
+
*
|
|
1286
|
+
* This backdrop is absolute-fill,
|
|
1287
|
+
* so it covers the ENTIRE screen.
|
|
1288
|
+
*
|
|
1289
|
+
* The modal card is rendered separately
|
|
1290
|
+
* above it.
|
|
1291
|
+
*/
|
|
1292
|
+
|
|
1282
1293
|
if (reanimated) {
|
|
1283
1294
|
return (
|
|
1284
1295
|
<Animated.View
|
|
1296
|
+
pointerEvents="box-none"
|
|
1285
1297
|
style={[
|
|
1286
1298
|
modalStyles.backdrop,
|
|
1287
1299
|
|
|
@@ -1293,7 +1305,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1293
1305
|
]}
|
|
1294
1306
|
>
|
|
1295
1307
|
<Pressable
|
|
1296
|
-
onPress={
|
|
1308
|
+
onPress={handleBackdropPress}
|
|
1297
1309
|
style={StyleSheet.absoluteFillObject}
|
|
1298
1310
|
/>
|
|
1299
1311
|
</Animated.View>
|
|
@@ -1302,6 +1314,7 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1302
1314
|
|
|
1303
1315
|
return (
|
|
1304
1316
|
<RNAnimated.View
|
|
1317
|
+
pointerEvents="box-none"
|
|
1305
1318
|
style={[
|
|
1306
1319
|
modalStyles.backdrop,
|
|
1307
1320
|
|
|
@@ -1313,13 +1326,29 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1313
1326
|
]}
|
|
1314
1327
|
>
|
|
1315
1328
|
<Pressable
|
|
1316
|
-
onPress={
|
|
1329
|
+
onPress={handleBackdropPress}
|
|
1317
1330
|
style={StyleSheet.absoluteFillObject}
|
|
1318
1331
|
/>
|
|
1319
1332
|
</RNAnimated.View>
|
|
1320
1333
|
);
|
|
1321
1334
|
};
|
|
1322
1335
|
|
|
1336
|
+
/* -------------------------------------------------------------------- */
|
|
1337
|
+
/* Card content */
|
|
1338
|
+
/* -------------------------------------------------------------------- */
|
|
1339
|
+
|
|
1340
|
+
const cardContent = (
|
|
1341
|
+
<>
|
|
1342
|
+
{renderHeader()}
|
|
1343
|
+
|
|
1344
|
+
{renderContent()}
|
|
1345
|
+
|
|
1346
|
+
{footer ? (
|
|
1347
|
+
<View style={[modalStyles.footer, footerStyle]}>{footer}</View>
|
|
1348
|
+
) : null}
|
|
1349
|
+
</>
|
|
1350
|
+
);
|
|
1351
|
+
|
|
1323
1352
|
/* -------------------------------------------------------------------- */
|
|
1324
1353
|
/* Closed */
|
|
1325
1354
|
/* -------------------------------------------------------------------- */
|
|
@@ -1329,19 +1358,23 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1329
1358
|
}
|
|
1330
1359
|
|
|
1331
1360
|
/* -------------------------------------------------------------------- */
|
|
1332
|
-
/*
|
|
1361
|
+
/* Animated card */
|
|
1333
1362
|
/* -------------------------------------------------------------------- */
|
|
1334
1363
|
|
|
1335
1364
|
const renderedCard = reanimated ? (
|
|
1336
|
-
<Animated.View style={[
|
|
1365
|
+
<Animated.View style={[modalCardStyle, animatedModalStyle]}>
|
|
1337
1366
|
{cardContent}
|
|
1338
1367
|
</Animated.View>
|
|
1339
1368
|
) : (
|
|
1340
|
-
<RNAnimated.View style={[
|
|
1369
|
+
<RNAnimated.View style={[modalCardStyle, nativeModalStyle]}>
|
|
1341
1370
|
{cardContent}
|
|
1342
1371
|
</RNAnimated.View>
|
|
1343
1372
|
);
|
|
1344
1373
|
|
|
1374
|
+
/* -------------------------------------------------------------------- */
|
|
1375
|
+
/* Render */
|
|
1376
|
+
/* -------------------------------------------------------------------- */
|
|
1377
|
+
|
|
1345
1378
|
return (
|
|
1346
1379
|
<Modal
|
|
1347
1380
|
visible={modalVisible}
|
|
@@ -1352,8 +1385,10 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1352
1385
|
{...modalProps}
|
|
1353
1386
|
>
|
|
1354
1387
|
<View testID={testID} style={[modalStyles.modalRoot, positionStyle]}>
|
|
1388
|
+
{/* FULL SCREEN OVERLAY */}
|
|
1355
1389
|
{renderBackdrop()}
|
|
1356
1390
|
|
|
1391
|
+
{/* MODAL CARD */}
|
|
1357
1392
|
{keyboardAvoiding ? (
|
|
1358
1393
|
<KeyboardAvoidingView
|
|
1359
1394
|
behavior={
|
|
@@ -1373,6 +1408,10 @@ const UIModal = forwardRef(function UIModal(
|
|
|
1373
1408
|
);
|
|
1374
1409
|
});
|
|
1375
1410
|
|
|
1411
|
+
/* -------------------------------------------------------------------------- */
|
|
1412
|
+
/* Display name */
|
|
1413
|
+
/* -------------------------------------------------------------------------- */
|
|
1414
|
+
|
|
1376
1415
|
UIModal.displayName = "UIModal";
|
|
1377
1416
|
|
|
1378
1417
|
/* -------------------------------------------------------------------------- */
|
|
@@ -1380,75 +1419,144 @@ UIModal.displayName = "UIModal";
|
|
|
1380
1419
|
/* -------------------------------------------------------------------------- */
|
|
1381
1420
|
|
|
1382
1421
|
const modalStyles = StyleSheet.create({
|
|
1422
|
+
/* -------------------------------------------------------------------- */
|
|
1423
|
+
/* Root */
|
|
1424
|
+
/* -------------------------------------------------------------------- */
|
|
1425
|
+
|
|
1383
1426
|
modalRoot: {
|
|
1384
1427
|
flex: 1,
|
|
1428
|
+
|
|
1385
1429
|
width: "100%",
|
|
1386
1430
|
},
|
|
1387
1431
|
|
|
1432
|
+
/* -------------------------------------------------------------------- */
|
|
1433
|
+
/* Keyboard */
|
|
1434
|
+
/* -------------------------------------------------------------------- */
|
|
1435
|
+
|
|
1388
1436
|
keyboardContainer: {
|
|
1389
1437
|
width: "100%",
|
|
1438
|
+
|
|
1390
1439
|
alignItems: "center",
|
|
1440
|
+
|
|
1391
1441
|
justifyContent: "center",
|
|
1392
1442
|
},
|
|
1393
1443
|
|
|
1444
|
+
/* -------------------------------------------------------------------- */
|
|
1445
|
+
/* Full screen backdrop */
|
|
1446
|
+
/* -------------------------------------------------------------------- */
|
|
1447
|
+
|
|
1394
1448
|
backdrop: {
|
|
1395
1449
|
...StyleSheet.absoluteFillObject,
|
|
1450
|
+
|
|
1451
|
+
zIndex: 0,
|
|
1396
1452
|
},
|
|
1397
1453
|
|
|
1454
|
+
/* -------------------------------------------------------------------- */
|
|
1455
|
+
/* Modal */
|
|
1456
|
+
/* -------------------------------------------------------------------- */
|
|
1457
|
+
|
|
1398
1458
|
modal: {
|
|
1399
1459
|
overflow: "hidden",
|
|
1460
|
+
|
|
1400
1461
|
maxWidth: "100%",
|
|
1462
|
+
|
|
1463
|
+
zIndex: 10,
|
|
1401
1464
|
},
|
|
1402
1465
|
|
|
1403
1466
|
/* -------------------------------------------------------------------- */
|
|
1404
|
-
/* Header
|
|
1467
|
+
/* Header */
|
|
1405
1468
|
/* -------------------------------------------------------------------- */
|
|
1406
1469
|
|
|
1407
1470
|
header: {
|
|
1408
1471
|
width: "100%",
|
|
1472
|
+
|
|
1409
1473
|
minHeight: 52,
|
|
1410
1474
|
|
|
1411
1475
|
flexDirection: "row",
|
|
1412
1476
|
|
|
1413
1477
|
alignItems: "center",
|
|
1478
|
+
|
|
1479
|
+
position: "relative",
|
|
1480
|
+
},
|
|
1481
|
+
|
|
1482
|
+
/* -------------------------------------------------------------------- */
|
|
1483
|
+
/* Left header area */
|
|
1484
|
+
/* -------------------------------------------------------------------- */
|
|
1485
|
+
|
|
1486
|
+
headerLeft: {
|
|
1487
|
+
flex: 1,
|
|
1488
|
+
|
|
1489
|
+
minWidth: 0,
|
|
1490
|
+
|
|
1491
|
+
justifyContent: "center",
|
|
1492
|
+
|
|
1493
|
+
alignItems: "flex-start",
|
|
1414
1494
|
},
|
|
1415
1495
|
|
|
1496
|
+
/* -------------------------------------------------------------------- */
|
|
1497
|
+
/* Center header area */
|
|
1498
|
+
/* -------------------------------------------------------------------- */
|
|
1499
|
+
|
|
1416
1500
|
headerCenter: {
|
|
1417
|
-
position: "
|
|
1501
|
+
position: "absolute",
|
|
1502
|
+
|
|
1503
|
+
left: 0,
|
|
1504
|
+
|
|
1505
|
+
right: 0,
|
|
1506
|
+
|
|
1507
|
+
top: 0,
|
|
1508
|
+
|
|
1509
|
+
bottom: 0,
|
|
1510
|
+
|
|
1511
|
+
alignItems: "center",
|
|
1418
1512
|
|
|
1419
1513
|
justifyContent: "center",
|
|
1514
|
+
|
|
1515
|
+
zIndex: 1,
|
|
1516
|
+
|
|
1517
|
+
paddingHorizontal: 50,
|
|
1420
1518
|
},
|
|
1421
1519
|
|
|
1520
|
+
/* -------------------------------------------------------------------- */
|
|
1521
|
+
/* Right header area */
|
|
1522
|
+
/* -------------------------------------------------------------------- */
|
|
1523
|
+
|
|
1422
1524
|
headerRight: {
|
|
1525
|
+
flex: 1,
|
|
1526
|
+
|
|
1527
|
+
minWidth: 0,
|
|
1528
|
+
|
|
1529
|
+
flexDirection: "row",
|
|
1530
|
+
|
|
1531
|
+
alignItems: "center",
|
|
1532
|
+
|
|
1423
1533
|
justifyContent: "flex-end",
|
|
1534
|
+
|
|
1535
|
+
zIndex: 2,
|
|
1424
1536
|
},
|
|
1425
1537
|
|
|
1426
1538
|
/* -------------------------------------------------------------------- */
|
|
1427
|
-
/* Title
|
|
1539
|
+
/* Title */
|
|
1428
1540
|
/* -------------------------------------------------------------------- */
|
|
1429
1541
|
|
|
1430
1542
|
title: {
|
|
1431
|
-
flex: 1,
|
|
1432
|
-
|
|
1433
1543
|
fontSize: 18,
|
|
1434
1544
|
|
|
1435
1545
|
lineHeight: 24,
|
|
1436
1546
|
|
|
1437
1547
|
fontWeight: "700",
|
|
1548
|
+
|
|
1549
|
+
includeFontPadding: false,
|
|
1438
1550
|
},
|
|
1439
1551
|
|
|
1440
1552
|
titleCenter: {
|
|
1441
|
-
position: "absolute",
|
|
1442
|
-
|
|
1443
|
-
left: 0,
|
|
1444
|
-
|
|
1445
|
-
right: 0,
|
|
1446
|
-
|
|
1447
1553
|
textAlign: "center",
|
|
1554
|
+
|
|
1555
|
+
flexShrink: 1,
|
|
1448
1556
|
},
|
|
1449
1557
|
|
|
1450
1558
|
/* -------------------------------------------------------------------- */
|
|
1451
|
-
/* Close
|
|
1559
|
+
/* Close */
|
|
1452
1560
|
/* -------------------------------------------------------------------- */
|
|
1453
1561
|
|
|
1454
1562
|
closeButton: {
|
|
@@ -1460,11 +1568,13 @@ const modalStyles = StyleSheet.create({
|
|
|
1460
1568
|
|
|
1461
1569
|
justifyContent: "center",
|
|
1462
1570
|
|
|
1571
|
+
marginLeft: 8,
|
|
1572
|
+
|
|
1463
1573
|
zIndex: 20,
|
|
1464
1574
|
},
|
|
1465
1575
|
|
|
1466
1576
|
/* -------------------------------------------------------------------- */
|
|
1467
|
-
/* Content
|
|
1577
|
+
/* Content */
|
|
1468
1578
|
/* -------------------------------------------------------------------- */
|
|
1469
1579
|
|
|
1470
1580
|
content: {
|
|
@@ -1484,7 +1594,7 @@ const modalStyles = StyleSheet.create({
|
|
|
1484
1594
|
},
|
|
1485
1595
|
|
|
1486
1596
|
/* -------------------------------------------------------------------- */
|
|
1487
|
-
/* Footer
|
|
1597
|
+
/* Footer */
|
|
1488
1598
|
/* -------------------------------------------------------------------- */
|
|
1489
1599
|
|
|
1490
1600
|
footer: {
|
|
@@ -1493,7 +1603,7 @@ const modalStyles = StyleSheet.create({
|
|
|
1493
1603
|
});
|
|
1494
1604
|
|
|
1495
1605
|
/* -------------------------------------------------------------------------- */
|
|
1496
|
-
/*
|
|
1606
|
+
/* Export */
|
|
1497
1607
|
/* -------------------------------------------------------------------------- */
|
|
1498
1608
|
|
|
1499
1609
|
export { UIModal };
|