@retray-dev/ui-kit 10.0.0 → 10.1.0

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/dist/index.js CHANGED
@@ -12,7 +12,7 @@ var Ionicons = require('@expo/vector-icons/Ionicons');
12
12
  var pressto = require('pressto');
13
13
  var Animated12 = require('react-native-reanimated');
14
14
  var expoFont = require('expo-font');
15
- var bottomSheet = require('@gorhom/bottom-sheet');
15
+ var BottomSheet = require('@gorhom/bottom-sheet');
16
16
  var reactNativeEase = require('react-native-ease');
17
17
  var vectorIcons = require('@expo/vector-icons');
18
18
  var expoLinearGradient = require('expo-linear-gradient');
@@ -32,6 +32,7 @@ var FontAwesome5__default = /*#__PURE__*/_interopDefault(FontAwesome5);
32
32
  var MaterialIcons__default = /*#__PURE__*/_interopDefault(MaterialIcons);
33
33
  var Ionicons__default = /*#__PURE__*/_interopDefault(Ionicons);
34
34
  var Animated12__default = /*#__PURE__*/_interopDefault(Animated12);
35
+ var BottomSheet__default = /*#__PURE__*/_interopDefault(BottomSheet);
35
36
  var RNSlider__default = /*#__PURE__*/_interopDefault(RNSlider);
36
37
 
37
38
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -1035,7 +1036,7 @@ function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suff
1035
1036
  },
1036
1037
  effectivePrefix ? typeof effectivePrefix === "string" ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles4.prefixText, { color: colors.foregroundMuted }, prefixStyle], allowFontScaling: true }, effectivePrefix) : /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles4.prefixContainer }, effectivePrefix) : null,
1037
1038
  sheetMode ? /* @__PURE__ */ React25__default.default.createElement(
1038
- bottomSheet.BottomSheetTextInput,
1039
+ BottomSheet.BottomSheetTextInput,
1039
1040
  {
1040
1041
  style: [
1041
1042
  styles4.input,
@@ -2028,9 +2029,11 @@ var THUMB_SIZE = s(24);
2028
2029
  var THUMB_OFFSET = s(3);
2029
2030
  var THUMB_TRAVEL = TRACK_WIDTH - THUMB_SIZE - THUMB_OFFSET * 2;
2030
2031
  var ICON_SIZE = s(13);
2032
+ var DISABLED_OPACITY = 0.45;
2031
2033
  function Switch({ checked = false, onCheckedChange, disabled, style, accessibilityLabel }) {
2032
2034
  const { colors } = useTheme();
2033
- return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [{ opacity: disabled ? 0.45 : 1, alignSelf: "flex-start" }, style] }, /* @__PURE__ */ React25__default.default.createElement(
2035
+ const isDisabled = !!disabled;
2036
+ return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [{ alignSelf: "flex-start" }, style] }, /* @__PURE__ */ React25__default.default.createElement(
2034
2037
  reactNative.TouchableOpacity,
2035
2038
  {
2036
2039
  onPress: () => {
@@ -2042,47 +2045,68 @@ function Switch({ checked = false, onCheckedChange, disabled, style, accessibili
2042
2045
  touchSoundDisabled: true,
2043
2046
  accessibilityRole: "switch",
2044
2047
  accessibilityLabel,
2045
- accessibilityState: { checked, disabled: !!disabled },
2048
+ accessibilityState: { checked, disabled: isDisabled },
2046
2049
  style: styles16.touchable
2047
2050
  },
2048
- /* @__PURE__ */ React25__default.default.createElement(
2051
+ /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles16.trackContainer }, /* @__PURE__ */ React25__default.default.createElement(
2049
2052
  reactNativeEase.EaseView,
2050
2053
  {
2051
- style: styles16.track,
2054
+ style: [styles16.track, isDisabled && styles16.disabledTrack],
2052
2055
  animate: { backgroundColor: checked ? colors.primary : colors.surfaceStrong },
2053
2056
  transition: COLOR_TRANSITION
2057
+ }
2058
+ ), /* @__PURE__ */ React25__default.default.createElement(
2059
+ reactNativeEase.EaseView,
2060
+ {
2061
+ style: [styles16.trackBorder, { borderWidth: 1.5 }],
2062
+ pointerEvents: "none",
2063
+ animate: { borderColor: checked ? "transparent" : colors.border },
2064
+ transition: COLOR_TRANSITION
2065
+ }
2066
+ ), /* @__PURE__ */ React25__default.default.createElement(
2067
+ reactNativeEase.EaseView,
2068
+ {
2069
+ style: [styles16.thumb, { backgroundColor: colors.primaryForeground }],
2070
+ animate: { translateX: checked ? THUMB_TRAVEL : 0 },
2071
+ transition: SPRING_ELASTIC
2054
2072
  },
2055
2073
  /* @__PURE__ */ React25__default.default.createElement(
2056
2074
  reactNativeEase.EaseView,
2057
2075
  {
2058
- style: [styles16.trackBorder, { borderWidth: 1.5 }],
2059
- pointerEvents: "none",
2060
- animate: { borderColor: checked ? "transparent" : colors.border },
2061
- transition: COLOR_TRANSITION
2062
- }
2076
+ style: styles16.iconWrapper,
2077
+ animate: { opacity: checked ? isDisabled ? DISABLED_OPACITY : 1 : 0 },
2078
+ transition: OPACITY_TRANSITION
2079
+ },
2080
+ /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "check", size: ICON_SIZE, color: colors.primary })
2063
2081
  ),
2064
2082
  /* @__PURE__ */ React25__default.default.createElement(
2065
2083
  reactNativeEase.EaseView,
2066
2084
  {
2067
- style: [styles16.thumb, { backgroundColor: colors.primaryForeground }],
2068
- animate: { translateX: checked ? THUMB_TRAVEL : 0 },
2069
- transition: SPRING_ELASTIC
2085
+ style: styles16.iconWrapper,
2086
+ animate: { opacity: checked ? 0 : isDisabled ? DISABLED_OPACITY : 1 },
2087
+ transition: OPACITY_TRANSITION
2070
2088
  },
2071
- /* @__PURE__ */ React25__default.default.createElement(reactNativeEase.EaseView, { style: styles16.iconWrapper, animate: { opacity: checked ? 1 : 0 }, transition: OPACITY_TRANSITION }, /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "check", size: ICON_SIZE, color: colors.primary })),
2072
- /* @__PURE__ */ React25__default.default.createElement(reactNativeEase.EaseView, { style: styles16.iconWrapper, animate: { opacity: checked ? 0 : 1 }, transition: OPACITY_TRANSITION }, /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "x", size: ICON_SIZE, color: colors.foregroundMuted }))
2089
+ /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "x", size: ICON_SIZE, color: colors.foregroundMuted })
2073
2090
  )
