@retray-dev/ui-kit 9.1.0 → 9.3.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.
Files changed (64) hide show
  1. package/COMPONENTS.md +166 -4
  2. package/CONSUMER.md +247 -0
  3. package/DESIGN.md +668 -0
  4. package/FONTS.md +107 -0
  5. package/README.md +3 -3
  6. package/dist/AlertBanner.d.mts +3 -1
  7. package/dist/AlertBanner.d.ts +3 -1
  8. package/dist/AlertBanner.js +18 -2
  9. package/dist/AlertBanner.mjs +1 -1
  10. package/dist/ConfirmDialog.d.mts +3 -1
  11. package/dist/ConfirmDialog.d.ts +3 -1
  12. package/dist/ConfirmDialog.js +3 -0
  13. package/dist/ConfirmDialog.mjs +1 -1
  14. package/dist/CurrencyInput.d.mts +3 -1
  15. package/dist/CurrencyInput.d.ts +3 -1
  16. package/dist/CurrencyInput.js +52 -39
  17. package/dist/CurrencyInput.mjs +2 -3
  18. package/dist/ImageUpload.d.mts +27 -0
  19. package/dist/ImageUpload.d.ts +27 -0
  20. package/dist/ImageUpload.js +399 -0
  21. package/dist/ImageUpload.mjs +9 -0
  22. package/dist/Input.d.mts +3 -1
  23. package/dist/Input.d.ts +3 -1
  24. package/dist/Input.js +48 -37
  25. package/dist/Input.mjs +1 -2
  26. package/dist/ListItem.d.mts +9 -2
  27. package/dist/ListItem.d.ts +9 -2
  28. package/dist/ListItem.js +9 -2
  29. package/dist/ListItem.mjs +1 -1
  30. package/dist/SheetSelect.d.mts +25 -0
  31. package/dist/SheetSelect.d.ts +25 -0
  32. package/dist/SheetSelect.js +440 -0
  33. package/dist/SheetSelect.mjs +9 -0
  34. package/dist/Textarea.mjs +1 -2
  35. package/dist/{chunk-M6ZXVBTK.mjs → chunk-6MKGPAR2.mjs} +21 -5
  36. package/dist/{chunk-EH745HE5.mjs → chunk-CZCQZHG6.mjs} +13 -4
  37. package/dist/{chunk-7QHVVCB3.mjs → chunk-FZZLPJ6B.mjs} +3 -0
  38. package/dist/{chunk-MAC465BB.mjs → chunk-JUXSWN54.mjs} +5 -3
  39. package/dist/{chunk-BNP626TY.mjs → chunk-OHBNABL5.mjs} +10 -3
  40. package/dist/chunk-URI2WBIV.mjs +147 -0
  41. package/dist/chunk-Y4GL2MHX.mjs +112 -0
  42. package/dist/{chunk-756RAKE4.mjs → chunk-ZUR7AU5R.mjs} +38 -20
  43. package/dist/fonts.d.mts +32 -0
  44. package/dist/fonts.d.ts +32 -0
  45. package/dist/fonts.js +44 -0
  46. package/dist/fonts.mjs +37 -0
  47. package/dist/index.d.mts +26 -1
  48. package/dist/index.d.ts +26 -1
  49. package/dist/index.js +425 -106
  50. package/dist/index.mjs +55 -17
  51. package/package.json +23 -6
  52. package/src/components/AlertBanner/AlertBanner.tsx +21 -3
  53. package/src/components/ConfirmDialog/ConfirmDialog.tsx +5 -0
  54. package/src/components/CurrencyInput/CurrencyInput.tsx +4 -0
  55. package/src/components/ImageUpload/ImageUpload.tsx +158 -0
  56. package/src/components/ImageUpload/index.ts +1 -0
  57. package/src/components/Input/Input.tsx +64 -53
  58. package/src/components/ListItem/ListItem.tsx +23 -4
  59. package/src/components/SheetSelect/SheetSelect.tsx +192 -0
  60. package/src/components/SheetSelect/index.ts +1 -0
  61. package/src/fonts.ts +30 -29
  62. package/src/hooks/useConfirmDialog.ts +67 -0
  63. package/src/index.ts +6 -0
  64. package/dist/chunk-26BCI223.mjs +0 -14
package/dist/index.js CHANGED
@@ -10,13 +10,13 @@ var FontAwesome5 = require('@expo/vector-icons/FontAwesome5');
10
10
  var MaterialIcons = require('@expo/vector-icons/MaterialIcons');
11
11
  var Ionicons = require('@expo/vector-icons/Ionicons');
12
12
  var pressto = require('pressto');
13
- var Animated13 = require('react-native-reanimated');
13
+ var Animated12 = require('react-native-reanimated');
14
14
  var expoFont = require('expo-font');
15
+ var bottomSheet = require('@gorhom/bottom-sheet');
16
+ var reactNativeEase = require('react-native-ease');
15
17
  var vectorIcons = require('@expo/vector-icons');
16
18
  var expoLinearGradient = require('expo-linear-gradient');
17
- var reactNativeEase = require('react-native-ease');
18
19
  var RNSlider = require('@react-native-community/slider');
19
- var bottomSheet = require('@gorhom/bottom-sheet');
20
20
  var reactNativeSafeAreaContext = require('react-native-safe-area-context');
21
21
  var picker = require('@react-native-picker/picker');
22
22
  var sonnerNative = require('sonner-native');
@@ -31,7 +31,7 @@ var Feather__default = /*#__PURE__*/_interopDefault(Feather);
31
31
  var FontAwesome5__default = /*#__PURE__*/_interopDefault(FontAwesome5);
32
32
  var MaterialIcons__default = /*#__PURE__*/_interopDefault(MaterialIcons);
33
33
  var Ionicons__default = /*#__PURE__*/_interopDefault(Ionicons);
34
- var Animated13__default = /*#__PURE__*/_interopDefault(Animated13);
34
+ var Animated12__default = /*#__PURE__*/_interopDefault(Animated12);
35
35
  var RNSlider__default = /*#__PURE__*/_interopDefault(RNSlider);
36
36
 
37
37
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -611,11 +611,11 @@ var TIMINGS = {
611
611
  };
612
612
  var EASINGS = {
613
613
  /** Material-style ease-out — natural deceleration for state changes. */
614
- standard: Animated13.Easing.bezier(0.2, 0, 0, 1),
614
+ standard: Animated12.Easing.bezier(0.2, 0, 0, 1),
615
615
  /** Strong ease-out for expanding surfaces (Accordion open). */
616
- expand: Animated13.Easing.bezier(0.23, 1, 0.32, 1),
616
+ expand: Animated12.Easing.bezier(0.23, 1, 0.32, 1),
617
617
  /** Quick ease-in for collapsing. */
618
- collapse: Animated13.Easing.in(Animated13.Easing.ease)
618
+ collapse: Animated12.Easing.in(Animated12.Easing.ease)
619
619
  };
620
620
  var COLOR_TRANSITION = {
621
621
  type: "timing",
@@ -1000,24 +1000,11 @@ function TextBase({ variant = "body-md", color, style, children, ...props }) {
1000
1000
  );
1001
1001
  }
