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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnnovaui",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "react-native": "src/index.js",
@@ -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 state */
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
- /* Resolve variant */
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 colors */
327
+ /* Theme values */
335
328
  /* -------------------------------------------------------------------- */
336
329
 
337
- const resolvedBackgroundColor =
338
- backgroundColor ??
339
- colors.card ??
340
- colors.surface ??
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 resolvedTitleColor = titleColor ?? colors.text ?? "#111111";
335
+ const resolvedBorderColor = getSafeValue(
336
+ borderColor,
337
+ colors.border ?? "#E5E5E5",
338
+ );
347
339
 
348
- const resolvedCloseIconColor = closeIconColor ?? colors.text ?? "#111111";
340
+ const resolvedTitleColor = getSafeValue(titleColor, colors.text ?? "#111111");
349
341
 
350
- const resolvedBorderRadius = borderRadius ?? radius.xl ?? 18;
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 ?? themeAnimation.normal ?? 280;
349
+ const resolvedAnimationDuration = getSafeValue(
350
+ animationDuration,
351
+ themeAnimation.normal ?? 280,
352
+ );
358
353
 
359
354
  /* -------------------------------------------------------------------- */
360
355
  /* Padding */
361
356
  /* -------------------------------------------------------------------- */
362
357
 
363
- const horizontalPadding = paddingHorizontal ?? padding;
358
+ const finalHorizontalPadding = paddingHorizontal ?? padding;
364
359
 
365
- const verticalPadding = paddingVertical ?? padding;
360
+ const finalVerticalPadding = paddingVertical ?? padding;
366
361
 
367
- const resolvedPaddingTop = paddingTop ?? verticalPadding;
362
+ const finalPaddingTop = paddingTop ?? finalVerticalPadding;
368
363
 
369
- const resolvedPaddingBottom = paddingBottom ?? verticalPadding;
364
+ const finalPaddingBottom = paddingBottom ?? finalVerticalPadding;
370
365
 
371
- const resolvedPaddingLeft = paddingLeft ?? horizontalPadding;
366
+ const finalPaddingLeft = paddingLeft ?? finalHorizontalPadding;
372
367
 
373
- const resolvedPaddingRight = paddingRight ?? horizontalPadding;
368
+ const finalPaddingRight = paddingRight ?? finalHorizontalPadding;
374
369
 
375
370
  /* -------------------------------------------------------------------- */
376
371
  /* Margin */
377
372
  /* -------------------------------------------------------------------- */
378
373
 
379
- const horizontalMargin = marginHorizontal ?? margin;
374
+ const finalHorizontalMargin = marginHorizontal ?? margin;
375
+
376
+ const finalVerticalMargin = marginVertical ?? margin;
377
+
378
+ const finalMarginTop = marginTop ?? finalVerticalMargin;
380
379
 
381
- const verticalMargin = marginVertical ?? margin;
380
+ const finalMarginBottom = marginBottom ?? finalVerticalMargin;
382
381
 
383
- const resolvedMarginTop = marginTop ?? verticalMargin;
382
+ const finalMarginLeft = marginLeft ?? finalHorizontalMargin;
384
383
 
385
- const resolvedMarginBottom = marginBottom ?? verticalMargin;
384
+ const finalMarginRight = marginRight ?? finalHorizontalMargin;
386
385
 
387
- const resolvedMarginLeft = marginLeft ?? horizontalMargin;
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
- const resolvedMarginRight = marginRight ?? horizontalMargin;
397
+ mass: springConfig?.mass ?? themeAnimation?.spring?.mass ?? 0.8,
398
+ }),
399
+ [springConfig, themeAnimation],
400
+ );
390
401
 
391
402
  /* -------------------------------------------------------------------- */
392
- /* Animation values */
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 getInitialAnimationValues = useCallback(() => {
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 open */
523
+ /* Reanimated OPEN */
529
524
  /* -------------------------------------------------------------------- */
530
525
 
531
526
  const animateReanimatedOpen = useCallback(() => {
532
- const initial = getInitialAnimationValues();
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
- getInitialAnimationValues,
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 close */
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 open */
619
+ /* Native OPEN */
623
620
  /* -------------------------------------------------------------------- */
624
621
 
625
622
  const animateNativeOpen = useCallback(() => {
626
- const initial = getInitialAnimationValues();
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
- getInitialAnimationValues,
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 close */
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 open */
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 close */
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
- /* Imperative API */
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 effect */
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
- /* Reanimated styles */
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 style */
1027
+ /* Modal card */
1012
1028
  /* -------------------------------------------------------------------- */
1013
1029
 
1014
- const modalStyle = [
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 : resolvedMarginTop,
1043
+ marginTop: resolvedVariant === "fullscreen" ? 0 : finalMarginTop,
1028
1044
 
1029
- marginBottom: resolvedVariant === "fullscreen" ? 0 : resolvedMarginBottom,
1045
+ marginBottom: resolvedVariant === "fullscreen" ? 0 : finalMarginBottom,
1030
1046
 
1031
- marginLeft: resolvedVariant === "fullscreen" ? 0 : resolvedMarginLeft,
1047
+ marginLeft: resolvedVariant === "fullscreen" ? 0 : finalMarginLeft,
1032
1048
 
1033
- marginRight: resolvedVariant === "fullscreen" ? 0 : resolvedMarginRight,
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
- resolvedPaddingTop +
1068
+ finalPaddingTop +
1053
1069
  (safeArea && resolvedPosition === "top" ? insets.top : 0),
1054
1070
 
1055
1071
  paddingBottom:
1056
- resolvedPaddingBottom +
1072
+ finalPaddingBottom +
1057
1073
  (safeArea && resolvedPosition === "bottom" ? insets.bottom : 0),
1058
1074
 
1059
- paddingLeft: resolvedPaddingLeft,
1075
+ paddingLeft: finalPaddingLeft,
1060
1076
 
1061
- paddingRight: resolvedPaddingRight,
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
- /* Center */
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
- if (resolvedTitlePosition === "center") {
1089
- return (
1090
- <View
1091
- style={[modalStyles.header, modalStyles.headerCenter, headerStyle]}
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
- title
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 onBackdropPress = () => {
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={onBackdropPress}
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={onBackdropPress}
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
- /* Render */
1361
+ /* Animated card */
1333
1362
  /* -------------------------------------------------------------------- */
1334
1363
 
1335
1364
  const renderedCard = reanimated ? (
1336
- <Animated.View style={[modalStyle, animatedModalStyle]}>
1365
+ <Animated.View style={[modalCardStyle, animatedModalStyle]}>
1337
1366
  {cardContent}
1338
1367
  </Animated.View>
1339
1368
  ) : (
1340
- <RNAnimated.View style={[modalStyle, nativeModalStyle]}>
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: "relative",
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
- /* Exports */
1606
+ /* Export */
1497
1607
  /* -------------------------------------------------------------------------- */
1498
1608
 
1499
1609
  export { UIModal };