2074
- )
2091
+ ))
2075
2092
  ));
2076
2093
  }
2077
2094
  var styles16 = reactNative.StyleSheet.create({
2078
2095
  touchable: {
2079
2096
  alignSelf: "flex-start"
2080
2097
  },
2081
- track: {
2098
+ trackContainer: {
2099
+ position: "relative",
2082
2100
  width: TRACK_WIDTH,
2083
- height: TRACK_HEIGHT,
2101
+ height: TRACK_HEIGHT
2102
+ },
2103
+ track: {
2104
+ ...reactNative.StyleSheet.absoluteFillObject,
2084
2105
  borderRadius: TRACK_HEIGHT / 2
2085
2106
  },
2107
+ disabledTrack: {
2108
+ opacity: DISABLED_OPACITY
2109
+ },
2086
2110
  trackBorder: {
2087
2111
  ...reactNative.StyleSheet.absoluteFillObject,
2088
2112
  borderRadius: TRACK_HEIGHT / 2
@@ -2601,9 +2625,9 @@ function AccordionItemComponent({
2601
2625
  },
2602
2626
  accessibilityRole: "button",
2603
2627
  accessibilityState: { expanded: isOpen },
2604
- accessibilityLabel: item.trigger
2628
+ accessibilityLabel: typeof item.trigger === "string" ? item.trigger : void 0
2605
2629
  },
2606
- /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles20.triggerContent }, resolvedIcon ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles20.icon }, resolvedIcon) : null, /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles20.triggerText, { color: colors.foreground }], allowFontScaling: true }, item.trigger)),
2630
+ /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles20.triggerContent }, resolvedIcon ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles20.icon }, resolvedIcon) : null, typeof item.trigger === "string" ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles20.triggerText, { color: colors.foreground }], allowFontScaling: true }, item.trigger) : item.trigger),
2607
2631
  /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [styles20.chevron, rotationStyle] }, /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Entypo, { name: "chevron-down", size: 18, color: colors.foregroundMuted }))