1002
1002
  var Text3 = React25__default.default.memo(TextBase);
1003
- function useColorTransition(active, options = {}) {
1004
- const { duration = TIMINGS.state.duration } = options;
1005
- const progress = Animated13.useSharedValue(active ? 1 : 0);
1006
- React25.useEffect(() => {
1007
- progress.value = Animated13.withTiming(active ? 1 : 0, { duration, easing: EASINGS.standard });
1008
- }, [active, duration, progress]);
1009
- return progress;
1010
- }
1011
-
1012
- // src/components/Input/Input.tsx
1013
1003
  var webInputResetStyle = reactNative.Platform.OS === "web" ? { outlineStyle: "none", outlineWidth: 0, outlineColor: "transparent", boxShadow: "none" } : {};
1014
- function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = "text", containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }) {
1004
+ function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = "text", containerStyle, inputWrapperStyle, sheetMode = false, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }) {
1015
1005
  const { colors } = useTheme();
1016
1006
  const [focused, setFocused] = React25.useState(false);
1017
1007
  const [showPassword, setShowPassword] = React25.useState(false);
1018
- const focusProgress = useColorTransition(focused, {
1019
- duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration
1020
- });
1021
1008
  const isDisabled = disabled || editable === false;
1022
1009
  const isPassword = type === "password";
1023
1010
  const effectiveSecure = isPassword ? !showPassword : secureTextEntry;
@@ -1033,22 +1020,45 @@ function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suff
1033
1020
  },
1034
1021
  /* @__PURE__ */ React25__default.default.createElement(vectorIcons.AntDesign, { name: showPassword ? "eye" : "eye-invisible", size: 20, color: colors.foregroundMuted })
1035
1022
  ) : suffixIcon ? renderIcon(suffixIcon, 20, suffixIconColor ?? colors.foregroundMuted) : suffix;
1036
- const borderAnimStyle = Animated13.useAnimatedStyle(() => ({
1037
- borderColor: error ? colors.destructive : Animated13.interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary]),
1038
- borderWidth: error ? 2 : Animated13.interpolate(focusProgress.value, [0, 1], [1, 2])
1039
- }));
1023
+ const borderColor = error ? colors.destructive : focused ? colors.primary : colors.border;
1040
1024
  return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles4.container, isDisabled && styles4.containerDisabled, containerStyle] }, label ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles4.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, /* @__PURE__ */ React25__default.default.createElement(
1041
- Animated13__default.default.View,
1025
+ reactNativeEase.EaseView,
1042
1026
  {
1043
1027
  style: [
1044
1028
  styles4.inputWrapper,
1045
1029
  { backgroundColor: isDisabled ? colors.surface : colors.background },
1030
+ error && styles4.inputWrapperError,
1046
1031
  inputWrapperStyle
1047
- ]
1032
+ ],
1033
+ animate: { borderColor },
1034
+ transition: COLOR_TRANSITION
1048
1035
  },
1049
- /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [styles4.borderOverlay, borderAnimStyle], pointerEvents: "none" }),
1050
1036
  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,
