@blinkdotnew/mobile-ui 2.0.0-alpha.11 → 2.0.0-alpha.12

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.mjs CHANGED
@@ -13,11 +13,11 @@ import {
13
13
  SizableStack,
14
14
  ThemeableStack,
15
15
  Frame,
16
- XStack as XStack27,
17
- YStack as YStack32,
16
+ XStack as XStack37,
17
+ YStack as YStack41,
18
18
  ZStack,
19
- ScrollView as ScrollView5,
20
- Circle as Circle8,
19
+ ScrollView as ScrollView7,
20
+ Circle as Circle9,
21
21
  Square,
22
22
  Spacer,
23
23
  EnsureFlexed,
@@ -38,11 +38,11 @@ import {
38
38
  H6 as H62,
39
39
  Heading,
40
40
  Paragraph,
41
- SizableText as SizableText34,
41
+ SizableText as SizableText44,
42
42
  Text,
43
43
  Label,
44
- Button as Button8,
45
- Input as Input3,
44
+ Button as Button11,
45
+ Input as Input5,
46
46
  TextArea,
47
47
  Switch as Switch2,
48
48
  Checkbox,
@@ -54,14 +54,14 @@ import {
54
54
  Card as Card2,
55
55
  Avatar as Avatar2,
56
56
  Separator as Separator5,
57
- Image as Image6,
57
+ Image as Image9,
58
58
  Progress,
59
59
  Spinner as Spinner2,
60
60
  ListItem as ListItem2,
61
61
  Anchor,
62
62
  Sheet as Sheet3,
63
63
  Dialog,
64
- AlertDialog as AlertDialog2,
64
+ AlertDialog as AlertDialog3,
65
65
  Popover as Popover2,
66
66
  Tooltip,
67
67
  TooltipSimple,
@@ -89,7 +89,7 @@ import {
89
89
  addTheme,
90
90
  updateTheme,
91
91
  replaceTheme,
92
- styled as styled12,
92
+ styled as styled13,
93
93
  withStaticProperties as withStaticProperties2,
94
94
  isWeb,
95
95
  isClient,
@@ -1035,78 +1035,137 @@ function Container({ children, maxWidth = 500, centered = true, padding = "$4" }
1035
1035
  }
1036
1036
 
1037
1037
  // src/patterns/PaywallScreen.tsx
1038
- import { Button as Button3, SizableText as SizableText17, XStack as XStack10, YStack as YStack14, Circle as Circle2 } from "tamagui";
1038
+ import { useState as useState5, useEffect as useEffect3 } from "react";
1039
+ import { Button as Button3, SizableText as SizableText17, XStack as XStack10, YStack as YStack14, ScrollView as ScrollView2 } from "tamagui";
1039
1040
  import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
1041
+ function useCountdown(minutes) {
1042
+ const [seconds, setSeconds] = useState5((minutes ?? 0) * 60);
1043
+ useEffect3(() => {
1044
+ if (!minutes) return;
1045
+ setSeconds(minutes * 60);
1046
+ const id = setInterval(() => setSeconds((s) => s > 0 ? s - 1 : 0), 1e3);
1047
+ return () => clearInterval(id);
1048
+ }, [minutes]);
1049
+ const mm = String(Math.floor(seconds / 60)).padStart(2, "0");
1050
+ const ss = String(seconds % 60).padStart(2, "0");
1051
+ return { display: `${mm}:${ss}`, expired: seconds <= 0 };
1052
+ }
1053
+ function PlanCard({ plan, selected, onPress }) {
1054
+ return /* @__PURE__ */ jsxs13(
1055
+ YStack14,
1056
+ {
1057
+ flex: 1,
1058
+ padding: "$3",
1059
+ borderRadius: "$5",
1060
+ borderWidth: 2,
1061
+ borderColor: selected ? "$color9" : "$color5",
1062
+ backgroundColor: selected ? "$color3" : "$color1",
1063
+ pressStyle: { scale: 0.97, opacity: 0.9 },
1064
+ animation: "quick",
1065
+ onPress,
1066
+ cursor: "pointer",
1067
+ position: "relative",
1068
+ gap: "$1.5",
1069
+ alignItems: "center",
1070
+ children: [
1071
+ plan.popular && /* @__PURE__ */ jsx20(YStack14, { position: "absolute", top: -10, backgroundColor: "$color9", paddingHorizontal: "$2", paddingVertical: 2, borderRadius: "$10", children: /* @__PURE__ */ jsx20(SizableText17, { size: "$1", color: "white", fontWeight: "700", children: "BEST VALUE" }) }),
1072
+ plan.savings && /* @__PURE__ */ jsx20(YStack14, { position: "absolute", top: plan.popular ? -24 : -10, right: 6, backgroundColor: "$green9", paddingHorizontal: "$1.5", paddingVertical: 2, borderRadius: "$10", children: /* @__PURE__ */ jsx20(SizableText17, { size: "$1", color: "white", fontWeight: "700", children: plan.savings }) }),
1073
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$2", fontWeight: "600", color: "$color11", paddingTop: plan.popular ? "$1" : 0, children: plan.name }),
1074
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$7", fontWeight: "800", children: plan.price }),
1075
+ /* @__PURE__ */ jsxs13(SizableText17, { size: "$2", color: "$color9", children: [
1076
+ "/",
1077
+ plan.period
1078
+ ] }),
1079
+ plan.pricePerWeek && /* @__PURE__ */ jsx20(SizableText17, { size: "$1", color: "$color10", children: plan.pricePerWeek }),
1080
+ plan.trial && /* @__PURE__ */ jsx20(YStack14, { backgroundColor: "$green3", paddingHorizontal: "$2", paddingVertical: 2, borderRadius: "$10", marginTop: "$1", children: /* @__PURE__ */ jsx20(SizableText17, { size: "$1", color: "$green9", fontWeight: "700", children: plan.trial }) })
1081
+ ]
1082
+ }
1083
+ );
1084
+ }
1040
1085
  function PaywallScreen({
1041
- title = "Upgrade Your Experience",
1042
- subtitle = "Choose the plan that works for you",
1086
+ title = "Unlock Premium",
1087
+ subtitle,
1088
+ features = [],
1043
1089
  plans,
1044
1090
  selectedPlan,
1045
1091
  onSelectPlan,
1046
1092
  onContinue,
1093
+ onClose,
1047
1094
  onRestore,
1048
- continueLabel = "Continue"
1095
+ onTerms,
1096
+ onPrivacy,
1097
+ continueLabel = "Continue",
1098
+ reassurance = "Cancel anytime",
1099
+ hero,
1100
+ socialProof,
1101
+ countdownMinutes,
1102
+ badge
1049
1103
  }) {
1050
- return /* @__PURE__ */ jsxs13(YStack14, { flex: 1, padding: "$4", gap: "$5", backgroundColor: "$background", children: [
1051
- /* @__PURE__ */ jsxs13(YStack14, { gap: "$2", paddingTop: "$6", children: [
1052
- /* @__PURE__ */ jsx20(SizableText17, { size: "$9", fontWeight: "700", textAlign: "center", children: title }),
1053
- /* @__PURE__ */ jsx20(SizableText17, { size: "$4", color: "$color10", textAlign: "center", children: subtitle })
1054
- ] }),
1055
- /* @__PURE__ */ jsx20(YStack14, { gap: "$3", flex: 1, children: plans.map((plan) => /* @__PURE__ */ jsxs13(
1104
+ const selected = selectedPlan ?? plans.find((p) => p.popular)?.id ?? plans[0]?.id;
1105
+ const countdown = useCountdown(countdownMinutes);
1106
+ return /* @__PURE__ */ jsxs13(YStack14, { flex: 1, backgroundColor: "$background", children: [
1107
+ onClose && /* @__PURE__ */ jsx20(XStack10, { position: "absolute", top: "$4", right: "$4", zIndex: 10, children: /* @__PURE__ */ jsx20(Button3, { size: "$3", circular: true, chromeless: true, onPress: onClose, pressStyle: { opacity: 0.6 }, children: /* @__PURE__ */ jsx20(SizableText17, { size: "$5", color: "$color9", children: "\u2715" }) }) }),
1108
+ /* @__PURE__ */ jsx20(ScrollView2, { flex: 1, contentContainerStyle: { paddingBottom: 220 }, children: /* @__PURE__ */ jsxs13(YStack14, { padding: "$4", gap: "$4", paddingTop: "$8", children: [
1109
+ hero && /* @__PURE__ */ jsx20(YStack14, { alignItems: "center", paddingVertical: "$3", children: hero }),
1110
+ badge && /* @__PURE__ */ jsx20(XStack10, { justifyContent: "center", children: /* @__PURE__ */ jsx20(YStack14, { backgroundColor: "$color9", paddingHorizontal: "$3", paddingVertical: "$1", borderRadius: "$10", children: /* @__PURE__ */ jsx20(SizableText17, { size: "$2", color: "white", fontWeight: "700", children: badge }) }) }),
1111
+ /* @__PURE__ */ jsxs13(YStack14, { gap: "$1.5", alignItems: "center", children: [
1112
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$9", fontWeight: "800", textAlign: "center", children: title }),
1113
+ subtitle && /* @__PURE__ */ jsx20(SizableText17, { size: "$4", color: "$color10", textAlign: "center", children: subtitle }),
1114
+ socialProof && /* @__PURE__ */ jsx20(SizableText17, { size: "$3", color: "$color9", fontWeight: "600", textAlign: "center", children: socialProof })
1115
+ ] }),
1116
+ countdownMinutes && !countdown.expired && /* @__PURE__ */ jsxs13(XStack10, { justifyContent: "center", padding: "$2", backgroundColor: "$red3", borderRadius: "$4", alignSelf: "center", paddingHorizontal: "$4", gap: "$2", alignItems: "center", children: [
1117
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$2", color: "$red9", fontWeight: "600", children: "Offer ends in" }),
1118
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$5", color: "$red9", fontWeight: "800", fontFamily: "$mono", children: countdown.display })
1119
+ ] }),
1120
+ features.length > 0 && /* @__PURE__ */ jsx20(YStack14, { gap: "$2.5", paddingHorizontal: "$2", children: features.map((f, i) => /* @__PURE__ */ jsxs13(XStack10, { gap: "$2.5", alignItems: "center", children: [
1121
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$4", color: "$green9", children: "\u2713" }),
1122
+ /* @__PURE__ */ jsx20(SizableText17, { size: "$4", color: "$color11", flex: 1, children: f })
1123
+ ] }, i)) }),
1124
+ /* @__PURE__ */ jsx20(XStack10, { gap: "$3", paddingTop: "$1", children: plans.map((plan) => /* @__PURE__ */ jsx20(PlanCard, { plan, selected: selected === plan.id, onPress: () => onSelectPlan?.(plan.id) }, plan.id)) })
1125
+ ] }) }),
1126
+ /* @__PURE__ */ jsxs13(
1056
1127
  YStack14,
1057
1128
  {
1129
+ position: "absolute",
1130
+ bottom: 0,
1131
+ left: 0,
1132
+ right: 0,
1058
1133
  padding: "$4",
1059
- borderRadius: "$5",
1060
- borderWidth: 2,
1061
- borderColor: selectedPlan === plan.id ? "$color9" : "$color4",
1062
- backgroundColor: selectedPlan === plan.id ? "$color2" : "$color1",
1063
- pressStyle: { scale: 0.98 },
1064
- onPress: () => onSelectPlan?.(plan.id),
1065
- cursor: "pointer",
1134
+ paddingBottom: "$6",
1135
+ backgroundColor: "$background",
1136
+ borderTopWidth: 1,
1137
+ borderTopColor: "$color4",
1138
+ gap: "$2.5",
1066
1139
  children: [
1067
- /* @__PURE__ */ jsxs13(XStack10, { justifyContent: "space-between", alignItems: "center", children: [
1068
- /* @__PURE__ */ jsxs13(YStack14, { children: [
1069
- /* @__PURE__ */ jsx20(SizableText17, { size: "$5", fontWeight: "600", children: plan.name }),
1070
- /* @__PURE__ */ jsxs13(XStack10, { alignItems: "baseline", gap: "$1", children: [
1071
- /* @__PURE__ */ jsx20(SizableText17, { size: "$8", fontWeight: "700", children: plan.price }),
1072
- /* @__PURE__ */ jsxs13(SizableText17, { size: "$3", color: "$color9", children: [
1073
- "/",
1074
- plan.period
1075
- ] })
1076
- ] })
1077
- ] }),
1078
- plan.popular && /* @__PURE__ */ jsx20(YStack14, { backgroundColor: "$color9", paddingHorizontal: "$2", paddingVertical: "$1", borderRadius: "$10", children: /* @__PURE__ */ jsx20(SizableText17, { size: "$1", color: "$color1", fontWeight: "600", children: "POPULAR" }) })
1079
- ] }),
1080
- /* @__PURE__ */ jsx20(YStack14, { gap: "$2", paddingTop: "$3", children: plan.features.map((feature, i) => /* @__PURE__ */ jsxs13(XStack10, { gap: "$2", alignItems: "center", children: [
1081
- /* @__PURE__ */ jsx20(Circle2, { size: 6, backgroundColor: "$green9" }),
1082
- /* @__PURE__ */ jsx20(SizableText17, { size: "$3", color: "$color11", children: feature })
1083
- ] }, i)) })
1140
+ /* @__PURE__ */ jsx20(
1141
+ Button3,
1142
+ {
1143
+ size: "$5",
1144
+ backgroundColor: "$color9",
1145
+ color: "$color1",
1146
+ onPress: onContinue,
1147
+ pressStyle: { backgroundColor: "$color8", scale: 0.98 },
1148
+ animation: "quick",
1149
+ borderRadius: "$10",
1150
+ fontWeight: "700",
1151
+ children: continueLabel
1152
+ }
1153
+ ),
1154
+ reassurance && /* @__PURE__ */ jsx20(SizableText17, { size: "$2", color: "$color9", textAlign: "center", children: reassurance }),
1155
+ /* @__PURE__ */ jsxs13(XStack10, { justifyContent: "center", gap: "$3", children: [
1156
+ onRestore && /* @__PURE__ */ jsx20(SizableText17, { size: "$2", color: "$color8", onPress: onRestore, pressStyle: { opacity: 0.6 }, children: "Restore" }),
1157
+ onTerms && /* @__PURE__ */ jsx20(SizableText17, { size: "$2", color: "$color8", onPress: onTerms, pressStyle: { opacity: 0.6 }, children: "Terms" }),
1158
+ onPrivacy && /* @__PURE__ */ jsx20(SizableText17, { size: "$2", color: "$color8", onPress: onPrivacy, pressStyle: { opacity: 0.6 }, children: "Privacy" })
1159
+ ] })
1084
1160
  ]
1085
- },
1086
- plan.id
1087
- )) }),
1088
- /* @__PURE__ */ jsxs13(YStack14, { gap: "$3", paddingBottom: "$4", children: [
1089
- /* @__PURE__ */ jsx20(
1090
- Button3,
1091
- {
1092
- size: "$5",
1093
- backgroundColor: "$color9",
1094
- color: "$color1",
1095
- onPress: onContinue,
1096
- hoverStyle: { backgroundColor: "$color10" },
1097
- pressStyle: { backgroundColor: "$color8" },
1098
- borderRadius: "$5",
1099
- children: continueLabel
1100
- }
1101
- ),
1102
- onRestore && /* @__PURE__ */ jsx20(Button3, { size: "$3", chromeless: true, onPress: onRestore, children: /* @__PURE__ */ jsx20(SizableText17, { size: "$3", color: "$color9", children: "Restore Purchases" }) })
1103
- ] })
1161
+ }
1162
+ )
1104
1163
  ] });
1105
1164
  }
1106
1165
 
1107
1166
  // src/patterns/OnboardingCarousel.tsx
1108
- import { useState as useState5 } from "react";
1109
- import { Button as Button4, SizableText as SizableText18, XStack as XStack11, YStack as YStack15, Circle as Circle3 } from "tamagui";
1167
+ import { useState as useState6 } from "react";
1168
+ import { Button as Button4, SizableText as SizableText18, XStack as XStack11, YStack as YStack15, Circle as Circle2 } from "tamagui";
1110
1169
  import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1111
1170
  function OnboardingCarousel({
1112
1171
  steps,
@@ -1116,13 +1175,13 @@ function OnboardingCarousel({
1116
1175
  skipLabel = "Skip",
1117
1176
  nextLabel = "Next"
1118
1177
  }) {
1119
- const [current, setCurrent] = useState5(0);
1178
+ const [current, setCurrent] = useState6(0);
1120
1179
  const isLast = current === steps.length - 1;
1121
1180
  const step = steps[current];
1122
1181
  return /* @__PURE__ */ jsxs14(YStack15, { flex: 1, backgroundColor: "$background", padding: "$4", justifyContent: "space-between", children: [
1123
1182
  /* @__PURE__ */ jsx21(XStack11, { justifyContent: "flex-end", paddingTop: "$4", children: !isLast && onSkip && /* @__PURE__ */ jsx21(Button4, { chromeless: true, onPress: onSkip, children: /* @__PURE__ */ jsx21(SizableText18, { size: "$4", color: "$color9", children: skipLabel }) }) }),
1124
1183
  /* @__PURE__ */ jsxs14(YStack15, { flex: 1, alignItems: "center", justifyContent: "center", gap: "$5", paddingHorizontal: "$4", children: [
1125
- step?.icon && /* @__PURE__ */ jsx21(Circle3, { size: 120, backgroundColor: "$color2", alignItems: "center", justifyContent: "center", children: step.icon }),
1184
+ step?.icon && /* @__PURE__ */ jsx21(Circle2, { size: 120, backgroundColor: "$color2", alignItems: "center", justifyContent: "center", children: step.icon }),
1126
1185
  /* @__PURE__ */ jsxs14(YStack15, { gap: "$3", alignItems: "center", children: [
1127
1186
  /* @__PURE__ */ jsx21(SizableText18, { size: "$9", fontWeight: "700", textAlign: "center", children: step?.title }),
1128
1187
  /* @__PURE__ */ jsx21(SizableText18, { size: "$4", color: "$color10", textAlign: "center", maxWidth: 300, children: step?.description })
@@ -1130,7 +1189,7 @@ function OnboardingCarousel({
1130
1189
  ] }),
1131
1190
  /* @__PURE__ */ jsxs14(YStack15, { gap: "$3", paddingBottom: "$2", children: [
1132
1191
  /* @__PURE__ */ jsx21(XStack11, { justifyContent: "center", gap: "$2", children: steps.map((_, i) => /* @__PURE__ */ jsx21(
1133
- Circle3,
1192
+ Circle2,
1134
1193
  {
1135
1194
  size: 8,
1136
1195
  backgroundColor: i === current ? "$color9" : "$color4",
@@ -1156,7 +1215,7 @@ function OnboardingCarousel({
1156
1215
  }
1157
1216
 
1158
1217
  // src/patterns/ChatBubble.tsx
1159
- import { SizableText as SizableText19, XStack as XStack12, YStack as YStack16, Circle as Circle4, Image as Image3 } from "tamagui";
1218
+ import { SizableText as SizableText19, XStack as XStack12, YStack as YStack16, Circle as Circle3, Image as Image3 } from "tamagui";
1160
1219
  import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1161
1220
  function ChatBubble({ message, showAvatar = true }) {
1162
1221
  const isUser = message.sender === "user";
@@ -1168,7 +1227,7 @@ function ChatBubble({ message, showAvatar = true }) {
1168
1227
  gap: "$2",
1169
1228
  flexDirection: isUser ? "row-reverse" : "row",
1170
1229
  children: [
1171
- showAvatar && !isUser && /* @__PURE__ */ jsx22(Circle4, { size: 32, backgroundColor: "$color4", overflow: "hidden", children: message.avatar ? /* @__PURE__ */ jsx22(Image3, { source: { uri: message.avatar }, width: 32, height: 32, objectFit: "cover" }) : /* @__PURE__ */ jsx22(SizableText19, { size: "$2", fontWeight: "600", color: "$color11", children: message.senderName?.[0]?.toUpperCase() ?? "?" }) }),
1230
+ showAvatar && !isUser && /* @__PURE__ */ jsx22(Circle3, { size: 32, backgroundColor: "$color4", overflow: "hidden", children: message.avatar ? /* @__PURE__ */ jsx22(Image3, { source: { uri: message.avatar }, width: 32, height: 32, objectFit: "cover" }) : /* @__PURE__ */ jsx22(SizableText19, { size: "$2", fontWeight: "600", color: "$color11", children: message.senderName?.[0]?.toUpperCase() ?? "?" }) }),
1172
1231
  /* @__PURE__ */ jsxs15(
1173
1232
  YStack16,
1174
1233
  {
@@ -1267,11 +1326,11 @@ function EmptyState({ icon, title, description, actionLabel, onAction }) {
1267
1326
  }
1268
1327
 
1269
1328
  // src/patterns/ProfileHeader.tsx
1270
- import { Circle as Circle5, Image as Image4, SizableText as SizableText22, XStack as XStack14, YStack as YStack19 } from "tamagui";
1329
+ import { Circle as Circle4, Image as Image4, SizableText as SizableText22, XStack as XStack14, YStack as YStack19 } from "tamagui";
1271
1330
  import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1272
1331
  function ProfileHeader({ name, subtitle, avatar, stats, actions }) {
1273
1332
  return /* @__PURE__ */ jsxs18(YStack19, { alignItems: "center", gap: "$4", paddingVertical: "$6", paddingHorizontal: "$4", children: [
1274
- /* @__PURE__ */ jsx25(Circle5, { size: 80, backgroundColor: "$color4", overflow: "hidden", children: avatar ? /* @__PURE__ */ jsx25(Image4, { source: { uri: avatar }, width: 80, height: 80, objectFit: "cover" }) : /* @__PURE__ */ jsx25(SizableText22, { size: "$9", fontWeight: "700", color: "$color11", children: name[0]?.toUpperCase() ?? "?" }) }),
1333
+ /* @__PURE__ */ jsx25(Circle4, { size: 80, backgroundColor: "$color4", overflow: "hidden", children: avatar ? /* @__PURE__ */ jsx25(Image4, { source: { uri: avatar }, width: 80, height: 80, objectFit: "cover" }) : /* @__PURE__ */ jsx25(SizableText22, { size: "$9", fontWeight: "700", color: "$color11", children: name[0]?.toUpperCase() ?? "?" }) }),
1275
1334
  /* @__PURE__ */ jsxs18(YStack19, { alignItems: "center", gap: "$1", children: [
1276
1335
  /* @__PURE__ */ jsx25(SizableText22, { size: "$7", fontWeight: "700", children: name }),
1277
1336
  subtitle && /* @__PURE__ */ jsx25(SizableText22, { size: "$4", color: "$color10", children: subtitle })
@@ -1316,7 +1375,7 @@ function AppHeader({ title, subtitle, variant = "simple", onBack, avatar, left,
1316
1375
 
1317
1376
  // src/patterns/BottomSheet.tsx
1318
1377
  import { Sheet, SizableText as SizableText24, XStack as XStack16, YStack as YStack21 } from "tamagui";
1319
- import { ScrollView as ScrollView2 } from "react-native";
1378
+ import { ScrollView as ScrollView3 } from "react-native";
1320
1379
  import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
1321
1380
  function BottomSheet({ open, onOpenChange, title, children, snapPoints = [85], dismissOnSnapToBottom = true, showHandle = true, showClose = false, zIndex = 1e5 }) {
1322
1381
  return /* @__PURE__ */ jsxs20(
@@ -1351,7 +1410,7 @@ function BottomSheet({ open, onOpenChange, title, children, snapPoints = [85], d
1351
1410
  }
1352
1411
  )
1353
1412
  ] }),
1354
- /* @__PURE__ */ jsx27(ScrollView2, { contentContainerStyle: { paddingBottom: 40 }, children: /* @__PURE__ */ jsx27(YStack21, { padding: "$4", children }) })
1413
+ /* @__PURE__ */ jsx27(ScrollView3, { contentContainerStyle: { paddingBottom: 40 }, children: /* @__PURE__ */ jsx27(YStack21, { padding: "$4", children }) })
1355
1414
  ] })
1356
1415
  ]
1357
1416
  }
@@ -1359,12 +1418,12 @@ function BottomSheet({ open, onOpenChange, title, children, snapPoints = [85], d
1359
1418
  }
1360
1419
 
1361
1420
  // src/patterns/LoginScreen.tsx
1362
- import { useState as useState6 } from "react";
1421
+ import { useState as useState7 } from "react";
1363
1422
  import { Button as Button6, SizableText as SizableText25, Spinner, XStack as XStack17, YStack as YStack22 } from "tamagui";
1364
1423
  import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
1365
1424
  function LoginScreen({ title = "Welcome", subtitle = "Sign in to continue", logo, providers = [], onProviderPress, showEmailForm, onEmailSubmit, onForgotPassword, onCreateAccount, onTerms, onPrivacy, loading }) {
1366
- const [email, setEmail] = useState6("");
1367
- const [password, setPassword] = useState6("");
1425
+ const [email, setEmail] = useState7("");
1426
+ const [password, setPassword] = useState7("");
1368
1427
  return /* @__PURE__ */ jsxs21(YStack22, { flex: 1, padding: "$4", gap: "$5", backgroundColor: "$background", justifyContent: "center", children: [
1369
1428
  /* @__PURE__ */ jsxs21(YStack22, { alignItems: "center", gap: "$2", children: [
1370
1429
  logo && /* @__PURE__ */ jsx28(YStack22, { paddingBottom: "$3", children: logo }),
@@ -1620,7 +1679,7 @@ function NotificationBanner({ title, message, variant = "info", onPress, onDismi
1620
1679
  }
1621
1680
 
1622
1681
  // src/patterns/ProgressSteps.tsx
1623
- import { Circle as Circle6, SizableText as SizableText31, XStack as XStack23, YStack as YStack27 } from "tamagui";
1682
+ import { Circle as Circle5, SizableText as SizableText31, XStack as XStack23, YStack as YStack27 } from "tamagui";
1624
1683
  import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
1625
1684
  function ProgressSteps({ steps, currentStep, variant = "dots" }) {
1626
1685
  if (variant === "bar") {
@@ -1632,7 +1691,7 @@ function ProgressSteps({ steps, currentStep, variant = "dots" }) {
1632
1691
  }
1633
1692
  return /* @__PURE__ */ jsx35(XStack23, { alignItems: "center", justifyContent: "center", gap: "$0", children: steps.map((label, i) => /* @__PURE__ */ jsxs27(XStack23, { alignItems: "center", gap: "$0", children: [
1634
1693
  /* @__PURE__ */ jsxs27(YStack27, { alignItems: "center", gap: "$1.5", children: [
1635
- /* @__PURE__ */ jsx35(Circle6, { size: variant === "numbered" ? 28 : 10, backgroundColor: i <= currentStep ? "$color9" : "$color4", animation: "quick", children: variant === "numbered" && /* @__PURE__ */ jsx35(SizableText31, { size: "$2", fontWeight: "600", color: i <= currentStep ? "$color1" : "$color8", children: i + 1 }) }),
1694
+ /* @__PURE__ */ jsx35(Circle5, { size: variant === "numbered" ? 28 : 10, backgroundColor: i <= currentStep ? "$color9" : "$color4", animation: "quick", children: variant === "numbered" && /* @__PURE__ */ jsx35(SizableText31, { size: "$2", fontWeight: "600", color: i <= currentStep ? "$color1" : "$color8", children: i + 1 }) }),
1636
1695
  /* @__PURE__ */ jsx35(SizableText31, { size: "$1", color: i <= currentStep ? "$color11" : "$color8", numberOfLines: 1, children: label })
1637
1696
  ] }),
1638
1697
  i < steps.length - 1 && /* @__PURE__ */ jsx35(YStack27, { height: 2, width: 32, backgroundColor: i < currentStep ? "$color9" : "$color4", marginBottom: "$4" })
@@ -1640,11 +1699,11 @@ function ProgressSteps({ steps, currentStep, variant = "dots" }) {
1640
1699
  }
1641
1700
 
1642
1701
  // src/patterns/SwipeableRow.tsx
1643
- import { useState as useState7 } from "react";
1702
+ import { useState as useState8 } from "react";
1644
1703
  import { Button as Button7, SizableText as SizableText32, XStack as XStack24, YStack as YStack28 } from "tamagui";
1645
1704
  import { Fragment as Fragment2, jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
1646
1705
  function SwipeableRow({ children, leftActions, rightActions }) {
1647
- const [showActions, setShowActions] = useState7(false);
1706
+ const [showActions, setShowActions] = useState8(false);
1648
1707
  const actions = [...leftActions ?? [], ...rightActions ?? []];
1649
1708
  if (actions.length === 0) return /* @__PURE__ */ jsx36(Fragment2, { children });
1650
1709
  return /* @__PURE__ */ jsxs28(YStack28, { children: [
@@ -1719,17 +1778,17 @@ function MediaCard({ image, title, subtitle, overlay = "gradient", aspectRatio =
1719
1778
  }
1720
1779
 
1721
1780
  // src/patterns/Carousel.tsx
1722
- import { Children as Children2, useState as useState8 } from "react";
1723
- import { Circle as Circle7, XStack as XStack26, YStack as YStack30 } from "tamagui";
1724
- import { ScrollView as ScrollView3 } from "react-native";
1781
+ import { Children as Children2, useState as useState9 } from "react";
1782
+ import { Circle as Circle6, XStack as XStack26, YStack as YStack30 } from "tamagui";
1783
+ import { ScrollView as ScrollView4 } from "react-native";
1725
1784
  import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
1726
1785
  function Carousel({ children, gap = "$3", snapToInterval, showIndicators = false }) {
1727
- const [activeIndex, setActiveIndex] = useState8(0);
1786
+ const [activeIndex, setActiveIndex] = useState9(0);
1728
1787
  const count = Children2.count(children);
1729
1788
  const gapPx = gap === "$2" ? 8 : gap === "$3" ? 12 : 16;
1730
1789
  return /* @__PURE__ */ jsxs30(YStack30, { gap: "$3", children: [
1731
1790
  /* @__PURE__ */ jsx38(
1732
- ScrollView3,
1791
+ ScrollView4,
1733
1792
  {
1734
1793
  horizontal: true,
1735
1794
  showsHorizontalScrollIndicator: false,
@@ -1742,17 +1801,17 @@ function Carousel({ children, gap = "$3", snapToInterval, showIndicators = false
1742
1801
  children
1743
1802
  }
1744
1803
  ),
1745
- showIndicators && count > 1 && /* @__PURE__ */ jsx38(XStack26, { justifyContent: "center", gap: "$1.5", children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx38(Circle7, { size: 6, backgroundColor: i === activeIndex ? "$color9" : "$color4", animation: "quick" }, i)) })
1804
+ showIndicators && count > 1 && /* @__PURE__ */ jsx38(XStack26, { justifyContent: "center", gap: "$1.5", children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx38(Circle6, { size: 6, backgroundColor: i === activeIndex ? "$color9" : "$color4", animation: "quick" }, i)) })
1746
1805
  ] });
1747
1806
  }
1748
1807
 
1749
1808
  // src/patterns/PullToRefresh.tsx
1750
1809
  import { YStack as YStack31 } from "tamagui";
1751
- import { RefreshControl, ScrollView as ScrollView4 } from "react-native";
1810
+ import { RefreshControl, ScrollView as ScrollView5 } from "react-native";
1752
1811
  import { jsx as jsx39 } from "react/jsx-runtime";
1753
1812
  function PullToRefresh({ children, onRefresh, refreshing = false }) {
1754
1813
  return /* @__PURE__ */ jsx39(
1755
- ScrollView4,
1814
+ ScrollView5,
1756
1815
  {
1757
1816
  contentContainerStyle: { flexGrow: 1 },
1758
1817
  refreshControl: /* @__PURE__ */ jsx39(RefreshControl, { refreshing, onRefresh }),
@@ -1760,17 +1819,849 @@ function PullToRefresh({ children, onRefresh, refreshing = false }) {
1760
1819
  }
1761
1820
  );
1762
1821
  }
1822
+
1823
+ // src/patterns/ProductCard.tsx
1824
+ import { Button as Button8, Image as Image6, SizableText as SizableText34, XStack as XStack27, YStack as YStack32 } from "tamagui";
1825
+ import { jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
1826
+ function Stars({ rating = 0 }) {
1827
+ return /* @__PURE__ */ jsx40(XStack27, { gap: "$0.5", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx40(SizableText34, { size: "$2", color: i < Math.round(rating) ? "$yellow9" : "$color5", children: "\u2605" }, i)) });
1828
+ }
1829
+ function CardContent2({ title, price, originalPrice, rating, reviewCount, onAddToCart }) {
1830
+ return /* @__PURE__ */ jsxs31(YStack32, { flex: 1, gap: "$1.5", justifyContent: "space-between", children: [
1831
+ /* @__PURE__ */ jsxs31(YStack32, { gap: "$1", children: [
1832
+ /* @__PURE__ */ jsx40(SizableText34, { size: "$4", fontWeight: "600", numberOfLines: 2, children: title }),
1833
+ rating !== void 0 && /* @__PURE__ */ jsxs31(XStack27, { gap: "$1.5", alignItems: "center", children: [
1834
+ /* @__PURE__ */ jsx40(Stars, { rating }),
1835
+ reviewCount !== void 0 && /* @__PURE__ */ jsxs31(SizableText34, { size: "$2", color: "$color9", children: [
1836
+ "(",
1837
+ reviewCount,
1838
+ ")"
1839
+ ] })
1840
+ ] })
1841
+ ] }),
1842
+ /* @__PURE__ */ jsxs31(XStack27, { alignItems: "center", justifyContent: "space-between", children: [
1843
+ /* @__PURE__ */ jsxs31(XStack27, { gap: "$2", alignItems: "baseline", children: [
1844
+ /* @__PURE__ */ jsx40(SizableText34, { size: "$6", fontWeight: "700", children: price }),
1845
+ originalPrice && /* @__PURE__ */ jsx40(SizableText34, { size: "$3", color: "$color8", textDecorationLine: "line-through", children: originalPrice })
1846
+ ] }),
1847
+ onAddToCart && /* @__PURE__ */ jsx40(
1848
+ Button8,
1849
+ {
1850
+ size: "$3",
1851
+ backgroundColor: "$color9",
1852
+ color: "$color1",
1853
+ borderRadius: "$10",
1854
+ onPress: (e) => {
1855
+ e.stopPropagation?.();
1856
+ onAddToCart();
1857
+ },
1858
+ pressStyle: { backgroundColor: "$color8", scale: 0.95 },
1859
+ animation: "quick",
1860
+ children: "+ Cart"
1861
+ }
1862
+ )
1863
+ ] })
1864
+ ] });
1865
+ }
1866
+ function ProductCard({ image, title, price, originalPrice, rating, reviewCount, badge, onPress, onAddToCart, variant = "vertical" }) {
1867
+ const isHorizontal = variant === "horizontal";
1868
+ const Wrapper = isHorizontal ? XStack27 : YStack32;
1869
+ return /* @__PURE__ */ jsxs31(
1870
+ Wrapper,
1871
+ {
1872
+ backgroundColor: "$color1",
1873
+ borderRadius: "$5",
1874
+ overflow: "hidden",
1875
+ borderWidth: 1,
1876
+ borderColor: "$color4",
1877
+ onPress,
1878
+ animation: "quick",
1879
+ pressStyle: onPress ? { scale: 0.98, opacity: 0.9 } : void 0,
1880
+ ...isHorizontal ? { height: 140 } : {},
1881
+ children: [
1882
+ /* @__PURE__ */ jsxs31(YStack32, { ...isHorizontal ? { width: 140 } : { aspectRatio: 4 / 3 }, position: "relative", children: [
1883
+ /* @__PURE__ */ jsx40(Image6, { source: { uri: image }, width: "100%", height: "100%", objectFit: "cover" }),
1884
+ badge && /* @__PURE__ */ jsx40(
1885
+ XStack27,
1886
+ {
1887
+ position: "absolute",
1888
+ top: "$2",
1889
+ left: "$2",
1890
+ backgroundColor: "$red9",
1891
+ paddingHorizontal: "$2",
1892
+ paddingVertical: "$1",
1893
+ borderRadius: "$10",
1894
+ children: /* @__PURE__ */ jsx40(SizableText34, { size: "$1", fontWeight: "700", color: "white", children: badge })
1895
+ }
1896
+ )
1897
+ ] }),
1898
+ /* @__PURE__ */ jsx40(YStack32, { flex: 1, padding: "$3", children: /* @__PURE__ */ jsx40(CardContent2, { ...{ title, price, originalPrice, rating, reviewCount, onAddToCart } }) })
1899
+ ]
1900
+ }
1901
+ );
1902
+ }
1903
+
1904
+ // src/patterns/PricingTable.tsx
1905
+ import { Button as Button9, ScrollView as ScrollView6, SizableText as SizableText35, XStack as XStack28, YStack as YStack33 } from "tamagui";
1906
+ import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
1907
+ function BillingToggle({ annual, onToggle }) {
1908
+ return /* @__PURE__ */ jsx41(XStack28, { alignSelf: "center", backgroundColor: "$color3", borderRadius: "$10", padding: "$1", gap: "$0.5", children: ["Monthly", "Annual"].map((label, i) => {
1909
+ const active = i === 1 ? annual : !annual;
1910
+ return /* @__PURE__ */ jsx41(
1911
+ XStack28,
1912
+ {
1913
+ paddingHorizontal: "$4",
1914
+ paddingVertical: "$2",
1915
+ borderRadius: "$10",
1916
+ backgroundColor: active ? "$color9" : "transparent",
1917
+ onPress: () => onToggle(i === 1),
1918
+ pressStyle: { opacity: 0.8 },
1919
+ animation: "quick",
1920
+ children: /* @__PURE__ */ jsx41(SizableText35, { size: "$3", fontWeight: "600", color: active ? "$color1" : "$color10", children: label })
1921
+ },
1922
+ label
1923
+ );
1924
+ }) });
1925
+ }
1926
+ function PlanCard2({ plan, selected, onSelect }) {
1927
+ return /* @__PURE__ */ jsxs32(
1928
+ YStack33,
1929
+ {
1930
+ flex: 1,
1931
+ minWidth: 260,
1932
+ padding: "$4",
1933
+ borderRadius: "$5",
1934
+ gap: "$3",
1935
+ borderWidth: 2,
1936
+ borderColor: selected ? "$color9" : plan.popular ? "$color9" : "$color4",
1937
+ backgroundColor: plan.popular ? "$color2" : "$color1",
1938
+ position: "relative",
1939
+ pressStyle: { scale: 0.98 },
1940
+ animation: "quick",
1941
+ onPress: onSelect,
1942
+ children: [
1943
+ plan.popular && /* @__PURE__ */ jsx41(
1944
+ XStack28,
1945
+ {
1946
+ position: "absolute",
1947
+ top: -12,
1948
+ alignSelf: "center",
1949
+ backgroundColor: "$color9",
1950
+ paddingHorizontal: "$3",
1951
+ paddingVertical: "$1",
1952
+ borderRadius: "$10",
1953
+ children: /* @__PURE__ */ jsx41(SizableText35, { size: "$1", fontWeight: "700", color: "$color1", children: "POPULAR" })
1954
+ }
1955
+ ),
1956
+ /* @__PURE__ */ jsxs32(YStack33, { gap: "$1", alignItems: "center", paddingTop: plan.popular ? "$2" : 0, children: [
1957
+ /* @__PURE__ */ jsx41(SizableText35, { size: "$4", fontWeight: "600", color: "$color11", children: plan.name }),
1958
+ plan.description && /* @__PURE__ */ jsx41(SizableText35, { size: "$2", color: "$color9", textAlign: "center", children: plan.description }),
1959
+ /* @__PURE__ */ jsxs32(XStack28, { alignItems: "baseline", gap: "$1", children: [
1960
+ /* @__PURE__ */ jsx41(SizableText35, { size: "$9", fontWeight: "800", children: plan.price }),
1961
+ plan.period && /* @__PURE__ */ jsxs32(SizableText35, { size: "$3", color: "$color9", children: [
1962
+ "/",
1963
+ plan.period
1964
+ ] })
1965
+ ] })
1966
+ ] }),
1967
+ /* @__PURE__ */ jsx41(YStack33, { gap: "$2", flex: 1, children: plan.features.map((f, i) => /* @__PURE__ */ jsxs32(XStack28, { gap: "$2", alignItems: "center", children: [
1968
+ /* @__PURE__ */ jsx41(SizableText35, { size: "$3", color: f.included ? "$green9" : "$color6", children: f.included ? "\u2713" : "\u2717" }),
1969
+ /* @__PURE__ */ jsx41(SizableText35, { size: "$3", color: f.included ? "$color11" : "$color8", flex: 1, children: f.label })
1970
+ ] }, i)) }),
1971
+ /* @__PURE__ */ jsx41(
1972
+ Button9,
1973
+ {
1974
+ size: "$4",
1975
+ borderRadius: "$10",
1976
+ fontWeight: "700",
1977
+ animation: "quick",
1978
+ backgroundColor: selected || plan.popular ? "$color9" : "transparent",
1979
+ color: selected || plan.popular ? "$color1" : "$color11",
1980
+ borderWidth: selected || plan.popular ? 0 : 1,
1981
+ borderColor: "$color7",
1982
+ onPress: onSelect,
1983
+ pressStyle: { scale: 0.97, opacity: 0.9 },
1984
+ children: plan.cta ?? "Get Started"
1985
+ }
1986
+ )
1987
+ ]
1988
+ }
1989
+ );
1990
+ }
1991
+ function PricingTable({ plans, selectedPlan, onSelectPlan, annual = false, onToggleBilling }) {
1992
+ const selected = selectedPlan ?? plans.find((p) => p.popular)?.id;
1993
+ return /* @__PURE__ */ jsxs32(YStack33, { gap: "$4", children: [
1994
+ onToggleBilling && /* @__PURE__ */ jsx41(BillingToggle, { annual, onToggle: onToggleBilling }),
1995
+ /* @__PURE__ */ jsx41(
1996
+ ScrollView6,
1997
+ {
1998
+ horizontal: true,
1999
+ showsHorizontalScrollIndicator: false,
2000
+ contentContainerStyle: { gap: 12, paddingHorizontal: 4 },
2001
+ children: plans.map((plan) => /* @__PURE__ */ jsx41(
2002
+ PlanCard2,
2003
+ {
2004
+ plan,
2005
+ selected: selected === plan.id,
2006
+ onSelect: () => onSelectPlan?.(plan.id)
2007
+ },
2008
+ plan.id
2009
+ ))
2010
+ }
2011
+ )
2012
+ ] });
2013
+ }
2014
+
2015
+ // src/patterns/CountdownBanner.tsx
2016
+ import { useState as useState10, useEffect as useEffect4, useRef, useCallback as useCallback2 } from "react";
2017
+ import { SizableText as SizableText36, XStack as XStack29, YStack as YStack34 } from "tamagui";
2018
+ import { jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
2019
+ function useCountdown2(endTime, minutes, onExpire) {
2020
+ const getRemaining = useCallback2(() => {
2021
+ if (endTime) return Math.max(0, Math.floor((endTime.getTime() - Date.now()) / 1e3));
2022
+ return 0;
2023
+ }, [endTime]);
2024
+ const [seconds, setSeconds] = useState10(() => endTime ? getRemaining() : (minutes ?? 0) * 60);
2025
+ const firedRef = useRef(false);
2026
+ useEffect4(() => {
2027
+ if (endTime) setSeconds(getRemaining());
2028
+ else setSeconds((minutes ?? 0) * 60);
2029
+ firedRef.current = false;
2030
+ }, [endTime, minutes, getRemaining]);
2031
+ useEffect4(() => {
2032
+ if (seconds <= 0) return;
2033
+ const id = setInterval(() => {
2034
+ setSeconds((s) => {
2035
+ const next = endTime ? Math.max(0, Math.floor((endTime.getTime() - Date.now()) / 1e3)) : s - 1;
2036
+ if (next <= 0 && !firedRef.current) {
2037
+ firedRef.current = true;
2038
+ onExpire?.();
2039
+ }
2040
+ return Math.max(0, next);
2041
+ });
2042
+ }, 1e3);
2043
+ return () => clearInterval(id);
2044
+ }, [seconds > 0, endTime, onExpire]);
2045
+ const hh = String(Math.floor(seconds / 3600)).padStart(2, "0");
2046
+ const mm = String(Math.floor(seconds % 3600 / 60)).padStart(2, "0");
2047
+ const ss = String(seconds % 60).padStart(2, "0");
2048
+ const display = seconds >= 3600 ? `${hh}:${mm}:${ss}` : `${mm}:${ss}`;
2049
+ return { display, expired: seconds <= 0 };
2050
+ }
2051
+ function TimeBox({ value }) {
2052
+ return /* @__PURE__ */ jsx42(XStack29, { backgroundColor: "rgba(0,0,0,0.15)", paddingHorizontal: "$2", paddingVertical: "$1", borderRadius: "$3", children: /* @__PURE__ */ jsx42(SizableText36, { size: "$6", fontWeight: "800", color: "white", fontFamily: "$mono", children: value }) });
2053
+ }
2054
+ function CountdownBanner({ endTime, minutes, label = "Offer ends in", onExpire, variant = "banner" }) {
2055
+ const { display, expired } = useCountdown2(endTime, minutes, onExpire);
2056
+ if (expired) return null;
2057
+ const parts = display.split(":");
2058
+ if (variant === "badge") {
2059
+ return /* @__PURE__ */ jsxs33(XStack29, { backgroundColor: "$red9", paddingHorizontal: "$2.5", paddingVertical: "$1", borderRadius: "$10", gap: "$1.5", alignItems: "center", children: [
2060
+ /* @__PURE__ */ jsx42(SizableText36, { size: "$1", fontWeight: "600", color: "white", children: label }),
2061
+ /* @__PURE__ */ jsx42(SizableText36, { size: "$2", fontWeight: "800", color: "white", fontFamily: "$mono", children: display })
2062
+ ] });
2063
+ }
2064
+ if (variant === "compact") {
2065
+ return /* @__PURE__ */ jsxs33(XStack29, { backgroundColor: "$red3", paddingHorizontal: "$3", paddingVertical: "$2", borderRadius: "$4", gap: "$2", alignItems: "center", alignSelf: "center", children: [
2066
+ /* @__PURE__ */ jsx42(SizableText36, { size: "$3", fontWeight: "600", color: "$red9", children: label }),
2067
+ /* @__PURE__ */ jsx42(SizableText36, { size: "$5", fontWeight: "800", color: "$red9", fontFamily: "$mono", children: display })
2068
+ ] });
2069
+ }
2070
+ return /* @__PURE__ */ jsxs33(YStack34, { backgroundColor: "$red9", paddingVertical: "$3", paddingHorizontal: "$4", gap: "$1.5", alignItems: "center", children: [
2071
+ /* @__PURE__ */ jsx42(SizableText36, { size: "$3", fontWeight: "600", color: "white", opacity: 0.9, children: label }),
2072
+ /* @__PURE__ */ jsx42(XStack29, { gap: "$1.5", alignItems: "center", children: parts.map((p, i) => /* @__PURE__ */ jsxs33(XStack29, { gap: "$1.5", alignItems: "center", children: [
2073
+ i > 0 && /* @__PURE__ */ jsx42(SizableText36, { size: "$6", fontWeight: "800", color: "white", children: ":" }),
2074
+ /* @__PURE__ */ jsx42(TimeBox, { value: p })
2075
+ ] }, i)) })
2076
+ ] });
2077
+ }
2078
+
2079
+ // src/patterns/TestimonialCard.tsx
2080
+ import { Image as Image7, SizableText as SizableText37, XStack as XStack30, YStack as YStack35 } from "tamagui";
2081
+ import { jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
2082
+ function Stars2({ count = 0 }) {
2083
+ if (!count) return null;
2084
+ return /* @__PURE__ */ jsx43(XStack30, { gap: "$0.5", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx43(SizableText37, { size: "$3", color: i < Math.round(count) ? "$yellow9" : "$color5", children: "\u2605" }, i)) });
2085
+ }
2086
+ function AuthorRow({ author, role, avatar }) {
2087
+ return /* @__PURE__ */ jsxs34(XStack30, { gap: "$2.5", alignItems: "center", children: [
2088
+ avatar && /* @__PURE__ */ jsx43(Image7, { source: { uri: avatar }, width: 36, height: 36, borderRadius: 18, objectFit: "cover" }),
2089
+ /* @__PURE__ */ jsxs34(YStack35, { children: [
2090
+ /* @__PURE__ */ jsx43(SizableText37, { size: "$3", fontWeight: "600", children: author }),
2091
+ role && /* @__PURE__ */ jsx43(SizableText37, { size: "$2", color: "$color9", children: role })
2092
+ ] })
2093
+ ] });
2094
+ }
2095
+ function TestimonialCard({ quote, author, role, avatar, rating, variant = "card" }) {
2096
+ if (variant === "minimal") {
2097
+ return /* @__PURE__ */ jsxs34(YStack35, { gap: "$3", paddingVertical: "$2", children: [
2098
+ /* @__PURE__ */ jsx43(Stars2, { count: rating }),
2099
+ /* @__PURE__ */ jsxs34(SizableText37, { size: "$4", color: "$color11", fontStyle: "italic", lineHeight: 24, children: [
2100
+ '"',
2101
+ quote,
2102
+ '"'
2103
+ ] }),
2104
+ /* @__PURE__ */ jsx43(AuthorRow, { author, role, avatar })
2105
+ ] });
2106
+ }
2107
+ if (variant === "featured") {
2108
+ return /* @__PURE__ */ jsxs34(YStack35, { backgroundColor: "$color3", padding: "$5", borderRadius: "$6", gap: "$4", alignItems: "center", children: [
2109
+ /* @__PURE__ */ jsx43(SizableText37, { size: "$8", color: "$color9", opacity: 0.3, fontWeight: "800", children: '"' }),
2110
+ /* @__PURE__ */ jsx43(Stars2, { count: rating }),
2111
+ /* @__PURE__ */ jsxs34(SizableText37, { size: "$5", color: "$color12", fontStyle: "italic", textAlign: "center", lineHeight: 28, children: [
2112
+ '"',
2113
+ quote,
2114
+ '"'
2115
+ ] }),
2116
+ /* @__PURE__ */ jsx43(AuthorRow, { author, role, avatar })
2117
+ ] });
2118
+ }
2119
+ return /* @__PURE__ */ jsxs34(
2120
+ YStack35,
2121
+ {
2122
+ backgroundColor: "$color1",
2123
+ padding: "$4",
2124
+ borderRadius: "$5",
2125
+ borderWidth: 1,
2126
+ borderColor: "$color4",
2127
+ gap: "$3",
2128
+ children: [
2129
+ /* @__PURE__ */ jsx43(Stars2, { count: rating }),
2130
+ /* @__PURE__ */ jsxs34(SizableText37, { size: "$4", color: "$color11", fontStyle: "italic", lineHeight: 24, children: [
2131
+ '"',
2132
+ quote,
2133
+ '"'
2134
+ ] }),
2135
+ /* @__PURE__ */ jsx43(AuthorRow, { author, role, avatar })
2136
+ ]
2137
+ }
2138
+ );
2139
+ }
2140
+
2141
+ // src/patterns/ConfirmDialog.tsx
2142
+ import { AlertDialog as AlertDialog2, Button as Button10, SizableText as SizableText38, XStack as XStack31, YStack as YStack36 } from "tamagui";
2143
+ import { jsx as jsx44, jsxs as jsxs35 } from "react/jsx-runtime";
2144
+ function ConfirmDialog({
2145
+ open,
2146
+ onOpenChange,
2147
+ title,
2148
+ description,
2149
+ confirmLabel = "Confirm",
2150
+ cancelLabel = "Cancel",
2151
+ onConfirm,
2152
+ onCancel,
2153
+ destructive = false,
2154
+ icon
2155
+ }) {
2156
+ const handleCancel = () => {
2157
+ onCancel?.();
2158
+ onOpenChange(false);
2159
+ };
2160
+ const handleConfirm = () => {
2161
+ onConfirm?.();
2162
+ onOpenChange(false);
2163
+ };
2164
+ return /* @__PURE__ */ jsx44(AlertDialog2, { open, onOpenChange, children: /* @__PURE__ */ jsxs35(AlertDialog2.Portal, { children: [
2165
+ /* @__PURE__ */ jsx44(
2166
+ AlertDialog2.Overlay,
2167
+ {
2168
+ opacity: 0.5,
2169
+ enterStyle: { opacity: 0 },
2170
+ exitStyle: { opacity: 0 },
2171
+ animation: "quick"
2172
+ },
2173
+ "overlay"
2174
+ ),
2175
+ /* @__PURE__ */ jsx44(
2176
+ AlertDialog2.Content,
2177
+ {
2178
+ bordered: true,
2179
+ elevate: true,
2180
+ width: "90%",
2181
+ maxWidth: 400,
2182
+ enterStyle: { y: -20, opacity: 0, scale: 0.9 },
2183
+ exitStyle: { y: 10, opacity: 0, scale: 0.95 },
2184
+ x: 0,
2185
+ y: 0,
2186
+ scale: 1,
2187
+ opacity: 1,
2188
+ animation: "quick",
2189
+ children: /* @__PURE__ */ jsxs35(YStack36, { gap: "$4", padding: "$4", children: [
2190
+ icon && /* @__PURE__ */ jsx44(YStack36, { alignItems: "center", children: icon }),
2191
+ /* @__PURE__ */ jsxs35(YStack36, { gap: "$2", alignItems: icon ? "center" : "flex-start", children: [
2192
+ /* @__PURE__ */ jsx44(AlertDialog2.Title, { size: "$6", fontWeight: "700", children: title }),
2193
+ description && /* @__PURE__ */ jsx44(
2194
+ AlertDialog2.Description,
2195
+ {
2196
+ size: "$3",
2197
+ color: "$color10",
2198
+ textAlign: icon ? "center" : "left",
2199
+ children: description
2200
+ }
2201
+ )
2202
+ ] }),
2203
+ /* @__PURE__ */ jsxs35(XStack31, { gap: "$3", justifyContent: "flex-end", children: [
2204
+ /* @__PURE__ */ jsx44(
2205
+ Button10,
2206
+ {
2207
+ flex: 1,
2208
+ size: "$4",
2209
+ borderRadius: "$4",
2210
+ variant: "outlined",
2211
+ borderColor: "$color7",
2212
+ onPress: handleCancel,
2213
+ pressStyle: { opacity: 0.7 },
2214
+ animation: "quick",
2215
+ children: /* @__PURE__ */ jsx44(SizableText38, { fontWeight: "600", children: cancelLabel })
2216
+ }
2217
+ ),
2218
+ /* @__PURE__ */ jsx44(
2219
+ Button10,
2220
+ {
2221
+ flex: 1,
2222
+ size: "$4",
2223
+ borderRadius: "$4",
2224
+ backgroundColor: destructive ? "$red9" : "$color9",
2225
+ onPress: handleConfirm,
2226
+ pressStyle: { backgroundColor: destructive ? "$red8" : "$color8", scale: 0.97 },
2227
+ animation: "quick",
2228
+ children: /* @__PURE__ */ jsx44(SizableText38, { fontWeight: "600", color: "white", children: confirmLabel })
2229
+ }
2230
+ )
2231
+ ] })
2232
+ ] })
2233
+ },
2234
+ "content"
2235
+ )
2236
+ ] }) });
2237
+ }
2238
+
2239
+ // src/patterns/Chip.tsx
2240
+ import { useCallback as useCallback3 } from "react";
2241
+ import { SizableText as SizableText39, XStack as XStack32 } from "tamagui";
2242
+ import { jsx as jsx45, jsxs as jsxs36 } from "react/jsx-runtime";
2243
+ var sizes2 = { sm: { h: 28, px: "$2", text: "$2" }, md: { h: 34, px: "$3", text: "$3" }, lg: { h: 42, px: "$4", text: "$4" } };
2244
+ function Chip({ label, selected, onPress, onRemove, variant = "filled", size = "md", icon, color }) {
2245
+ const s = sizes2[size];
2246
+ const filled = variant === "filled";
2247
+ const active = selected ?? false;
2248
+ const bg = active ? color ?? "$color9" : filled ? "$color3" : "transparent";
2249
+ const border = active ? color ?? "$color9" : "$color6";
2250
+ const fg = active ? "$color1" : "$color11";
2251
+ return /* @__PURE__ */ jsxs36(
2252
+ XStack32,
2253
+ {
2254
+ height: s.h,
2255
+ borderRadius: "$10",
2256
+ paddingHorizontal: s.px,
2257
+ backgroundColor: bg,
2258
+ borderWidth: filled ? 0 : 1,
2259
+ borderColor: border,
2260
+ alignItems: "center",
2261
+ gap: "$1.5",
2262
+ pressStyle: { scale: 0.96, opacity: 0.85 },
2263
+ animation: "quick",
2264
+ onPress,
2265
+ cursor: "pointer",
2266
+ children: [
2267
+ active && /* @__PURE__ */ jsx45(SizableText39, { size: s.text, color: fg, children: "\u2713" }),
2268
+ icon && /* @__PURE__ */ jsx45(SizableText39, { color: fg, children: icon }),
2269
+ /* @__PURE__ */ jsx45(SizableText39, { size: s.text, color: fg, fontWeight: "500", children: label }),
2270
+ onRemove && /* @__PURE__ */ jsx45(
2271
+ SizableText39,
2272
+ {
2273
+ size: "$2",
2274
+ color: fg,
2275
+ opacity: 0.7,
2276
+ pressStyle: { opacity: 1 },
2277
+ onPress: (e) => {
2278
+ e.stopPropagation?.();
2279
+ onRemove();
2280
+ },
2281
+ marginLeft: "$1",
2282
+ children: "\u2715"
2283
+ }
2284
+ )
2285
+ ]
2286
+ }
2287
+ );
2288
+ }
2289
+ function ChipGroup({ chips, selected = [], onSelectionChange, multiSelect = true, variant, size }) {
2290
+ const toggle = useCallback3((id) => {
2291
+ if (!onSelectionChange) return;
2292
+ const isSelected = selected.includes(id);
2293
+ if (multiSelect) {
2294
+ onSelectionChange(isSelected ? selected.filter((s) => s !== id) : [...selected, id]);
2295
+ } else {
2296
+ onSelectionChange(isSelected ? [] : [id]);
2297
+ }
2298
+ }, [selected, onSelectionChange, multiSelect]);
2299
+ return /* @__PURE__ */ jsx45(XStack32, { flexWrap: "wrap", gap: "$2", children: chips.map((chip) => /* @__PURE__ */ jsx45(
2300
+ Chip,
2301
+ {
2302
+ label: chip.label,
2303
+ icon: chip.icon,
2304
+ selected: selected.includes(chip.id),
2305
+ onPress: () => toggle(chip.id),
2306
+ variant,
2307
+ size
2308
+ },
2309
+ chip.id
2310
+ )) });
2311
+ }
2312
+
2313
+ // src/patterns/OTPInput.tsx
2314
+ import { useCallback as useCallback4, useRef as useRef2 } from "react";
2315
+ import { Input as Input3, SizableText as SizableText40, XStack as XStack33, YStack as YStack37 } from "tamagui";
2316
+ import { jsx as jsx46, jsxs as jsxs37 } from "react/jsx-runtime";
2317
+ function OTPInput({ length = 6, value = "", onChange, onComplete, error, autoFocus, secureEntry }) {
2318
+ const inputRef = useRef2(null);
2319
+ const digits = value.padEnd(length, " ").slice(0, length);
2320
+ const handleChange = useCallback4((text) => {
2321
+ const cleaned = text.replace(/\D/g, "").slice(0, length);
2322
+ onChange?.(cleaned);
2323
+ if (cleaned.length === length) onComplete?.(cleaned);
2324
+ }, [length, onChange, onComplete]);
2325
+ return /* @__PURE__ */ jsxs37(YStack37, { children: [
2326
+ /* @__PURE__ */ jsx46(XStack33, { gap: "$2", justifyContent: "center", onPress: () => inputRef.current?.focus(), children: Array.from({ length }, (_, i) => {
2327
+ const char = digits[i]?.trim();
2328
+ const focused = value.length === i;
2329
+ return /* @__PURE__ */ jsxs37(
2330
+ YStack37,
2331
+ {
2332
+ width: 48,
2333
+ height: 56,
2334
+ borderRadius: "$3",
2335
+ borderWidth: 2,
2336
+ borderColor: error ? "$red9" : focused ? "$color9" : "$color6",
2337
+ backgroundColor: error ? "$red2" : "$color2",
2338
+ alignItems: "center",
2339
+ justifyContent: "center",
2340
+ animation: "quick",
2341
+ children: [
2342
+ /* @__PURE__ */ jsx46(SizableText40, { size: "$7", fontWeight: "600", color: "$color12", children: char ? secureEntry ? "\u25CF" : char : "" }),
2343
+ focused && /* @__PURE__ */ jsx46(YStack37, { position: "absolute", bottom: 10, width: 20, height: 2, backgroundColor: "$color9", animation: "quick" })
2344
+ ]
2345
+ },
2346
+ i
2347
+ );
2348
+ }) }),
2349
+ /* @__PURE__ */ jsx46(
2350
+ Input3,
2351
+ {
2352
+ ref: inputRef,
2353
+ value,
2354
+ onChangeText: handleChange,
2355
+ keyboardType: "number-pad",
2356
+ maxLength: length,
2357
+ autoFocus,
2358
+ position: "absolute",
2359
+ opacity: 0,
2360
+ width: 1,
2361
+ height: 1
2362
+ }
2363
+ )
2364
+ ] });
2365
+ }
2366
+
2367
+ // src/patterns/PasswordInput.tsx
2368
+ import { useState as useState11, useCallback as useCallback5 } from "react";
2369
+ import { Input as Input4, SizableText as SizableText41, XStack as XStack34, YStack as YStack38 } from "tamagui";
2370
+ import { jsx as jsx47, jsxs as jsxs38 } from "react/jsx-runtime";
2371
+ function getStrength(pw) {
2372
+ if (!pw) return { label: "", color: "$color6", width: "0%" };
2373
+ const score = [pw.length >= 8, /[A-Z]/.test(pw), /[0-9]/.test(pw), /[^A-Za-z0-9]/.test(pw)].filter(Boolean).length;
2374
+ if (score <= 1) return { label: "Weak", color: "$red9", width: "33%" };
2375
+ if (score <= 2) return { label: "Medium", color: "$yellow9", width: "66%" };
2376
+ return { label: "Strong", color: "$green9", width: "100%" };
2377
+ }
2378
+ function PasswordInput({ value = "", onChangeText, placeholder = "Password", label, error, size = "$4", strengthIndicator }) {
2379
+ const [visible, setVisible] = useState11(false);
2380
+ const toggle = useCallback5(() => setVisible((v) => !v), []);
2381
+ const strength = getStrength(value);
2382
+ return /* @__PURE__ */ jsxs38(YStack38, { gap: "$1.5", children: [
2383
+ label && /* @__PURE__ */ jsx47(SizableText41, { size: "$3", color: "$color11", fontWeight: "500", children: label }),
2384
+ /* @__PURE__ */ jsxs38(
2385
+ XStack34,
2386
+ {
2387
+ borderWidth: 1,
2388
+ borderColor: error ? "$red9" : "$color6",
2389
+ borderRadius: "$3",
2390
+ backgroundColor: "$color2",
2391
+ alignItems: "center",
2392
+ paddingRight: "$2",
2393
+ focusStyle: { borderColor: "$color9" },
2394
+ children: [
2395
+ /* @__PURE__ */ jsx47(
2396
+ Input4,
2397
+ {
2398
+ flex: 1,
2399
+ size,
2400
+ value,
2401
+ onChangeText,
2402
+ placeholder,
2403
+ placeholderTextColor: "$color8",
2404
+ secureTextEntry: !visible,
2405
+ backgroundColor: "transparent",
2406
+ borderWidth: 0
2407
+ }
2408
+ ),
2409
+ /* @__PURE__ */ jsx47(
2410
+ SizableText41,
2411
+ {
2412
+ size: "$4",
2413
+ color: "$color8",
2414
+ paddingHorizontal: "$2",
2415
+ pressStyle: { opacity: 0.6 },
2416
+ onPress: toggle,
2417
+ cursor: "pointer",
2418
+ children: visible ? "\u25C9" : "\u25CE"
2419
+ }
2420
+ )
2421
+ ]
2422
+ }
2423
+ ),
2424
+ strengthIndicator && value.length > 0 && /* @__PURE__ */ jsxs38(YStack38, { gap: "$1", children: [
2425
+ /* @__PURE__ */ jsx47(YStack38, { height: 3, backgroundColor: "$color4", borderRadius: 2, overflow: "hidden", children: /* @__PURE__ */ jsx47(YStack38, { height: 3, width: strength.width, backgroundColor: strength.color, borderRadius: 2, animation: "quick" }) }),
2426
+ /* @__PURE__ */ jsx47(SizableText41, { size: "$1", color: strength.color, children: strength.label })
2427
+ ] }),
2428
+ error && /* @__PURE__ */ jsx47(SizableText41, { size: "$2", color: "$red9", children: error })
2429
+ ] });
2430
+ }
2431
+
2432
+ // src/patterns/AvatarGroup.tsx
2433
+ import { Circle as Circle7, Image as Image8, SizableText as SizableText42, XStack as XStack35 } from "tamagui";
2434
+ import { jsx as jsx48, jsxs as jsxs39 } from "react/jsx-runtime";
2435
+ function getInitials(name) {
2436
+ if (!name) return "?";
2437
+ return name.split(" ").map((p) => p[0]).join("").toUpperCase().slice(0, 2);
2438
+ }
2439
+ function AvatarItem({ uri, name, color, size }) {
2440
+ return /* @__PURE__ */ jsx48(
2441
+ Circle7,
2442
+ {
2443
+ size,
2444
+ backgroundColor: color ?? "$color4",
2445
+ borderWidth: 2,
2446
+ borderColor: "$background",
2447
+ overflow: "hidden",
2448
+ alignItems: "center",
2449
+ justifyContent: "center",
2450
+ children: uri ? /* @__PURE__ */ jsx48(Image8, { source: { uri }, width: size, height: size, objectFit: "cover" }) : /* @__PURE__ */ jsx48(SizableText42, { size: "$2", fontWeight: "600", color: color ? "$color1" : "$color11", children: getInitials(name) })
2451
+ }
2452
+ );
2453
+ }
2454
+ function AvatarGroup({ avatars, max = 4, size = 36, overlap = 10 }) {
2455
+ const visible = avatars.slice(0, max);
2456
+ const remaining = avatars.length - max;
2457
+ return /* @__PURE__ */ jsxs39(XStack35, { alignItems: "center", children: [
2458
+ visible.map((avatar, i) => /* @__PURE__ */ jsx48(XStack35, { marginLeft: i === 0 ? 0 : -overlap, zIndex: visible.length - i, children: /* @__PURE__ */ jsx48(AvatarItem, { ...avatar, size }) }, i)),
2459
+ remaining > 0 && /* @__PURE__ */ jsx48(XStack35, { marginLeft: -overlap, zIndex: 0, children: /* @__PURE__ */ jsx48(
2460
+ Circle7,
2461
+ {
2462
+ size,
2463
+ backgroundColor: "$color6",
2464
+ borderWidth: 2,
2465
+ borderColor: "$background",
2466
+ alignItems: "center",
2467
+ justifyContent: "center",
2468
+ children: /* @__PURE__ */ jsxs39(SizableText42, { size: "$2", fontWeight: "600", color: "$color11", children: [
2469
+ "+",
2470
+ remaining
2471
+ ] })
2472
+ }
2473
+ ) })
2474
+ ] });
2475
+ }
2476
+
2477
+ // src/patterns/SwipeCards.tsx
2478
+ import { useState as useState12, useCallback as useCallback6 } from "react";
2479
+ import { Circle as Circle8, SizableText as SizableText43, XStack as XStack36, YStack as YStack39 } from "tamagui";
2480
+ import { jsx as jsx49, jsxs as jsxs40 } from "react/jsx-runtime";
2481
+ var STACK_SIZE = 3;
2482
+ var CARD_OFFSETS = [
2483
+ { scale: 1, y: 0, opacity: 1 },
2484
+ { scale: 0.95, y: 8, opacity: 0.9 },
2485
+ { scale: 0.9, y: 16, opacity: 0.8 }
2486
+ ];
2487
+ function SwipeCards({
2488
+ items,
2489
+ renderCard,
2490
+ onSwipeLeft,
2491
+ onSwipeRight,
2492
+ onEmpty,
2493
+ leftLabel = "Nope",
2494
+ rightLabel = "Like",
2495
+ emptyMessage = "No more cards"
2496
+ }) {
2497
+ const [index, setIndex] = useState12(0);
2498
+ const [exitDir, setExitDir] = useState12(null);
2499
+ const remaining = items.slice(index);
2500
+ const isEmpty = remaining.length === 0;
2501
+ const handleSwipe = useCallback6((dir) => {
2502
+ if (isEmpty) return;
2503
+ const current = items[index];
2504
+ setExitDir(dir);
2505
+ const timer = setTimeout(() => {
2506
+ setExitDir(null);
2507
+ setIndex((i) => {
2508
+ const next = i + 1;
2509
+ if (next >= items.length) onEmpty?.();
2510
+ return next;
2511
+ });
2512
+ dir === "left" ? onSwipeLeft?.(current) : onSwipeRight?.(current);
2513
+ }, 250);
2514
+ return () => clearTimeout(timer);
2515
+ }, [isEmpty, index, items, onEmpty, onSwipeLeft, onSwipeRight]);
2516
+ if (isEmpty) {
2517
+ return /* @__PURE__ */ jsx49(YStack39, { flex: 1, alignItems: "center", justifyContent: "center", gap: "$3", padding: "$4", children: /* @__PURE__ */ jsx49(SizableText43, { size: "$5", color: "$color8", children: emptyMessage }) });
2518
+ }
2519
+ return /* @__PURE__ */ jsxs40(YStack39, { flex: 1, gap: "$4", children: [
2520
+ /* @__PURE__ */ jsx49(YStack39, { flex: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx49(YStack39, { width: "100%", maxWidth: 340, aspectRatio: 3 / 4, position: "relative", children: remaining.slice(0, STACK_SIZE).reverse().map((item, reverseIdx) => {
2521
+ const stackIdx = Math.min(remaining.length, STACK_SIZE) - 1 - reverseIdx;
2522
+ const isTop = stackIdx === 0;
2523
+ const offset = CARD_OFFSETS[stackIdx] ?? CARD_OFFSETS[2];
2524
+ const exitX = exitDir === "left" ? -400 : exitDir === "right" ? 400 : 0;
2525
+ const exitRotate = exitDir === "left" ? "-15deg" : exitDir === "right" ? "15deg" : "0deg";
2526
+ return /* @__PURE__ */ jsxs40(
2527
+ YStack39,
2528
+ {
2529
+ position: "absolute",
2530
+ top: 0,
2531
+ left: 0,
2532
+ right: 0,
2533
+ bottom: 0,
2534
+ animation: "quick",
2535
+ borderRadius: "$5",
2536
+ overflow: "hidden",
2537
+ backgroundColor: "$background",
2538
+ elevate: isTop,
2539
+ shadowColor: "$shadowColor",
2540
+ shadowRadius: isTop ? 16 : 4,
2541
+ scale: isTop && exitDir ? 1 : offset.scale,
2542
+ opacity: isTop && exitDir ? 0 : offset.opacity,
2543
+ y: isTop && exitDir ? 0 : offset.y,
2544
+ x: isTop ? exitX : 0,
2545
+ rotate: isTop ? exitRotate : "0deg",
2546
+ children: [
2547
+ renderCard(item),
2548
+ isTop && exitDir === "left" && /* @__PURE__ */ jsx49(
2549
+ YStack39,
2550
+ {
2551
+ position: "absolute",
2552
+ top: "$4",
2553
+ right: "$4",
2554
+ borderWidth: 3,
2555
+ borderColor: "$red10",
2556
+ borderRadius: "$3",
2557
+ padding: "$2",
2558
+ rotate: "15deg",
2559
+ children: /* @__PURE__ */ jsx49(SizableText43, { size: "$7", fontWeight: "800", color: "$red10", children: leftLabel.toUpperCase() })
2560
+ }
2561
+ ),
2562
+ isTop && exitDir === "right" && /* @__PURE__ */ jsx49(
2563
+ YStack39,
2564
+ {
2565
+ position: "absolute",
2566
+ top: "$4",
2567
+ left: "$4",
2568
+ borderWidth: 3,
2569
+ borderColor: "$green10",
2570
+ borderRadius: "$3",
2571
+ padding: "$2",
2572
+ rotate: "-15deg",
2573
+ children: /* @__PURE__ */ jsx49(SizableText43, { size: "$7", fontWeight: "800", color: "$green10", children: rightLabel.toUpperCase() })
2574
+ }
2575
+ )
2576
+ ]
2577
+ },
2578
+ item.id
2579
+ );
2580
+ }) }) }),
2581
+ /* @__PURE__ */ jsxs40(XStack36, { justifyContent: "center", gap: "$6", paddingBottom: "$4", children: [
2582
+ /* @__PURE__ */ jsx49(
2583
+ Circle8,
2584
+ {
2585
+ size: 60,
2586
+ backgroundColor: "$red3",
2587
+ borderWidth: 2,
2588
+ borderColor: "$red7",
2589
+ pressStyle: { scale: 0.9, backgroundColor: "$red5" },
2590
+ animation: "quick",
2591
+ onPress: () => handleSwipe("left"),
2592
+ alignItems: "center",
2593
+ justifyContent: "center",
2594
+ children: /* @__PURE__ */ jsx49(SizableText43, { size: "$6", color: "$red10", fontWeight: "700", children: "\u2715" })
2595
+ }
2596
+ ),
2597
+ /* @__PURE__ */ jsx49(
2598
+ Circle8,
2599
+ {
2600
+ size: 60,
2601
+ backgroundColor: "$green3",
2602
+ borderWidth: 2,
2603
+ borderColor: "$green7",
2604
+ pressStyle: { scale: 0.9, backgroundColor: "$green5" },
2605
+ animation: "quick",
2606
+ onPress: () => handleSwipe("right"),
2607
+ alignItems: "center",
2608
+ justifyContent: "center",
2609
+ children: /* @__PURE__ */ jsx49(SizableText43, { size: "$6", color: "$green10", fontWeight: "700", children: "\u2665" })
2610
+ }
2611
+ )
2612
+ ] })
2613
+ ] });
2614
+ }
2615
+
2616
+ // src/patterns/GlassCard.tsx
2617
+ import { styled as styled12, YStack as YStack40 } from "tamagui";
2618
+ import { jsx as jsx50 } from "react/jsx-runtime";
2619
+ var BLUR = { light: 8, medium: 16, heavy: 24 };
2620
+ var TINT_BG = {
2621
+ light: "rgba(255,255,255,0.15)",
2622
+ dark: "rgba(0,0,0,0.25)"
2623
+ };
2624
+ var GlassFrame = styled12(YStack40, {
2625
+ borderWidth: 1,
2626
+ borderColor: "rgba(255,255,255,0.2)",
2627
+ overflow: "hidden"
2628
+ });
2629
+ function GlassCard({
2630
+ children,
2631
+ intensity = "medium",
2632
+ tint = "light",
2633
+ borderRadius = "$4",
2634
+ padding = "$4",
2635
+ elevated = false
2636
+ }) {
2637
+ const blur = BLUR[intensity];
2638
+ return /* @__PURE__ */ jsx50(
2639
+ GlassFrame,
2640
+ {
2641
+ borderRadius,
2642
+ padding,
2643
+ backgroundColor: TINT_BG[tint],
2644
+ elevate: elevated,
2645
+ shadowColor: elevated ? "$shadowColor" : void 0,
2646
+ shadowRadius: elevated ? 20 : void 0,
2647
+ shadowOpacity: elevated ? 0.3 : void 0,
2648
+ style: { backdropFilter: `blur(${blur}px)`, WebkitBackdropFilter: `blur(${blur}px)` },
2649
+ children
2650
+ }
2651
+ );
2652
+ }
1763
2653
  export {
1764
2654
  Accordion,
1765
2655
  ActionSheet,
1766
2656
  Adapt,
1767
- AlertDialog2 as AlertDialog,
2657
+ AlertDialog3 as AlertDialog,
1768
2658
  Anchor,
1769
2659
  AnimatePresence,
1770
2660
  AppHeader,
1771
2661
  Article,
1772
2662
  Aside,
1773
2663
  Avatar2 as Avatar,
2664
+ AvatarGroup,
1774
2665
  Badge,
1775
2666
  BlinkAccordion,
1776
2667
  Avatar as BlinkAvatar,
@@ -1784,13 +2675,17 @@ export {
1784
2675
  BlinkToggleGroup,
1785
2676
  BlinkTooltip,
1786
2677
  BottomSheet,
1787
- Button8 as Button,
2678
+ Button11 as Button,
1788
2679
  Card2 as Card,
1789
2680
  Carousel,
1790
2681
  ChatBubble,
1791
2682
  Checkbox,
1792
- Circle8 as Circle,
2683
+ Chip,
2684
+ ChipGroup,
2685
+ Circle9 as Circle,
2686
+ ConfirmDialog,
1793
2687
  Container,
2688
+ CountdownBanner,
1794
2689
  Dialog,
1795
2690
  DialogProvider,
1796
2691
  Divider,
@@ -1802,6 +2697,7 @@ export {
1802
2697
  Form,
1803
2698
  FormField,
1804
2699
  Frame,
2700
+ GlassCard,
1805
2701
  Grid,
1806
2702
  Group,
1807
2703
  H12 as H1,
@@ -1815,7 +2711,7 @@ export {
1815
2711
  ICONS,
1816
2712
  Icon,
1817
2713
  Image2 as Image,
1818
- Input3 as Input,
2714
+ Input5 as Input,
1819
2715
  KeyboardStickyFooter,
1820
2716
  Label,
1821
2717
  ListItem,
@@ -1824,10 +2720,12 @@ export {
1824
2720
  MediaCard,
1825
2721
  Nav,
1826
2722
  NotificationBanner,
2723
+ OTPInput,
1827
2724
  OnboardingCarousel,
1828
2725
  PageContainer,
1829
2726
  PageMainContainer,
1830
2727
  Paragraph,
2728
+ PasswordInput,
1831
2729
  PaywallScreen,
1832
2730
  Popover2 as Popover,
1833
2731
  Portal,
@@ -1835,6 +2733,8 @@ export {
1835
2733
  PortalItem,
1836
2734
  PortalProvider,
1837
2735
  Pressable,
2736
+ PricingTable,
2737
+ ProductCard,
1838
2738
  ProfileHeader,
1839
2739
  Progress,
1840
2740
  ProgressSteps,
@@ -1842,7 +2742,7 @@ export {
1842
2742
  RadioGroup,
1843
2743
  SafeArea,
1844
2744
  ScreenLayout,
1845
- ScrollView5 as ScrollView,
2745
+ ScrollView7 as ScrollView,
1846
2746
  SearchBar,
1847
2747
  Section,
1848
2748
  Select,
@@ -1851,7 +2751,7 @@ export {
1851
2751
  SettingsScreen,
1852
2752
  Sheet3 as Sheet,
1853
2753
  SizableStack,
1854
- SizableText34 as SizableText,
2754
+ SizableText44 as SizableText,
1855
2755
  Skeleton,
1856
2756
  Slider,
1857
2757
  Spacer,
@@ -1860,13 +2760,15 @@ export {
1860
2760
  Stack,
1861
2761
  StepPageLayout,
1862
2762
  SubHeading,
2763
+ SwipeCards,
1863
2764
  SwipeableRow,
1864
2765
  Switch2 as Switch,
1865
2766
  TabBar,
1866
2767
  Tabs,
1867
- Image6 as TamaguiImage,
2768
+ Image9 as TamaguiImage,
1868
2769
  ListItem2 as TamaguiListItem,
1869
2770
  TamaguiProvider,
2771
+ TestimonialCard,
1870
2772
  Text,
1871
2773
  TextArea,
1872
2774
  Theme,
@@ -1878,9 +2780,9 @@ export {
1878
2780
  View6 as View,
1879
2781
  VisuallyHidden,
1880
2782
  XGroup,
1881
- XStack27 as XStack,
2783
+ XStack37 as XStack,
1882
2784
  YGroup,
1883
- YStack32 as YStack,
2785
+ YStack41 as YStack,
1884
2786
  ZStack,
1885
2787
  addTheme,
1886
2788
  blinkConfig,
@@ -1902,7 +2804,7 @@ export {
1902
2804
  isWeb,
1903
2805
  replaceTheme,
1904
2806
  showError,
1905
- styled12 as styled,
2807
+ styled13 as styled,
1906
2808
  defaultConfig2 as tamaguiDefaultConfig,
1907
2809
  toast,
1908
2810
  updateTheme,