2608
2632
  ), /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: bodyStyle }, /* @__PURE__ */ React25__default.default.createElement(
2609
2633
  reactNative.View,
@@ -2811,13 +2835,13 @@ function Sheet({
2811
2835
  React25.useEffect(() => {
2812
2836
  if (open) {
2813
2837
  impactMedium();
2814
- ref.current?.present();
2838
+ ref.current?.snapToIndex(0);
2815
2839
  } else {
2816
- ref.current?.dismiss();
2840
+ ref.current?.close();
2817
2841
  }
2818
2842
  }, [open]);
2819
2843
  const renderBackdrop = React25.useCallback((props) => /* @__PURE__ */ React25__default.default.createElement(
2820
- bottomSheet.BottomSheetBackdrop,
2844
+ BottomSheet.BottomSheetBackdrop,
2821
2845
  {
2822
2846
  ...props,
2823
2847
  disappearsOnIndex: -1,
@@ -2834,7 +2858,7 @@ function Sheet({
2834
2858
  ) : children;
2835
2859
  const effectiveSubtitle = subtitle ?? description;
2836
2860
  const showHeader = !!(title || effectiveSubtitle || showCloseButton) && !customHeader;
2837
- const headerNode = customHeader ? customHeader : showHeader ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles22.header, accessibilityRole: "header" }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles22.headerRow }, title ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles22.title, { color: colors.foreground }], allowFontScaling: true }, title) : /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: { flex: 1 } }), showCloseButton ? /* @__PURE__ */ React25__default.default.createElement(
2861
+ const headerNode = customHeader ? customHeader : showHeader ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles22.header, { backgroundColor: colors.card }], accessibilityRole: "header" }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles22.headerRow }, title ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles22.title, { color: colors.foreground }], allowFontScaling: true }, title) : /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: { flex: 1 } }), showCloseButton ? /* @__PURE__ */ React25__default.default.createElement(
2838
2862
  reactNative.TouchableOpacity,
2839
2863
  {
2840
2864
  onPress: onClose,
@@ -2851,7 +2875,7 @@ function Sheet({
2851
2875
  const effectiveFooter = customFooter ? customFooter : footer;
2852
2876
  const renderFooter = React25.useCallback((props) => {
2853
2877
  if (!effectiveFooter) return null;
2854
- return /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetFooter, { ...props }, effectiveFooter);
2878
+ return /* @__PURE__ */ React25__default.default.createElement(BottomSheet.BottomSheetFooter, { ...props }, effectiveFooter);
2855
2879
  }, [effectiveFooter]);
2856
2880
  if (asDialog) {
2857
2881
  return /* @__PURE__ */ React25__default.default.createElement(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: onClose }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Pressable, { style: styles22.dialogBackdrop, onPress: onClose, accessibilityRole: "button", accessibilityLabel: "Close" }, /* @__PURE__ */ React25__default.default.createElement(
@@ -2882,13 +2906,14 @@ function Sheet({
2882
2906
  const effectiveMaxHeight = maxHeight ?? DEFAULT_MAX_HEIGHT;
2883
2907
  const useDynamicSizing = !snapPoints;
2884
2908
  return /* @__PURE__ */ React25__default.default.createElement(
2885
- bottomSheet.BottomSheetModal,
2909
+ BottomSheet__default.default,
2886
2910
  {
2887
2911
  ref,
2912
+ index: -1,
2913
+ onClose,
2888
2914
  enableDynamicSizing: useDynamicSizing,
2889
2915
  snapPoints,
2890
2916
  maxDynamicContentSize: useDynamicSizing ? effectiveMaxHeight : void 0,
2891
- onDismiss: onClose,
2892
2917
  backdropComponent: renderBackdrop,
2893
2918
  footerComponent: effectiveFooter ? renderFooter : void 0,
2894
2919
  backgroundStyle: [styles22.background, { backgroundColor: colors.card }],
@@ -2901,7 +2926,7 @@ function Sheet({
2901
2926
  enableBlurKeyboardOnGesture
2902
2927
  },
2903
2928
  useScroll ? /* @__PURE__ */ React25__default.default.createElement(
2904
- bottomSheet.BottomSheetScrollView,
2929
+ BottomSheet.BottomSheetScrollView,
2905
2930
  {
2906
2931
  contentContainerStyle: [
2907
2932
  styles22.scrollContent,
@@ -2910,11 +2935,12 @@ function Sheet({
2910
2935
  style: contentStyle,
2911
2936
  showsVerticalScrollIndicator: true,
2912
2937
  indicatorStyle: "black",
2913
- persistentScrollbar: isAndroid
2938
+ persistentScrollbar: isAndroid,
2939
+ stickyHeaderIndices: headerNode ? [0] : void 0
2914
2940
  },
2915
2941
  headerNode,
2916
2942
  contentNode
2917
- ) : /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetView, { style: [styles22.content, contentStyle, style] }, headerNode, contentNode)
2943
+ ) : /* @__PURE__ */ React25__default.default.createElement(BottomSheet.BottomSheetView, { style: [styles22.content, contentStyle, style] }, headerNode, contentNode)
2918
2944
  );
2919
2945
  }
2920
2946
  Sheet.Header = SheetHeader;
@@ -3900,26 +3926,29 @@ var styles29 = reactNative.StyleSheet.create({
3900
3926
  function ConfirmDialog({
3901
3927
  visible,
3902
3928
  title,
3929
+ subtitle,
3903
3930
  description,
3904
3931
  confirmLabel = "Confirm",
3905
3932
  cancelLabel = "Cancel",
3906
3933
  confirmVariant = "primary",
3907
3934
  loading = false,
3935
+ showCloseButton = false,
3908
3936
  onConfirm,
3909
3937
  onCancel
3910
3938
  }) {
3911
3939
  const { colors } = useTheme();
3912
3940
  const ref = React25.useRef(null);
3941
+ const effectiveSubtitle = subtitle ?? description;
3913
3942
  React25.useEffect(() => {
3914
3943
  if (visible) {
3915
3944
  impactMedium();
3916
- ref.current?.present();
3945
+ ref.current?.snapToIndex(0);
3917
3946
  } else {
3918
- ref.current?.dismiss();
3947
+ ref.current?.close();
3919
3948
  }
3920
3949
  }, [visible]);
3921
3950
  const renderBackdrop = (props) => /* @__PURE__ */ React25__default.default.createElement(
3922
- bottomSheet.BottomSheetBackdrop,
3951
+ BottomSheet.BottomSheetBackdrop,
3923
3952
  {
3924
3953
  ...props,
3925
3954
  disappearsOnIndex: -1,
@@ -3928,17 +3957,30 @@ function ConfirmDialog({
3928
3957
  }
3929
3958
  );
3930
3959
  return /* @__PURE__ */ React25__default.default.createElement(
3931
- bottomSheet.BottomSheetModal,
3960
+ BottomSheet__default.default,
3932
3961
  {
3933
3962
  ref,
3963
+ index: -1,
3964
+ onClose: onCancel,
3934
3965
  enableDynamicSizing: true,
3935
- onDismiss: onCancel,
3936
3966
  backdropComponent: renderBackdrop,
3937
3967
  backgroundStyle: [styles30.background, { backgroundColor: colors.card }],
3938
3968
  handleIndicatorStyle: [styles30.handle, { backgroundColor: colors.border }],
3939
3969
  enablePanDownToClose: true
3940
3970
  },
3941
- /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetView, { style: styles30.content }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles30.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles30.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles30.actions }, /* @__PURE__ */ React25__default.default.createElement(
3971
+ /* @__PURE__ */ React25__default.default.createElement(BottomSheet.BottomSheetView, { style: styles30.content }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles30.header, accessibilityRole: "header" }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles30.headerRow }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles30.title, { color: colors.foreground }], allowFontScaling: true }, title), showCloseButton ? /* @__PURE__ */ React25__default.default.createElement(
3972
+ reactNative.TouchableOpacity,
3973
+ {
3974
+ onPress: onCancel,
3975
+ style: styles30.closeButton,
3976
+ activeOpacity: 0.6,
3977
+ touchSoundDisabled: true,
3978
+ accessibilityRole: "button",
3979
+ accessibilityLabel: "Close",
3980
+ hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }
3981
+ },
3982
+ /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "x", size: ms(18), color: colors.foregroundMuted })
3983
+ ) : null), effectiveSubtitle ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles30.subtitle, { color: colors.foregroundMuted }], allowFontScaling: true }, effectiveSubtitle) : null), /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles30.actions }, /* @__PURE__ */ React25__default.default.createElement(
3942
3984
  Button,
3943
3985
  {
3944
3986
  label: confirmLabel,
@@ -3985,19 +4027,32 @@ var styles30 = reactNative.StyleSheet.create({
3985
4027
  borderRadius: ms(2)
3986
4028
  },
3987
4029
  content: {
3988
- paddingHorizontal: s(24),
3989
- paddingBottom: vs(32),
3990
- gap: vs(12)
4030
+ paddingHorizontal: s(16),
4031
+ paddingBottom: vs(32)
4032
+ },
4033
+ header: {
4034
+ paddingTop: vs(4),
4035
+ paddingBottom: vs(12),
4036
+ gap: vs(4)
4037
+ },
4038
+ headerRow: {
4039
+ flexDirection: "row",
4040
+ alignItems: "center",
4041
+ justifyContent: "space-between"
3991
4042
  },
3992
4043
  title: {
3993
4044
  fontFamily: "Sohne-SemiBold",
3994
4045
  fontSize: ms(18),
3995
- lineHeight: mvs(26)
4046
+ flex: 1
3996
4047
  },
3997
- description: {
4048
+ closeButton: {
4049
+ padding: s(4),
4050
+ marginLeft: s(8)
4051
+ },
4052
+ subtitle: {
3998
4053
  fontFamily: "Sohne-Regular",
3999
- fontSize: ms(15),
4000
- lineHeight: mvs(22)
4054
+ fontSize: ms(14),
4055
+ lineHeight: mvs(20)
4001
4056
  },
4002
4057
  actions: {
4003
4058
  gap: vs(10),
@@ -4620,7 +4675,7 @@ function VirtualListInner({ itemHeight, keyExtractor, renderItem, ...props }, re
4620
4675
  }
4621
4676
  var VirtualList = React25__default.default.forwardRef(VirtualListInner);
4622
4677
  function RetrayProvider({ children, theme, colorScheme = "system" }) {
4623
- return /* @__PURE__ */ React25__default.default.createElement(reactNativeSafeAreaContext.SafeAreaProvider, { initialMetrics: reactNativeSafeAreaContext.initialWindowMetrics }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureHandlerRootView, { style: styles37.root }, /* @__PURE__ */ React25__default.default.createElement(ThemeProvider, { theme, colorScheme }, /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetModalProvider, null, /* @__PURE__ */ React25__default.default.createElement(ToastProvider, null, children)))));
4678
+ return /* @__PURE__ */ React25__default.default.createElement(reactNativeSafeAreaContext.SafeAreaProvider, { initialMetrics: reactNativeSafeAreaContext.initialWindowMetrics }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureHandlerRootView, { style: styles37.root }, /* @__PURE__ */ React25__default.default.createElement(ThemeProvider, { theme, colorScheme }, /* @__PURE__ */ React25__default.default.createElement(BottomSheet.BottomSheetModalProvider, null, /* @__PURE__ */ React25__default.default.createElement(ToastProvider, null, children)))));
4624
4679
  }
4625
4680
  var styles37 = reactNative.StyleSheet.create({
4626
4681
  root: { flex: 1 }
@@ -5381,17 +5436,12 @@ function ZoomableImage({ source, width, height, onZoomChange }) {
5381
5436
  { scale: scale2.value }
5382
5437
  ]
5383
5438
  }));
5384
- return /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: composed }, /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [{ width, height }, styles44.imageWrap] }, /* @__PURE__ */ React25__default.default.createElement(
5385
- Animated12__default.default.Image,
5386
- {
5387
- source,
5388
- style: [{ width, height }, animatedStyle],
5389
- resizeMode: "contain"
5390
- }
5391
- )));
5439
+ return /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: composed }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [{ width, height }, styles44.imageWrap], collapsable: false }, /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [{ width, height }, animatedStyle] }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Image, { source, style: { width, height }, resizeMode: "contain" }))));
5392
5440
  }