1051
- /* @__PURE__ */ React25__default.default.createElement(
1037
+ sheetMode ? /* @__PURE__ */ React25__default.default.createElement(
1038
+ bottomSheet.BottomSheetTextInput,
1039
+ {
1040
+ style: [
1041
+ styles4.input,
1042
+ { color: colors.foreground },
1043
+ webInputResetStyle,
1044
+ style
1045
+ ],
1046
+ onFocus: (e) => {
1047
+ setFocused(true);
1048
+ onFocus?.(e);
1049
+ },
1050
+ onBlur: (e) => {
1051
+ setFocused(false);
1052
+ onBlur?.(e);
1053
+ },
1054
+ placeholderTextColor: colors.foregroundMuted,
1055
+ allowFontScaling: true,
1056
+ secureTextEntry: effectiveSecure,
1057
+ editable: isDisabled ? false : editable,
1058
+ accessibilityLabel: accessibilityLabel ?? label,
1059
+ ...props
1060
+ }
1061
+ ) : /* @__PURE__ */ React25__default.default.createElement(
1052
1062
  reactNative.TextInput,
1053
1063
  {
1054
1064
  style: [
@@ -1098,16 +1108,14 @@ var styles4 = reactNative.StyleSheet.create({
1098
1108
  inputWrapper: {
1099
1109
  flexDirection: "row",
1100
1110
  alignItems: "center",
1101
- // Border lives on borderOverlay (absolute) so its 1px→2px focus change
1102
- // never resizes this box. Wrapper itself carries no border.
1103
1111
  borderRadius: 8,
1112
+ borderWidth: 1,
1104
1113
  paddingHorizontal: s(14),
1105
1114
  paddingVertical: vs(11),
1106
1115
  minHeight: 48
1107
1116
  },
1108
- borderOverlay: {
1109
- ...reactNative.StyleSheet.absoluteFillObject,
1110
- borderRadius: 8
1117
+ inputWrapperError: {
1118
+ borderWidth: 2
1111
1119
  },
1112
1120
  input: {
1113
1121
  fontFamily: "Sohne-Regular",
@@ -1397,17 +1405,17 @@ function Skeleton({
1397
1405
  style
1398
1406
  }) {
1399
1407
  const { colors, colorScheme } = useTheme();
1400
- const shimmer = Animated13.useSharedValue(0);
1408
+ const shimmer = Animated12.useSharedValue(0);
1401
1409
  const [containerWidth, setContainerWidth] = React25.useState(300);
1402
1410
  const shimmerHighlight = colorScheme === "dark" ? "rgba(255,255,255,0.08)" : "rgba(255,255,255,0.7)";
1403
1411
  React25.useEffect(() => {
1404
- shimmer.value = Animated13.withRepeat(
1405
- Animated13.withTiming(1, { duration: TIMINGS.shimmer.duration, easing: Animated13.Easing.linear }),
1412
+ shimmer.value = Animated12.withRepeat(
1413
+ Animated12.withTiming(1, { duration: TIMINGS.shimmer.duration, easing: Animated12.Easing.linear }),
1406
1414
  -1,
1407
1415
  false
1408
1416
  );
1409
1417
  }, [shimmer]);
1410
- const shimmerStyle = Animated13.useAnimatedStyle(() => ({
1418
+ const shimmerStyle = Animated12.useAnimatedStyle(() => ({
1411
1419
  transform: [{ translateX: -containerWidth + shimmer.value * (containerWidth * 2) }]
1412
1420
  }));
1413
1421
  const resolvedWidth = preset === "circle" ? s(diameter) : preset === "text" ? "60%" : width;
@@ -1426,7 +1434,7 @@ function Skeleton({
1426
1434
  accessibilityLabel: "Loading",
1427
1435
  accessibilityState: { busy: true }
1428
1436
  },
1429
- /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [reactNative.StyleSheet.absoluteFill, shimmerStyle] }, /* @__PURE__ */ React25__default.default.createElement(
1437
+ /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [reactNative.StyleSheet.absoluteFill, shimmerStyle] }, /* @__PURE__ */ React25__default.default.createElement(
1430
1438
  expoLinearGradient.LinearGradient,
1431
1439
  {
1432
1440
  colors: ["transparent", shimmerHighlight, "transparent"],
@@ -1604,7 +1612,7 @@ var styles10 = reactNative.StyleSheet.create({
1604
1612
  right: 0
1605
1613
  }
1606
1614
  });
1607
- function AlertBanner({ title, description, variant = "default", icon, iconName, iconColor, style }) {
1615
+ function AlertBanner({ title, description, variant = "default", icon, iconName, iconColor, onDismiss, style }) {
1608
1616
  const { colors, colorScheme } = useTheme();
1609
1617
  const isDark = colorScheme === "dark";
1610
1618
  const accentColor = variant === "destructive" ? colors.destructive : variant === "success" ? colors.success : variant === "warning" ? colors.warning : colors.foreground;
@@ -1630,7 +1638,18 @@ function AlertBanner({ title, description, variant = "default", icon, iconName,
1630
1638
  accessibilityLabel: a11yLabel
1631
1639
  },
1632
1640
  /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles11.iconSlot }, effectiveIcon),
1633
- /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles11.content }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles11.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles11.description, { color: colors.foreground, opacity: 0.85 }], allowFontScaling: true }, description) : null)
1641
+ /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles11.content }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles11.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles11.description, { color: colors.foreground, opacity: 0.85 }], allowFontScaling: true }, description) : null),
1642
+ onDismiss ? /* @__PURE__ */ React25__default.default.createElement(
1643
+ reactNative.TouchableOpacity,
1644
+ {
1645
+ onPress: onDismiss,
1646
+ style: styles11.dismissButton,
1647
+ activeOpacity: 0.6,
1648
+ accessibilityRole: "button",
1649
+ accessibilityLabel: "Dismiss"
1650
+ },
1651
+ /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "x", size: ms(16), color: colors.foregroundMuted })
1652
+ ) : null
1634
1653
  );
1635
1654
  }
1636
1655
  var styles11 = reactNative.StyleSheet.create({
@@ -1656,18 +1675,23 @@ var styles11 = reactNative.StyleSheet.create({
1656
1675
  description: {
1657
1676
  fontFamily: "Sohne-Regular",
1658
1677
  fontSize: ms(12)
1678
+ },
1679
+ dismissButton: {
1680
+ padding: s(4),
1681
+ marginTop: vs(-2),
1682
+ marginRight: -s(4)
1659
1683
  }
1660
1684
  });
1661
1685
  function Progress({ value = 0, max = 100, variant = "default", style, accessibilityLabel }) {
1662
1686
  const { colors } = useTheme();
1663
1687
  const percent = Math.min(Math.max(value / max * 100, 0), 100);
1664
1688
  const [trackWidth, setTrackWidth] = React25.useState(0);
1665
- const animatedWidth = Animated13.useSharedValue(0);
1689
+ const animatedWidth = Animated12.useSharedValue(0);
1666
1690
  React25.useEffect(() => {
1667
1691
  if (trackWidth === 0) return;
1668
- animatedWidth.value = Animated13.withSpring(percent / 100 * trackWidth, SPRINGS.glide);
1692
+ animatedWidth.value = Animated12.withSpring(percent / 100 * trackWidth, SPRINGS.glide);
1669
1693
  }, [percent, trackWidth, animatedWidth]);
1670
- const indicatorAnimatedStyle = Animated13.useAnimatedStyle(() => ({
1694
+ const indicatorAnimatedStyle = Animated12.useAnimatedStyle(() => ({
1671
1695
  width: animatedWidth.value
1672
1696
  }));
1673
1697
  const indicatorColor = variant === "success" ? colors.success : variant === "warning" ? colors.warning : variant === "destructive" ? colors.destructive : colors.primary;
@@ -1681,7 +1705,7 @@ function Progress({ value = 0, max = 100, variant = "default", style, accessibil
1681
1705
  accessibilityValue: { min: 0, max: 100, now: Math.round(percent) }
1682
1706
  },
1683
1707
  /* @__PURE__ */ React25__default.default.createElement(
1684
- Animated13__default.default.View,
1708
+ Animated12__default.default.View,
1685
1709
  {
1686
1710
  style: [styles12.indicator, { backgroundColor: indicatorColor }, indicatorAnimatedStyle]
1687
1711
  }
@@ -1789,6 +1813,16 @@ var styles13 = reactNative.StyleSheet.create({
1789
1813
  paddingHorizontal: s(32)
1790
1814
  }
1791
1815
  });
1816
+ function useColorTransition(active, options = {}) {
1817
+ const { duration = TIMINGS.state.duration } = options;
1818
+ const progress = Animated12.useSharedValue(active ? 1 : 0);
1819
+ React25.useEffect(() => {
1820
+ progress.value = Animated12.withTiming(active ? 1 : 0, { duration, easing: EASINGS.standard });
1821
+ }, [active, duration, progress]);
1822
+ return progress;
1823
+ }
1824
+
1825
+ // src/components/Textarea/Textarea.tsx
1792
1826
  var webInputResetStyle2 = reactNative.Platform.OS === "web" ? { outlineStyle: "none", outlineWidth: 0, outlineColor: "transparent", boxShadow: "none" } : {};
1793
1827
  function Textarea({
1794
1828
  label,
@@ -1811,19 +1845,19 @@ function Textarea({
1811
1845
  duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration
1812
1846
  });
1813
1847
  const resolvedPrefixIcon = prefixIcon ? renderIcon(prefixIcon, ms(16), prefixIconColor ?? colors.foregroundMuted) : prefixIconNode;
1814
- const borderAnimStyle = Animated13.useAnimatedStyle(() => ({
1815
- borderColor: error ? colors.destructive : Animated13.interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary]),
1816
- borderWidth: error ? 2 : Animated13.interpolate(focusProgress.value, [0, 1], [1, 2])
1848
+ const borderAnimStyle = Animated12.useAnimatedStyle(() => ({
1849
+ borderColor: error ? colors.destructive : Animated12.interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary]),
1850
+ borderWidth: error ? 2 : Animated12.interpolate(focusProgress.value, [0, 1], [1, 2])
1817
1851
  }));
1818
1852
  return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles14.container, containerStyle] }, label ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles14.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, /* @__PURE__ */ React25__default.default.createElement(
1819
- Animated13__default.default.View,
1853
+ Animated12__default.default.View,
1820
1854
  {
1821
1855
  style: [
1822
1856
  styles14.inputWrapper,
1823
1857
  { backgroundColor: colors.background }
1824
1858
  ]
1825
1859
  },
1826
- /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [styles14.borderOverlay, borderAnimStyle], pointerEvents: "none" }),
1860
+ /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [styles14.borderOverlay, borderAnimStyle], pointerEvents: "none" }),
1827
1861
  resolvedPrefixIcon ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles14.prefixIcon }, resolvedPrefixIcon) : null,
1828
1862
  /* @__PURE__ */ React25__default.default.createElement(
1829
1863
  reactNative.TextInput,
@@ -2203,18 +2237,18 @@ function usePressScale({
2203
2237
  pressOutSpring = SPRINGS.pressOut,
2204
2238
  disabled = false
2205
2239
  } = {}) {
2206
- const scale2 = Animated13.useSharedValue(1);
2240
+ const scale2 = Animated12.useSharedValue(1);
2207
2241
  const { hovered, hoverHandlers } = useHover();
2208
2242
  const onPressIn = React25.useCallback(() => {
2209
2243
  if (disabled) return;
2210
- scale2.value = Animated13.withSpring(pressScale, pressInSpring);
2244
+ scale2.value = Animated12.withSpring(pressScale, pressInSpring);
2211
2245
  }, [disabled, pressScale, pressInSpring, scale2]);
2212
2246
  const onPressOut = React25.useCallback(() => {
2213
2247
  if (disabled) return;
2214
- scale2.value = Animated13.withSpring(1, pressOutSpring);
2248
+ scale2.value = Animated12.withSpring(1, pressOutSpring);
2215
2249
  }, [disabled, pressOutSpring, scale2]);
2216
2250
  const hoverActive = reactNative.Platform.OS === "web" && hovered && hoverScale !== 1 && !disabled;
2217
- const animatedStyle = Animated13.useAnimatedStyle(() => ({
2251
+ const animatedStyle = Animated12.useAnimatedStyle(() => ({
2218
2252
  transform: [
2219
2253
  { scale: scale2.value * (hoverActive ? hoverScale : 1) }
2220
2254
  ]
@@ -2261,7 +2295,7 @@ function RadioItem({
2261
2295
  accessibilityLabel: option.label,
2262
2296
  accessibilityState: { checked: selected, disabled: !!option.disabled }
2263
2297
  },
2264
- /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: scaleStyle }, /* @__PURE__ */ React25__default.default.createElement(
2298
+ /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: scaleStyle }, /* @__PURE__ */ React25__default.default.createElement(
2265
2299
  reactNativeEase.EaseView,
2266
2300
  {
2267
2301
  style: styles18.radio,
@@ -2396,15 +2430,15 @@ function Tabs({ tabs, variant = "pill", value, onValueChange, children, style })
2396
2430
  const { colors } = useTheme();
2397
2431
  const active = value ?? internal;
2398
2432
  const tabLayouts = React25.useRef({});
2399
- const pillX = Animated13.useSharedValue(0);
2400
- const pillWidth = Animated13.useSharedValue(0);
2433
+ const pillX = Animated12.useSharedValue(0);
2434
+ const pillWidth = Animated12.useSharedValue(0);
2401
2435
  const initialised = React25.useRef(false);
2402
2436
  const animatePill = React25.useCallback((tabValue, animate) => {
2403
2437
  const layout = tabLayouts.current[tabValue];
2404
2438
  if (!layout) return;
2405
2439
  if (animate) {
2406
- pillX.value = Animated13.withSpring(layout.x, SPRINGS.glide);
2407
- pillWidth.value = Animated13.withSpring(layout.width, SPRINGS.glide);
2440
+ pillX.value = Animated12.withSpring(layout.x, SPRINGS.glide);
2441
+ pillWidth.value = Animated12.withSpring(layout.width, SPRINGS.glide);
2408
2442
  } else {
2409
2443
  pillX.value = layout.x;
2410
2444
  pillWidth.value = layout.width;
@@ -2418,7 +2452,7 @@ function Tabs({ tabs, variant = "pill", value, onValueChange, children, style })
2418
2452
  if (!value) setInternal(v);
2419
2453
  onValueChange?.(v);
2420
2454
  };
2421
- const pillAnimatedStyle = Animated13.useAnimatedStyle(() => ({
2455
+ const pillAnimatedStyle = Animated12.useAnimatedStyle(() => ({
2422
2456
  transform: [{ translateX: pillX.value }],
2423
2457
  width: pillWidth.value
2424
2458
  }));
@@ -2431,7 +2465,7 @@ function Tabs({ tabs, variant = "pill", value, onValueChange, children, style })
2431
2465
  accessibilityRole: "tablist"
2432
2466
  },
2433
2467
  variant === "pill" && /* @__PURE__ */ React25__default.default.createElement(
2434
- Animated13__default.default.View,
2468
+ Animated12__default.default.View,
2435
2469
  {
2436
2470
  style: [
2437
2471
  styles19.pill,
@@ -2533,28 +2567,28 @@ function AccordionItemComponent({
2533
2567
  }) {
2534
2568
  const { colors } = useTheme();
2535
2569
  const resolvedIcon = item.iconName ? renderIcon(item.iconName, ms(16), item.iconColor ?? colors.foregroundMuted) : item.icon;
2536
- const isExpanded = Animated13.useSharedValue(isOpen);
2537
- const height = Animated13.useSharedValue(0);
2570
+ const isExpanded = Animated12.useSharedValue(isOpen);
2571
+ const height = Animated12.useSharedValue(0);
2538
2572
  React25__default.default.useEffect(() => {
2539
2573
  isExpanded.value = isOpen;
2540
2574
  }, [isOpen, isExpanded]);
2541
- const derivedHeight = Animated13.useDerivedValue(
2542
- () => Animated13.withTiming(height.value * Number(isExpanded.value), {
2575
+ const derivedHeight = Animated12.useDerivedValue(
2576
+ () => Animated12.withTiming(height.value * Number(isExpanded.value), {
2543
2577
  duration: isExpanded.value ? TIMINGS.expand.duration : TIMINGS.collapse.duration,
2544
2578
  easing: isExpanded.value ? EASINGS.expand : EASINGS.collapse
2545
2579
  })
2546
2580
  );
2547
- const derivedRotation = Animated13.useDerivedValue(
2548
- () => Animated13.withTiming(isExpanded.value ? 1 : 0, {
2581
+ const derivedRotation = Animated12.useDerivedValue(
2582
+ () => Animated12.withTiming(isExpanded.value ? 1 : 0, {
2549
2583
  duration: isExpanded.value ? TIMINGS.expand.duration : TIMINGS.collapse.duration,
2550
2584
  easing: isExpanded.value ? EASINGS.expand : EASINGS.collapse
2551
2585
  })
2552
2586
  );
2553
- const bodyStyle = Animated13.useAnimatedStyle(() => ({
2587
+ const bodyStyle = Animated12.useAnimatedStyle(() => ({
2554
2588
  height: derivedHeight.value,
2555
2589
  overflow: "hidden"
2556
2590
  }));
2557
- const rotationStyle = Animated13.useAnimatedStyle(() => ({
2591
+ const rotationStyle = Animated12.useAnimatedStyle(() => ({
2558
2592
  transform: [{ rotate: `${derivedRotation.value * 180}deg` }]
2559
2593
  }));
2560
2594
  return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles20.item, { backgroundColor: colors.card, borderColor: colors.border }] }, /* @__PURE__ */ React25__default.default.createElement(
@@ -2570,8 +2604,8 @@ function AccordionItemComponent({
2570
2604
  accessibilityLabel: item.trigger
2571
2605
  },
2572
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)),
2573
- /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [styles20.chevron, rotationStyle] }, /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Entypo, { name: "chevron-down", size: 18, color: colors.foregroundMuted }))
2574
- ), /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: bodyStyle }, /* @__PURE__ */ React25__default.default.createElement(
2607
+ /* @__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
+ ), /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: bodyStyle }, /* @__PURE__ */ React25__default.default.createElement(
2575
2609
  reactNative.View,
2576
2610
  {
2577
2611
  style: styles20.content,
@@ -3002,7 +3036,7 @@ function Select({
3002
3036
  }
3003
3037
  setPickerVisible(false);
3004
3038
  };
3005
- return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles23.container, style] }, label ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles23.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, !isWeb2 ? /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [animatedStyle, { opacity: disabled ? 0.45 : 1 }] }, /* @__PURE__ */ React25__default.default.createElement(
3039
+ return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles23.container, style] }, label ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles23.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, !isWeb2 ? /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [animatedStyle, { opacity: disabled ? 0.45 : 1 }] }, /* @__PURE__ */ React25__default.default.createElement(
3006
3040
  reactNative.TouchableOpacity,
3007
3041
  {
3008
3042
  style: [
@@ -3249,7 +3283,8 @@ function CurrencyInput({
3249
3283
  placeholder,
3250
3284
  editable,
3251
3285
  containerStyle,
3252
- style
3286
+ style,
3287
+ sheetMode
3253
3288
  }) {
3254
3289
  const handleChange = (text) => {
3255
3290
  const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text;
@@ -3282,7 +3317,8 @@ function CurrencyInput({
3282
3317
  prefixStyle,
3283
3318
  containerStyle,
3284
3319
  inputWrapperStyle: isLarge ? { paddingVertical: vs(16), minHeight: 72 } : void 0,
3285
- style: [inputStyle, style]
3320
+ style: [inputStyle, style],
3321
+ sheetMode
3286
3322
  }
3287
3323
  );
3288
3324
  }
@@ -3340,6 +3376,7 @@ var styles24 = reactNative.StyleSheet.create({
3340
3376
  }
3341
3377
  });
3342
3378
  function ListItemBase({
3379
+ imageSource,
3343
3380
  leftRender,
3344
3381
  rightRender,
3345
3382
  trailing,
@@ -3359,6 +3396,7 @@ function ListItemBase({
3359
3396
  style,
3360
3397
  titleStyle,
3361
3398
  subtitleStyle,
3399
+ subtitleNumberOfLines = 2,
3362
3400
  captionStyle,
3363
3401
  accessibilityLabel
3364
3402
  }) {
@@ -3367,7 +3405,7 @@ function ListItemBase({
3367
3405
  selectionAsync();
3368
3406
  onPress?.();
3369
3407
  };
3370
- const effectiveLeft = leftIcon ? renderIcon(leftIcon, 24, leftIconColor ?? colors.foreground) : leftRender ?? icon;
3408
+ const effectiveLeft = imageSource ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Image, { source: imageSource, style: styles25.image }) : leftIcon ? renderIcon(leftIcon, 24, leftIconColor ?? colors.foreground) : leftRender ?? icon;
3371
3409
  const effectiveRight = rightIcon ? renderIcon(rightIcon, 24, rightIconColor ?? colors.foregroundMuted) : rightRender ?? trailing;
3372
3410
  const cardStyle = variant === "card" ? {
3373
3411
  backgroundColor: colors.card,
@@ -3393,7 +3431,7 @@ function ListItemBase({
3393
3431
  reactNative.Text,
3394
3432
  {
3395
3433
  style: [styles25.subtitle, { color: colors.foregroundMuted }, subtitleStyle],
3396
- numberOfLines: 2,
3434
+ numberOfLines: subtitleNumberOfLines,
3397
3435
  allowFontScaling: true
3398
3436
  },
3399
3437
  subtitle
@@ -3448,6 +3486,11 @@ var styles25 = reactNative.StyleSheet.create({
3448
3486
  justifyContent: "center",
3449
3487
  flexShrink: 0
3450
3488
  },
3489
+ image: {
3490
+ width: s(40),
3491
+ height: s(40),
3492
+ borderRadius: 8
3493
+ },
3451
3494
  content: {
3452
3495
  flex: 1,
3453
3496
  gap: vs(4)
@@ -3861,6 +3904,7 @@ function ConfirmDialog({
3861
3904
  confirmLabel = "Confirm",
3862
3905
  cancelLabel = "Cancel",
3863
3906
  confirmVariant = "primary",
3907
+ loading = false,
3864
3908
  onConfirm,
3865
3909
  onCancel
3866
3910
  }) {
@@ -3900,6 +3944,8 @@ function ConfirmDialog({
3900
3944
  label: confirmLabel,
3901
3945
  variant: confirmVariant,
3902
3946
  fullWidth: true,
3947
+ loading,
3948
+ disabled: loading,
3903
3949
  onPress: () => {
3904
3950
  notificationSuccess();
3905
3951
  onConfirm();
@@ -4166,7 +4212,7 @@ function MediaCardBase({
4166
4212
  (title || subtitle || caption || footer) && /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles33.meta }, title ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles33.title, { color: colors.foreground }], numberOfLines: 2, allowFontScaling: true }, title) : null, subtitle ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles33.subtitle, { color: colors.foregroundSubtle }], numberOfLines: 1, allowFontScaling: true }, subtitle) : null, caption ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles33.caption, { color: colors.foregroundMuted }], numberOfLines: 1, allowFontScaling: true }, caption) : null, footer)
4167
4213
  );
4168
4214
  if (onPress) {
4169
- return /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: animatedStyle }, /* @__PURE__ */ React25__default.default.createElement(
4215
+ return /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: animatedStyle }, /* @__PURE__ */ React25__default.default.createElement(
4170
4216
  reactNative.TouchableOpacity,
4171
4217
  {
4172
4218
  onPress: handlePress,
@@ -4251,7 +4297,7 @@ var CategoryChip = React25__default.default.memo(function CategoryChip2({
4251
4297
  });
4252
4298
  const iconColor = selected ? colors.primaryForeground : colors.foregroundSubtle;
4253
4299
  const resolvedIcon = typeof item.icon === "string" ? renderIcon(item.icon, 16, iconColor) : item.icon ?? null;
4254
- return /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: scaleStyle, ...hoverHandlers }, /* @__PURE__ */ React25__default.default.createElement(
4300
+ return /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: scaleStyle, ...hoverHandlers }, /* @__PURE__ */ React25__default.default.createElement(
4255
4301
  reactNative.TouchableOpacity,
4256
4302
  {
4257
4303
  onPress: () => onSelect(item.value),
@@ -4674,15 +4720,15 @@ var styles38 = reactNative.StyleSheet.create({
4674
4720
  }
4675
4721
  });
4676
4722
  function Dot({ active, size, activeColor, inactiveColor, onPress, index, total }) {
4677
- const progress = Animated13.useSharedValue(active ? 1 : 0);
4723
+ const progress = Animated12.useSharedValue(active ? 1 : 0);
4678
4724
  React25.useEffect(() => {
4679
- progress.value = Animated13.withSpring(active ? 1 : 0, SPRINGS.glide);
4725
+ progress.value = Animated12.withSpring(active ? 1 : 0, SPRINGS.glide);
4680
4726
  }, [active, progress]);
4681
- const animatedStyle = Animated13.useAnimatedStyle(() => ({
4727
+ const animatedStyle = Animated12.useAnimatedStyle(() => ({
4682
4728
  width: size + progress.value * size * 1.5,
4683
- backgroundColor: Animated13.interpolateColor(progress.value, [0, 1], [inactiveColor, activeColor])
4729
+ backgroundColor: Animated12.interpolateColor(progress.value, [0, 1], [inactiveColor, activeColor])
4684
4730
  }));
4685
- const dot = /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [{ height: size, borderRadius: size / 2 }, animatedStyle] });
4731
+ const dot = /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [{ height: size, borderRadius: size / 2 }, animatedStyle] });
4686
4732
  if (!onPress) return dot;
4687
4733
  const handlePress = () => {
4688
4734
  selectionAsync();
@@ -4921,7 +4967,7 @@ function Cell({ item, selected, width, onPress }) {
4921
4967
  });
4922
4968
  const iconColor = selected ? colors.primary : colors.foregroundSubtle;
4923
4969
  const iconNode = item.icon ?? (item.iconName ? renderIcon(item.iconName, ms(24), iconColor) : null);
4924
- return /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [{ width }, animatedStyle] }, /* @__PURE__ */ React25__default.default.createElement(
4970
+ return /* @__PURE__ */ React25__default.default.createElement(Animated12__default.default.View, { style: [{ width }, animatedStyle] }, /* @__PURE__ */ React25__default.default.createElement(
4925
4971
  reactNative.TouchableOpacity,
4926
4972
  {
4927
4973
  onPress,
@@ -5283,22 +5329,22 @@ var styles43 = reactNative.StyleSheet.create({
5283
5329
  var MAX_SCALE = 3;
5284
5330
  var DOUBLE_TAP_SCALE = 2.5;
5285
5331
  function ZoomableImage({ source, width, height, onZoomChange }) {
5286
- const scale2 = Animated13.useSharedValue(1);
5287
- const savedScale = Animated13.useSharedValue(1);
5288
- const translateX = Animated13.useSharedValue(0);
5289
- const translateY = Animated13.useSharedValue(0);
5290
- const savedX = Animated13.useSharedValue(0);
5291
- const savedY = Animated13.useSharedValue(0);
5332
+ const scale2 = Animated12.useSharedValue(1);
5333
+ const savedScale = Animated12.useSharedValue(1);
5334
+ const translateX = Animated12.useSharedValue(0);
5335
+ const translateY = Animated12.useSharedValue(0);
5336
+ const savedX = Animated12.useSharedValue(0);
5337
+ const savedY = Animated12.useSharedValue(0);
5292
5338
  const reportZoom = React25.useCallback((zoomed) => onZoomChange(zoomed), [onZoomChange]);
5293
5339
  const reset = () => {
5294
5340
  "worklet";
5295
- scale2.value = Animated13.withTiming(1);
5341
+ scale2.value = Animated12.withTiming(1);
5296
5342
  savedScale.value = 1;
5297
- translateX.value = Animated13.withTiming(0);
5298
- translateY.value = Animated13.withTiming(0);
5343
+ translateX.value = Animated12.withTiming(0);
5344
+ translateY.value = Animated12.withTiming(0);
5299
5345
  savedX.value = 0;
5300
5346
  savedY.value = 0;
5301
- Animated13.runOnJS(reportZoom)(false);
5347
+ Animated12.runOnJS(reportZoom)(false);
5302
5348
  };
5303
5349
  const pinch = reactNativeGestureHandler.Gesture.Pinch().onUpdate((e) => {
5304
5350
  scale2.value = Math.max(1, Math.min(savedScale.value * e.scale, MAX_SCALE));
@@ -5307,7 +5353,7 @@ function ZoomableImage({ source, width, height, onZoomChange }) {
5307
5353
  if (scale2.value <= 1) {
5308
5354
  reset();
5309
5355
  } else {
5310
- Animated13.runOnJS(reportZoom)(true);
5356
+ Animated12.runOnJS(reportZoom)(true);
5311
5357
  }
5312
5358
  });
5313
5359
  const pan = reactNativeGestureHandler.Gesture.Pan().onUpdate((e) => {
@@ -5322,21 +5368,21 @@ function ZoomableImage({ source, width, height, onZoomChange }) {
5322
5368
  if (scale2.value > 1) {
5323
5369
  reset();
5324
5370
  } else {
5325
- scale2.value = Animated13.withTiming(DOUBLE_TAP_SCALE);
5371
+ scale2.value = Animated12.withTiming(DOUBLE_TAP_SCALE);
5326
5372
  savedScale.value = DOUBLE_TAP_SCALE;
5327
- Animated13.runOnJS(reportZoom)(true);
5373
+ Animated12.runOnJS(reportZoom)(true);
5328
5374
  }
5329
5375
  });
5330
5376
  const composed = reactNativeGestureHandler.Gesture.Exclusive(doubleTap, reactNativeGestureHandler.Gesture.Simultaneous(pinch, pan));
5331
- const animatedStyle = Animated13.useAnimatedStyle(() => ({
5377
+ const animatedStyle = Animated12.useAnimatedStyle(() => ({
5332
5378
  transform: [
5333
5379
  { translateX: translateX.value },
5334
5380
  { translateY: translateY.value },
5335
5381
  { scale: scale2.value }
5336
5382
  ]
5337
5383
  }));
5338
- return /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: composed }, /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [{ width, height }, styles44.imageWrap] }, /* @__PURE__ */ React25__default.default.createElement(
5339
- Animated13__default.default.Image,
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,
5340
5386
  {
5341
5387
  source,
5342
5388
  style: [{ width, height }, animatedStyle],
@@ -5359,25 +5405,25 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
5359
5405
  });
5360
5406
  return () => cancelAnimationFrame(handle);
5361
5407
  }, [visible, initialIndex, width]);
5362
- const dragY = Animated13.useSharedValue(0);
5408
+ const dragY = Animated12.useSharedValue(0);
5363
5409
  const DISMISS_THRESHOLD = height * 0.18;
5364
5410
  const closeViewer = React25.useCallback(() => onClose(), [onClose]);
5365
5411
  const swipeDown = reactNativeGestureHandler.Gesture.Pan().enabled(pagingEnabled).activeOffsetY(12).failOffsetX([-16, 16]).onUpdate((e) => {
5366
5412
  dragY.value = Math.max(0, e.translationY);
5367
5413
  }).onEnd((e) => {
5368
5414
  if (e.translationY > DISMISS_THRESHOLD || e.velocityY > 800) {
5369
- Animated13.runOnJS(closeViewer)();
5415
+ Animated12.runOnJS(closeViewer)();
5370
5416
  } else {
5371
- dragY.value = Animated13.withTiming(0);
5417
+ dragY.value = Animated12.withTiming(0);
5372
5418
  }
5373
5419
  });
5374
5420
  React25__default.default.useEffect(() => {
5375
5421
  if (visible) dragY.value = 0;
5376
5422
  }, [visible, dragY]);
5377
- const dismissStyle = Animated13.useAnimatedStyle(() => ({
5423
+ const dismissStyle = Animated12.useAnimatedStyle(() => ({
5378
5424
  transform: [{ translateY: dragY.value }]
5379
5425
  }));
5380
- const backdropStyle = Animated13.useAnimatedStyle(() => ({
5426
+ const backdropStyle = Animated12.useAnimatedStyle(() => ({
5381
5427
  opacity: 1 - Math.min(dragY.value / (height * 0.5), 0.85)
5382
5428
  }));
5383
5429
  const onMomentumEnd = (e) => {
@@ -5388,7 +5434,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
5388
5434
  scrollRef.current?.scrollTo({ x: page * width, animated: true });
5389
5435
  setIndex(page);
5390
5436
  };
5391
- 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(Animated13__default.default.View, { style: [styles44.backdrop, backdropStyle], pointerEvents: "none" }), /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: [styles44.container, dismissStyle] }, /* @__PURE__ */ React25__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: swipeDown }, /* @__PURE__ */ React25__default.default.createElement(Animated13__default.default.View, { style: styles44.root }, /* @__PURE__ */ React25__default.default.createElement(
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(
5392
5438
  reactNative.ScrollView,
5393
5439
  {
5394
5440
  ref: scrollRef,
@@ -5465,6 +5511,240 @@ var styles44 = reactNative.StyleSheet.create({
5465
5511
  alignItems: "center"
5466
5512
  }
5467
5513
  });
5514
+ function SheetSelectChip({
5515
+ option,
5516
+ selected,
5517
+ onPress
5518
+ }) {
5519
+ const { colors } = useTheme();
5520
+ const handlePress = () => {
5521
+ selectionAsync();
5522
+ onPress();
5523
+ };
5524
+ const iconColor = selected ? colors.primaryForeground : colors.foreground;
5525
+ const resolvedIcon = option.iconName ? renderIcon(option.iconName, ms(13), iconColor) : null;
5526
+ return /* @__PURE__ */ React25__default.default.createElement(
5527
+ PressableChip,
5528
+ {
5529
+ onPress: option.disabled ? void 0 : handlePress,
5530
+ rippleColor: "transparent",
5531
+ touchSoundDisabled: true,
5532
+ accessibilityRole: "button",
5533
+ accessibilityLabel: option.disabled ? `${option.label}, unavailable` : option.label,
5534
+ accessibilityState: { selected, disabled: option.disabled }
5535
+ },
5536
+ /* @__PURE__ */ React25__default.default.createElement(
5537
+ reactNativeEase.EaseView,
5538
+ {
5539
+ style: [styles45.chip, option.disabled && styles45.chipDisabled],
5540
+ animate: {
5541
+ backgroundColor: selected ? colors.primary : colors.surface,
5542
+ borderColor: selected ? colors.primary : colors.border
5543
+ },
5544
+ transition: COLOR_TRANSITION
5545
+ },
5546
+ resolvedIcon ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles45.chipIcon }, resolvedIcon) : null,
5547
+ /* @__PURE__ */ React25__default.default.createElement(
5548
+ reactNative.Text,
5549
+ {
5550
+ style: [styles45.chipLabel, { color: selected ? colors.primaryForeground : colors.foreground }],
5551
+ allowFontScaling: true
5552
+ },
5553
+ option.label
5554
+ )
5555
+ )
5556
+ );
5557
+ }
5558
+ function SheetSelect({
5559
+ options,
5560
+ value,
5561
+ onValueChange,
5562
+ multiSelect = false,
5563
+ label,
5564
+ error,
5565
+ wrap = false,
5566
+ style,
5567
+ accessibilityLabel
5568
+ }) {
5569
+ const { colors } = useTheme();
5570
+ const isSelected2 = (optionValue) => {
5571
+ if (Array.isArray(value)) return value.includes(optionValue);
5572
+ return optionValue === value;
5573
+ };
5574
+ const handlePress = (optionValue) => {
5575
+ if (!multiSelect) {
5576
+ onValueChange?.(optionValue);
5577
+ return;
5578
+ }
5579
+ const currentArray = Array.isArray(value) ? value : value != null ? [value] : [];
5580
+ const alreadySelected = currentArray.includes(optionValue);
5581
+ const newArray = alreadySelected ? currentArray.filter((v) => v !== optionValue) : [...currentArray, optionValue];
5582
+ onValueChange?.(newArray);
5583
+ };
5584
+ const chips = options.map((opt) => /* @__PURE__ */ React25__default.default.createElement(
5585
+ SheetSelectChip,
5586
+ {
5587
+ key: opt.value,
5588
+ option: opt,
5589
+ selected: isSelected2(opt.value),
5590
+ onPress: () => handlePress(opt.value)
5591
+ }
5592
+ ));
5593
+ return /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles45.container, style], accessibilityLabel }, label ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles45.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, wrap ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles45.wrapContainer }, chips) : /* @__PURE__ */ React25__default.default.createElement(
5594
+ reactNative.ScrollView,
5595
+ {
5596
+ horizontal: true,
5597
+ showsHorizontalScrollIndicator: false,
5598
+ contentContainerStyle: styles45.scrollContent
5599
+ },
5600
+ chips
5601
+ ), error ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles45.error, { color: colors.destructive }], allowFontScaling: true, accessibilityLiveRegion: "polite" }, error) : null);
5602
+ }
5603
+ var styles45 = reactNative.StyleSheet.create({
5604
+ container: {
5605
+ gap: vs(8)
5606
+ },
5607
+ label: {
5608
+ fontFamily: "Sohne-Medium",
5609
+ fontSize: ms(14)
5610
+ },
5611
+ scrollContent: {
5612
+ flexDirection: "row",
5613
+ gap: s(8)
5614
+ },
5615
+ wrapContainer: {
5616
+ flexDirection: "row",
5617
+ flexWrap: "wrap",
5618
+ gap: s(8)
5619
+ },
5620
+ chip: {
5621
+ borderRadius: RADIUS.full,
5622
+ paddingHorizontal: s(14),
5623
+ paddingVertical: vs(10),
5624
+ minHeight: 44,
5625
+ borderWidth: 1,
5626
+ alignItems: "center",
5627
+ justifyContent: "center",
5628
+ flexDirection: "row",
5629
+ gap: s(5)
5630
+ },
5631
+ chipDisabled: {
5632
+ opacity: 0.4
5633
+ },
5634
+ chipIcon: {
5635
+ alignItems: "center",
5636
+ justifyContent: "center"
5637
+ },
5638
+ chipLabel: {
5639
+ fontFamily: "Sohne-Medium",
5640
+ fontSize: ms(13),
5641
+ lineHeight: mvs(18)
5642
+ },
5643
+ error: {
5644
+ fontFamily: "Sohne-Regular",
5645
+ fontSize: ms(13)
5646
+ }
5647
+ });
5648
+ function ImageUpload({
5649
+ value,
5650
+ onChange,
5651
+ loading = false,
5652
+ placeholder = "Tap to add image",
5653
+ width,
5654
+ height = 200,
5655
+ borderRadius = RADIUS.lg,
5656
+ resizeMode = "cover",
5657
+ disabled = false,
5658
+ style,
5659
+ accessibilityLabel
5660
+ }) {
5661
+ const { colors } = useTheme();
5662
+ const handlePress = async () => {
5663
+ if (disabled || loading) return;
5664
+ impactLight();
5665
+ let ImagePicker;
5666
+ try {
5667
+ ImagePicker = await import('expo-image-picker');
5668
+ } catch {
5669
+ if (__DEV__) console.warn("[ImageUpload] expo-image-picker not installed. Add it as a dependency.");
5670
+ return;
5671
+ }
5672
+ if (reactNative.Platform.OS !== "web") {
5673
+ const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
5674
+ if (status !== "granted") return;
5675
+ }
5676
+ const result = await ImagePicker.launchImageLibraryAsync({
5677
+ mediaTypes: ["images"],
5678
+ allowsEditing: true,
5679
+ quality: 0.8
5680
+ });
5681
+ if (!result.canceled && result.assets[0]) {
5682
+ onChange?.(result.assets[0].uri);
5683
+ }
5684
+ };
5685
+ const containerStyle = {
5686
+ width,
5687
+ height,
5688
+ borderRadius,
5689
+ borderWidth: value ? 0 : 1,
5690
+ borderStyle: "dashed",
5691
+ borderColor: colors.border,
5692
+ backgroundColor: value ? "transparent" : colors.surface,
5693
+ overflow: "hidden"
5694
+ };
5695
+ return /* @__PURE__ */ React25__default.default.createElement(
5696
+ PressableCard,
5697
+ {
5698
+ onPress: handlePress,
5699
+ enabled: !disabled && !loading,
5700
+ rippleColor: "transparent",
5701
+ touchSoundDisabled: true,
5702
+ accessibilityRole: "button",
5703
+ accessibilityLabel: accessibilityLabel ?? (value ? "Change image" : placeholder),
5704
+ accessibilityState: { disabled: disabled || loading },
5705
+ style: [containerStyle, style]
5706
+ },
5707
+ value ? /* @__PURE__ */ React25__default.default.createElement(
5708
+ reactNative.Image,
5709
+ {
5710
+ source: { uri: value },
5711
+ style: [reactNative.StyleSheet.absoluteFillObject, { borderRadius }],
5712
+ resizeMode
5713
+ }
5714
+ ) : /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles46.placeholder }, /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "image", size: ms(28), color: colors.foregroundMuted }), /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles46.placeholderText, { color: colors.foregroundMuted }], allowFontScaling: true }, placeholder)),
5715
+ loading ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles46.loadingOverlay, { backgroundColor: colors.overlay }] }, /* @__PURE__ */ React25__default.default.createElement(Spinner, { size: "md" })) : null,
5716
+ value && !loading ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles46.editBadge, pointerEvents: "none" }, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: [styles46.editBadgeInner, { backgroundColor: colors.overlay }] }, /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "edit-2", size: ms(12), color: "#fff" }))) : null
5717
+ );
5718
+ }
5719
+ var styles46 = reactNative.StyleSheet.create({
5720
+ placeholder: {
5721
+ flex: 1,
5722
+ alignItems: "center",
5723
+ justifyContent: "center",
5724
+ gap: vs(8)
5725
+ },
5726
+ placeholderText: {
5727
+ fontFamily: "Sohne-Regular",
5728
+ fontSize: ms(13)
5729
+ },
5730
+ loadingOverlay: {
5731
+ ...reactNative.StyleSheet.absoluteFillObject,
5732
+ alignItems: "center",
5733
+ justifyContent: "center"
5734
+ },
5735
+ editBadge: {
5736
+ position: "absolute",
5737
+ bottom: vs(8),
5738
+ right: s(8)
5739
+ },
5740
+ editBadgeInner: {
5741
+ width: s(28),
5742
+ height: s(28),
5743
+ borderRadius: 999,
5744
+ alignItems: "center",
5745
+ justifyContent: "center"
5746
+ }
5747
+ });
5468
5748
 
5469
5749
  // src/utils/typography.ts
5470
5750
  function getResponsiveFontSize(text, maxSize, steps = [
@@ -5479,6 +5759,42 @@ function getResponsiveFontSize(text, maxSize, steps = [
5479
5759
  }
5480
5760
  return maxSize - 8;
5481
5761
  }
5762
+ function useConfirmDialog(options) {
5763
+ const [visible, setVisible] = React25.useState(false);
5764
+ const [target, setTarget] = React25.useState(null);
5765
+ const [loading, setLoading] = React25.useState(false);
5766
+ const open = React25.useCallback((t) => {
5767
+ setTarget(t ?? null);
5768
+ setVisible(true);
5769
+ }, []);
5770
+ const handleConfirm = React25.useCallback(async () => {
5771
+ setLoading(true);
5772
+ try {
5773
+ await options.onConfirm();
5774
+ } finally {
5775
+ setLoading(false);
5776
+ setVisible(false);
5777
+ setTarget(null);
5778
+ }
5779
+ }, [options]);
5780
+ const handleCancel = React25.useCallback(() => {
5781
+ setVisible(false);
5782
+ setTarget(null);
5783
+ options.onCancel?.();
5784
+ }, [options]);
5785
+ return {
5786
+ visible,
5787
+ target,
5788
+ loading,
5789
+ open,
5790
+ dialogProps: {
5791
+ visible,
5792
+ loading,
5793
+ onConfirm: handleConfirm,
5794
+ onCancel: handleCancel
5795
+ }
5796
+ };
5797
+ }
5482
5798
 
5483
5799
  Object.defineProperty(exports, "BottomSheetModalProvider", {
5484
5800
  enumerable: true,
@@ -5523,6 +5839,7 @@ exports.FormSection = FormSection;
5523
5839
  exports.ICON_SIZES = ICON_SIZES;
5524
5840
  exports.Icon = Icon;
5525
5841
  exports.IconButton = IconButton;
5842
+ exports.ImageUpload = ImageUpload;
5526
5843
  exports.ImageViewer = ImageViewer;
5527
5844
  exports.Input = Input;
5528
5845
  exports.LabelValue = LabelValue;
@@ -5549,6 +5866,7 @@ exports.Select = Select;
5549
5866
  exports.SelectableGrid = SelectableGrid;
5550
5867
  exports.Separator = Separator;
5551
5868
  exports.Sheet = Sheet;
5869
+ exports.SheetSelect = SheetSelect;
5552
5870
  exports.Skeleton = Skeleton;
5553
5871
  exports.Slider = Slider;
5554
5872
  exports.Spinner = Spinner;
@@ -5579,5 +5897,6 @@ exports.notificationWarning = notificationWarning;
5579
5897
  exports.renderIcon = renderIcon;
5580
5898
  exports.richHaptics = richHaptics;
5581
5899
  exports.selectionAsync = selectionAsync;
5900
+ exports.useConfirmDialog = useConfirmDialog;
5582
5901
  exports.useTheme = useTheme;
5583
5902
  exports.useToast = useToast;