5393
5441
  function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
5394
- const { width, height } = reactNative.useWindowDimensions();
5442
+ const window = reactNative.useWindowDimensions();
5443
+ const width = window.width > 0 ? window.width : reactNative.Dimensions.get("window").width;
5444
+ const height = window.height > 0 ? window.height : reactNative.Dimensions.get("window").height;
5395
5445
  const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
5396
5446
  const [index, setIndex] = React25.useState(initialIndex);
5397
5447
  const [pagingEnabled, setPagingEnabled] = React25.useState(true);
@@ -5434,7 +5484,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
5434
5484
  scrollRef.current?.scrollTo({ x: page * width, animated: true });
5435
5485
  setIndex(page);
5436
5486
  };
5437
- return /* @__PURE__ */ React25__default.default.createElement(reactNative.Modal, { visible, transparent: false, animationType: "fade", onRequestClose: onClose, statusBarTranslucent: true }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureHandlerRootView, { style: styles44.root }, /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [styles44.backdrop, backdropStyle], pointerEvents: "none" }), /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [styles44.container, dismissStyle] }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: swipeDown }, /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: styles44.root }, /* @__PURE__ */ React25__default.default.createElement(
5487
+ return /* @__PURE__ */ React25__default.default.createElement(reactNative.Modal, { visible, transparent: false, animationType: "fade", onRequestClose: onClose, statusBarTranslucent: true }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureHandlerRootView, { style: styles44.root }, /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [styles44.backdrop, backdropStyle], pointerEvents: "none" }), /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [styles44.container, dismissStyle] }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: swipeDown }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles44.root, collapsable: false }, /* @__PURE__ */ React25__default.default.createElement(
5438
5488
  reactNative.ScrollView,
5439
5489
  {
5440
5490
  ref: scrollRef,
@@ -5455,19 +5505,18 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
5455
5505
  onZoomChange: (zoomed) => setPagingEnabled(!zoomed)
5456
5506
  }
5457
5507
  ))
5458
- ))), /* @__PURE__ */ React25__default.default.createElement(
5459
- reactNative.TouchableOpacity,
5508
+ ))), /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles44.closeButtonWrapper, { top: insets.top + vs(8) }] }, /* @__PURE__ */ React25__default.default.createElement(
5509
+ IconButton,
5460
5510
  {
5461
- style: [styles44.closeButton, { top: insets.top + vs(8) }],
5511
+ iconName: "x",
5512
+ size: "md",
5513
+ variant: "text",
5514
+ style: { backgroundColor: "rgba(255,255,255,0.18)" },
5515
+ iconColor: "#fff",
5462
5516
  onPress: onClose,
5463
- activeOpacity: 0.7,
5464
- touchSoundDisabled: true,
5465
- accessibilityRole: "button",
5466
- accessibilityLabel: "Close",
5467
- hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }
5468
- },
5469
- renderIcon("x", 26, "#fff")
5470
- ), images.length > 1 ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles44.dots, { bottom: insets.bottom + vs(16) }], pointerEvents: "box-none" }, /* @__PURE__ */ React25__default.default.createElement(
5517
+ accessibilityLabel: "Close"
5518
+ }
5519
+ )), images.length > 1 ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles44.dots, { bottom: insets.bottom + vs(16) }], pointerEvents: "box-none" }, /* @__PURE__ */ React25__default.default.createElement(
5471
5520
  PagerDots,
5472
5521
  {
5473
5522
  count: images.length,
@@ -5494,15 +5543,9 @@ var styles44 = reactNative.StyleSheet.create({
5494
5543
  justifyContent: "center",
5495
5544
  overflow: "hidden"
5496
5545
  },
5497
- closeButton: {
5546
+ closeButtonWrapper: {
5498
5547
  position: "absolute",
5499
- right: s(12),
5500
- width: s(40),
5501
- height: s(40),
5502
- borderRadius: s(20),
5503
- backgroundColor: "rgba(0,0,0,0.4)",
5504
- alignItems: "center",
5505
- justifyContent: "center"
5548
+ right: s(12)
5506
5549
  },
5507
5550
  dots: {
5508
5551
  position: "absolute",
@@ -5798,11 +5841,11 @@ function useConfirmDialog(options) {
5798
5841
 
5799
5842
  Object.defineProperty(exports, "BottomSheetModalProvider", {
5800
5843
  enumerable: true,
5801
- get: function () { return bottomSheet.BottomSheetModalProvider; }
5844
+ get: function () { return BottomSheet.BottomSheetModalProvider; }
5802
5845
  });
5803
5846
  Object.defineProperty(exports, "SheetTextInput", {
5804
5847
  enumerable: true,
5805
- get: function () { return bottomSheet.BottomSheetTextInput; }
5848
+ get: function () { return BottomSheet.BottomSheetTextInput; }
5806
5849
  });
5807
5850
  Object.defineProperty(exports, "toast", {
5808
5851
  enumerable: true,
package/dist/index.mjs CHANGED
@@ -4,11 +4,11 @@ export { Textarea } from './chunk-CZCQZHG6.mjs';
4
4
  export { Toggle } from './chunk-KIHCWCWL.mjs';
5
5
  export { VirtualList } from './chunk-NC5ZTR2Y.mjs';
6
6
  export { Separator } from './chunk-MX6HRKMI.mjs';
7
- export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-PFZTM6D5.mjs';
7
+ export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-Y2NS74WS.mjs';
8
8
  export { SheetSelect } from './chunk-URI2WBIV.mjs';
9
9
  export { Skeleton } from './chunk-AJ7ZDNBT.mjs';
10
10
  export { Slider } from './chunk-JMOZEC77.mjs';
11
- export { Switch } from './chunk-QKH5ZOD5.mjs';
11
+ export { Switch } from './chunk-WF2XDFRK.mjs';
12
12
  export { TabBar } from './chunk-MLF3EZFW.mjs';
13
13
  export { Pressable } from './chunk-MBMXYJJV.mjs';
14
14
  export { PricingCard } from './chunk-4I7D47FH.mjs';
@@ -31,7 +31,7 @@ export { ErrorBoundary } from './chunk-LXJIIOYQ.mjs';
31
31
  export { Form, FormField, FormFooter, FormSection } from './chunk-6Q64UFIA.mjs';
32
32
  export { ImageUpload } from './chunk-Y4GL2MHX.mjs';
33
33
  export { Spinner } from './chunk-WBOOUHSS.mjs';
34
- export { ImageViewer } from './chunk-Z4BVUWW6.mjs';
34
+ export { ImageViewer } from './chunk-WOEYDUJZ.mjs';
35
35
  export { PagerDots } from './chunk-4K625MVM.mjs';
36
36
  export { ButtonGroup } from './chunk-3BBOZ3OQ.mjs';
37
37
  export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-ID72TK46.mjs';
@@ -39,11 +39,11 @@ export { CategoryStrip } from './chunk-VQ57HWPL.mjs';
39
39
  import './chunk-YNROWHQJ.mjs';
40
40
  export { Checkbox } from './chunk-AV4EMIRH.mjs';
41
41
  export { Chip, ChipGroup } from './chunk-UREA2GYY.mjs';
42
- export { ConfirmDialog } from './chunk-FZZLPJ6B.mjs';
42
+ export { ConfirmDialog } from './chunk-KZL5VTYK.mjs';
43
43
  export { CurrencyDisplay } from './chunk-BRKYVJVV.mjs';
44
44
  export { CurrencyInput } from './chunk-JUXSWN54.mjs';
45
45
  export { Input } from './chunk-ZUR7AU5R.mjs';
46
- export { Accordion } from './chunk-O3HA6TYM.mjs';
46
+ export { Accordion } from './chunk-DJ7RN37L.mjs';
47
47
  export { AlertBanner } from './chunk-6MKGPAR2.mjs';
48
48
  export { AppHeader } from './chunk-AZJF2BLK.mjs';
49
49
  export { IconButton } from './chunk-3U4SSNWP.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retray-dev/ui-kit",
3
- "version": "10.0.0",
3
+ "version": "10.1.0",
4
4
  "description": "Personal UI Kit for React Native / Expo",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -21,7 +21,7 @@ import { TIMINGS, EASINGS } from '../../utils/animations'
21
21
 
22
22
  export interface AccordionItem {
23
23
  value: string
24
- trigger: string
24
+ trigger: string | React.ReactNode
25
25
  content: React.ReactNode
26
26
  /** Icon name from @expo/vector-icons rendered left of trigger. */
27
27
  iconName?: string
@@ -102,11 +102,15 @@ function AccordionItemComponent({
102
102
  }}
103
103
  accessibilityRole="button"
104
104
  accessibilityState={{ expanded: isOpen }}
105
- accessibilityLabel={item.trigger}
105
+ accessibilityLabel={typeof item.trigger === 'string' ? item.trigger : undefined}
106
106
  >
107
107
  <View style={styles.triggerContent}>
108
108
  {resolvedIcon ? <View style={styles.icon}>{resolvedIcon}</View> : null}
109
- <Text style={[styles.triggerText, { color: colors.foreground }]} allowFontScaling={true}>{item.trigger}</Text>
109
+ {typeof item.trigger === 'string' ? (
110
+ <Text style={[styles.triggerText, { color: colors.foreground }]} allowFontScaling={true}>{item.trigger}</Text>
111
+ ) : (
112
+ item.trigger
113
+ )}
110
114
  </View>
111
115
  <Animated.View style={[styles.chevron, rotationStyle]}>
112
116
  <Entypo name="chevron-down" size={18} color={colors.foregroundMuted} />
@@ -1,7 +1,6 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
- import { View, Text, StyleSheet } from 'react-native'
3
- import {
4
- BottomSheetModal,
2
+ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
3
+ import BottomSheet, {
5
4
  BottomSheetView,
6
5
  BottomSheetBackdrop,
7
6
  type BottomSheetBackdropProps,
@@ -15,12 +14,17 @@ import { s, vs, ms, mvs } from '../../utils/scaling'
15
14
  export interface ConfirmDialogProps {
16
15
  visible: boolean
17
16
  title: string
17
+ /** Secondary text below title. */
18
+ subtitle?: string
19
+ /** @deprecated Use `subtitle` instead. */
18
20
  description?: string
19
21
  confirmLabel?: string
20
22
  cancelLabel?: string
21
23
  confirmVariant?: 'primary' | 'destructive'
22
24
  /** Show a loading spinner in the confirm button (e.g. while async action completes). */
23
25
  loading?: boolean
26
+ /** Show an X close button in the top-right corner. */
27
+ showCloseButton?: boolean
24
28
  onConfirm: () => void
25
29
  onCancel: () => void
26
30
  }
@@ -28,23 +32,26 @@ export interface ConfirmDialogProps {
28
32
  export function ConfirmDialog({
29
33
  visible,
30
34
  title,
35
+ subtitle,
31
36
  description,
32
37
  confirmLabel = 'Confirm',
33
38
  cancelLabel = 'Cancel',
34
39
  confirmVariant = 'primary',
35
40
  loading = false,
41
+ showCloseButton = false,
36
42
  onConfirm,
37
43
  onCancel,
38
44
  }: ConfirmDialogProps) {
39
45
  const { colors } = useTheme()
40
- const ref = useRef<BottomSheetModal>(null)
46
+ const ref = useRef<BottomSheet>(null)
47
+ const effectiveSubtitle = subtitle ?? description
41
48
 
42
49
  useEffect(() => {
43
50
  if (visible) {
44
51
  impactMedium()
45
- ref.current?.present()
52
+ ref.current?.snapToIndex(0)
46
53
  } else {
47
- ref.current?.dismiss()
54
+ ref.current?.close()
48
55
  }
49
56
  }, [visible])
50
57
 
@@ -58,24 +65,42 @@ export function ConfirmDialog({
58
65
  )
59
66
 
60
67
  return (
61
- <BottomSheetModal
68
+ <BottomSheet
62
69
  ref={ref}
70
+ index={-1}
71
+ onClose={onCancel}
63
72
  enableDynamicSizing
64
- onDismiss={onCancel}
65
73
  backdropComponent={renderBackdrop}
66
74
  backgroundStyle={[styles.background, { backgroundColor: colors.card }]}
67
75
  handleIndicatorStyle={[styles.handle, { backgroundColor: colors.border }]}
68
76
  enablePanDownToClose
69
77
  >
70
78
  <BottomSheetView style={styles.content}>
71
- <Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>
72
- {title}
73
- </Text>
74
- {description ? (
75
- <Text style={[styles.description, { color: colors.foregroundMuted }]} allowFontScaling={true}>
76
- {description}
77
- </Text>
78
- ) : null}
79
+ <View style={styles.header} accessibilityRole="header">
80
+ <View style={styles.headerRow}>
81
+ <Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>
82
+ {title}
83
+ </Text>
84
+ {showCloseButton ? (
85
+ <TouchableOpacity
86
+ onPress={onCancel}
87
+ style={styles.closeButton}
88
+ activeOpacity={0.6}
89
+ touchSoundDisabled={true}
90
+ accessibilityRole="button"
91
+ accessibilityLabel="Close"
92
+ hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
93
+ >
94
+ <Feather name="x" size={ms(18)} color={colors.foregroundMuted} />
95
+ </TouchableOpacity>
96
+ ) : null}
97
+ </View>
98
+ {effectiveSubtitle ? (
99
+ <Text style={[styles.subtitle, { color: colors.foregroundMuted }]} allowFontScaling={true}>
100
+ {effectiveSubtitle}
101
+ </Text>
102
+ ) : null}
103
+ </View>
79
104
  <View style={styles.actions}>
80
105
  <Button
81
106
  label={confirmLabel}
@@ -105,7 +130,7 @@ export function ConfirmDialog({
105
130
  />
106
131
  </View>
107
132
  </BottomSheetView>
108
- </BottomSheetModal>
133
+ </BottomSheet>
109
134
  )
110
135
  }
111
136
 
@@ -120,19 +145,32 @@ const styles = StyleSheet.create({
120
145
  borderRadius: ms(2),
121
146
  },
122
147
  content: {
123
- paddingHorizontal: s(24),
148
+ paddingHorizontal: s(16),
124
149
  paddingBottom: vs(32),
125
- gap: vs(12),
150
+ },
151
+ header: {
152
+ paddingTop: vs(4),
153
+ paddingBottom: vs(12),
154
+ gap: vs(4),
155
+ },
156
+ headerRow: {
157
+ flexDirection: 'row',
158
+ alignItems: 'center',
159
+ justifyContent: 'space-between',
126
160
  },
127
161
  title: {
128
162
  fontFamily: 'Sohne-SemiBold',
129
163
  fontSize: ms(18),
130
- lineHeight: mvs(26),
164
+ flex: 1,
165
+ },
166
+ closeButton: {
167
+ padding: s(4),
168
+ marginLeft: s(8),
131
169
  },
132
- description: {
170
+ subtitle: {
133
171
  fontFamily: 'Sohne-Regular',
134
- fontSize: ms(15),
135
- lineHeight: mvs(22),
172
+ fontSize: ms(14),
173
+ lineHeight: mvs(20),
136
174
  },
137
175
  actions: {
138
176
  gap: vs(10),