@blinkdotnew/mobile-ui 2.0.0-alpha.10 → 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,42 +1375,55 @@ 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
- function BottomSheet({ open, onOpenChange, title, children, snapPoints = [85], dismissOnSnapToBottom = true, showHandle = true, showClose = false }) {
1322
- return /* @__PURE__ */ jsxs20(Sheet, { modal: true, open, onOpenChange, snapPoints, dismissOnSnapToBottom, animation: "medium", children: [
1323
- /* @__PURE__ */ jsx27(Sheet.Overlay, { animation: "lazy", enterStyle: { opacity: 0 }, exitStyle: { opacity: 0 } }),
1324
- showHandle && /* @__PURE__ */ jsx27(Sheet.Handle, {}),
1325
- /* @__PURE__ */ jsxs20(Sheet.Frame, { borderTopLeftRadius: "$6", borderTopRightRadius: "$6", backgroundColor: "$background", children: [
1326
- (title || showClose) && /* @__PURE__ */ jsxs20(XStack16, { paddingHorizontal: "$4", paddingTop: "$3", paddingBottom: "$2", alignItems: "center", justifyContent: "space-between", children: [
1327
- /* @__PURE__ */ jsx27(SizableText24, { size: "$6", fontWeight: "600", flexShrink: 1, children: title }),
1328
- showClose && /* @__PURE__ */ jsx27(
1329
- XStack16,
1330
- {
1331
- width: 28,
1332
- height: 28,
1333
- borderRadius: "$10",
1334
- backgroundColor: "$color4",
1335
- alignItems: "center",
1336
- justifyContent: "center",
1337
- pressStyle: { opacity: 0.7 },
1338
- onPress: () => onOpenChange(false),
1339
- children: /* @__PURE__ */ jsx27(SizableText24, { size: "$3", color: "$color10", fontWeight: "600", children: "\u2715" })
1340
- }
1341
- )
1342
- ] }),
1343
- /* @__PURE__ */ jsx27(ScrollView2, { contentContainerStyle: { paddingBottom: 40 }, children: /* @__PURE__ */ jsx27(YStack21, { padding: "$4", children }) })
1344
- ] })
1345
- ] });
1380
+ function BottomSheet({ open, onOpenChange, title, children, snapPoints = [85], dismissOnSnapToBottom = true, showHandle = true, showClose = false, zIndex = 1e5 }) {
1381
+ return /* @__PURE__ */ jsxs20(
1382
+ Sheet,
1383
+ {
1384
+ modal: true,
1385
+ forceRemoveScrollEnabled: open,
1386
+ open,
1387
+ onOpenChange,
1388
+ snapPoints,
1389
+ dismissOnSnapToBottom,
1390
+ zIndex,
1391
+ animation: "medium",
1392
+ children: [
1393
+ /* @__PURE__ */ jsx27(Sheet.Overlay, { animation: "lazy", enterStyle: { opacity: 0 }, exitStyle: { opacity: 0 } }),
1394
+ showHandle && /* @__PURE__ */ jsx27(Sheet.Handle, {}),
1395
+ /* @__PURE__ */ jsxs20(Sheet.Frame, { children: [
1396
+ (title || showClose) && /* @__PURE__ */ jsxs20(XStack16, { paddingHorizontal: "$4", paddingTop: "$3", paddingBottom: "$2", alignItems: "center", justifyContent: "space-between", children: [
1397
+ /* @__PURE__ */ jsx27(SizableText24, { size: "$6", fontWeight: "600", flexShrink: 1, children: title }),
1398
+ showClose && /* @__PURE__ */ jsx27(
1399
+ XStack16,
1400
+ {
1401
+ width: 28,
1402
+ height: 28,
1403
+ borderRadius: "$10",
1404
+ backgroundColor: "$color4",
1405
+ alignItems: "center",
1406
+ justifyContent: "center",
1407
+ pressStyle: { opacity: 0.7 },
1408
+ onPress: () => onOpenChange(false),
1409
+ children: /* @__PURE__ */ jsx27(SizableText24, { size: "$3", color: "$color10", fontWeight: "600", children: "\u2715" })
1410
+ }
1411
+ )
1412
+ ] }),
1413
+ /* @__PURE__ */ jsx27(ScrollView3, { contentContainerStyle: { paddingBottom: 40 }, children: /* @__PURE__ */ jsx27(YStack21, { padding: "$4", children }) })
1414
+ ] })
1415
+ ]
1416
+ }
1417
+ );
1346
1418
  }
1347
1419
 
1348
1420
  // src/patterns/LoginScreen.tsx
1349
- import { useState as useState6 } from "react";
1421
+ import { useState as useState7 } from "react";
1350
1422
  import { Button as Button6, SizableText as SizableText25, Spinner, XStack as XStack17, YStack as YStack22 } from "tamagui";
1351
1423
  import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
1352
1424
  function LoginScreen({ title = "Welcome", subtitle = "Sign in to continue", logo, providers = [], onProviderPress, showEmailForm, onEmailSubmit, onForgotPassword, onCreateAccount, onTerms, onPrivacy, loading }) {
1353
- const [email, setEmail] = useState6("");
1354
- const [password, setPassword] = useState6("");
1425
+ const [email, setEmail] = useState7("");
1426
+ const [password, setPassword] = useState7("");
1355
1427
  return /* @__PURE__ */ jsxs21(YStack22, { flex: 1, padding: "$4", gap: "$5", backgroundColor: "$background", justifyContent: "center", children: [
1356
1428
  /* @__PURE__ */ jsxs21(YStack22, { alignItems: "center", gap: "$2", children: [
1357
1429
  logo && /* @__PURE__ */ jsx28(YStack22, { paddingBottom: "$3", children: logo }),
@@ -1498,55 +1570,68 @@ function FloatingActionButton({ icon, label, onPress, position = "bottom-right",
1498
1570
  // src/patterns/ActionSheet.tsx
1499
1571
  import { Sheet as Sheet2, SizableText as SizableText29, XStack as XStack21, YStack as YStack24 } from "tamagui";
1500
1572
  import { jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
1501
- function ActionSheet({ open, onOpenChange, title, items, onSelect, cancelLabel = "Cancel" }) {
1502
- return /* @__PURE__ */ jsxs25(Sheet2, { modal: true, open, onOpenChange, snapPoints: [50], dismissOnSnapToBottom: true, animation: "medium", children: [
1503
- /* @__PURE__ */ jsx32(Sheet2.Overlay, { animation: "lazy", enterStyle: { opacity: 0 }, exitStyle: { opacity: 0 } }),
1504
- /* @__PURE__ */ jsx32(Sheet2.Handle, {}),
1505
- /* @__PURE__ */ jsxs25(Sheet2.Frame, { borderTopLeftRadius: "$6", borderTopRightRadius: "$6", backgroundColor: "$background", children: [
1506
- title && /* @__PURE__ */ jsx32(SizableText29, { size: "$3", color: "$color8", textAlign: "center", paddingTop: "$3", paddingBottom: "$1", children: title }),
1507
- /* @__PURE__ */ jsx32(YStack24, { paddingHorizontal: "$3", paddingTop: "$2", children: items.map((item) => /* @__PURE__ */ jsxs25(
1508
- XStack21,
1509
- {
1510
- height: 52,
1511
- alignItems: "center",
1512
- gap: "$3",
1513
- paddingHorizontal: "$3",
1514
- borderRadius: "$4",
1515
- pressStyle: { backgroundColor: "$color3" },
1516
- onPress: () => {
1517
- onSelect(item.id);
1518
- onOpenChange(false);
1519
- },
1520
- children: [
1521
- item.icon && /* @__PURE__ */ jsx32(SizableText29, { size: "$5", children: item.icon }),
1522
- /* @__PURE__ */ jsx32(
1523
- SizableText29,
1524
- {
1525
- size: "$5",
1526
- flex: 1,
1527
- color: item.destructive ? "$red9" : "$color12",
1528
- fontWeight: item.destructive ? "600" : "400",
1529
- children: item.label
1530
- }
1531
- )
1532
- ]
1533
- },
1534
- item.id
1535
- )) }),
1536
- /* @__PURE__ */ jsx32(YStack24, { paddingHorizontal: "$3", paddingVertical: "$3", borderTopWidth: 1, borderTopColor: "$borderColor", marginTop: "$2", children: /* @__PURE__ */ jsx32(
1537
- XStack21,
1538
- {
1539
- height: 48,
1540
- alignItems: "center",
1541
- justifyContent: "center",
1542
- borderRadius: "$4",
1543
- pressStyle: { backgroundColor: "$color3" },
1544
- onPress: () => onOpenChange(false),
1545
- children: /* @__PURE__ */ jsx32(SizableText29, { size: "$5", fontWeight: "600", color: "$color9", children: cancelLabel })
1546
- }
1547
- ) })
1548
- ] })
1549
- ] });
1573
+ function ActionSheet({ open, onOpenChange, title, items, onSelect, cancelLabel = "Cancel", zIndex = 1e5 }) {
1574
+ return /* @__PURE__ */ jsxs25(
1575
+ Sheet2,
1576
+ {
1577
+ modal: true,
1578
+ forceRemoveScrollEnabled: open,
1579
+ open,
1580
+ onOpenChange,
1581
+ snapPoints: [50],
1582
+ dismissOnSnapToBottom: true,
1583
+ zIndex,
1584
+ animation: "medium",
1585
+ children: [
1586
+ /* @__PURE__ */ jsx32(Sheet2.Overlay, { animation: "lazy", enterStyle: { opacity: 0 }, exitStyle: { opacity: 0 } }),
1587
+ /* @__PURE__ */ jsx32(Sheet2.Handle, {}),
1588
+ /* @__PURE__ */ jsxs25(Sheet2.Frame, { children: [
1589
+ title && /* @__PURE__ */ jsx32(SizableText29, { size: "$3", color: "$color8", textAlign: "center", paddingTop: "$3", paddingBottom: "$1", children: title }),
1590
+ /* @__PURE__ */ jsx32(YStack24, { paddingHorizontal: "$3", paddingTop: "$2", children: items.map((item) => /* @__PURE__ */ jsxs25(
1591
+ XStack21,
1592
+ {
1593
+ height: 52,
1594
+ alignItems: "center",
1595
+ gap: "$3",
1596
+ paddingHorizontal: "$3",
1597
+ borderRadius: "$4",
1598
+ pressStyle: { backgroundColor: "$color3" },
1599
+ onPress: () => {
1600
+ onSelect(item.id);
1601
+ onOpenChange(false);
1602
+ },
1603
+ children: [
1604
+ item.icon && /* @__PURE__ */ jsx32(SizableText29, { size: "$5", children: item.icon }),
1605
+ /* @__PURE__ */ jsx32(
1606
+ SizableText29,
1607
+ {
1608
+ size: "$5",
1609
+ flex: 1,
1610
+ color: item.destructive ? "$red9" : "$color12",
1611
+ fontWeight: item.destructive ? "600" : "400",
1612
+ children: item.label
1613
+ }
1614
+ )
1615
+ ]
1616
+ },
1617
+ item.id
1618
+ )) }),
1619
+ /* @__PURE__ */ jsx32(YStack24, { paddingHorizontal: "$3", paddingVertical: "$3", borderTopWidth: 1, borderTopColor: "$borderColor", marginTop: "$2", children: /* @__PURE__ */ jsx32(
1620
+ XStack21,
1621
+ {
1622
+ height: 48,
1623
+ alignItems: "center",
1624
+ justifyContent: "center",
1625
+ borderRadius: "$4",
1626
+ pressStyle: { backgroundColor: "$color3" },
1627
+ onPress: () => onOpenChange(false),
1628
+ children: /* @__PURE__ */ jsx32(SizableText29, { size: "$5", fontWeight: "600", color: "$color9", children: cancelLabel })
1629
+ }
1630
+ ) })
1631
+ ] })
1632
+ ]
1633
+ }
1634
+ );
1550
1635
  }
1551
1636
 
1552
1637
  // src/patterns/Skeleton.tsx
@@ -1594,7 +1679,7 @@ function NotificationBanner({ title, message, variant = "info", onPress, onDismi
1594
1679
  }
1595
1680
 
1596
1681
  // src/patterns/ProgressSteps.tsx
1597
- 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";
1598
1683
  import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
1599
1684
  function ProgressSteps({ steps, currentStep, variant = "dots" }) {
1600
1685
  if (variant === "bar") {
@@ -1606,7 +1691,7 @@ function ProgressSteps({ steps, currentStep, variant = "dots" }) {
1606
1691
  }
1607
1692
  return /* @__PURE__ */ jsx35(XStack23, { alignItems: "center", justifyContent: "center", gap: "$0", children: steps.map((label, i) => /* @__PURE__ */ jsxs27(XStack23, { alignItems: "center", gap: "$0", children: [
1608
1693
  /* @__PURE__ */ jsxs27(YStack27, { alignItems: "center", gap: "$1.5", children: [
1609
- /* @__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 }) }),
1610
1695
  /* @__PURE__ */ jsx35(SizableText31, { size: "$1", color: i <= currentStep ? "$color11" : "$color8", numberOfLines: 1, children: label })
1611
1696
  ] }),
1612
1697
  i < steps.length - 1 && /* @__PURE__ */ jsx35(YStack27, { height: 2, width: 32, backgroundColor: i < currentStep ? "$color9" : "$color4", marginBottom: "$4" })
@@ -1614,11 +1699,11 @@ function ProgressSteps({ steps, currentStep, variant = "dots" }) {
1614
1699
  }
1615
1700
 
1616
1701
  // src/patterns/SwipeableRow.tsx
1617
- import { useState as useState7 } from "react";
1702
+ import { useState as useState8 } from "react";
1618
1703
  import { Button as Button7, SizableText as SizableText32, XStack as XStack24, YStack as YStack28 } from "tamagui";
1619
1704
  import { Fragment as Fragment2, jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
1620
1705
  function SwipeableRow({ children, leftActions, rightActions }) {
1621
- const [showActions, setShowActions] = useState7(false);
1706
+ const [showActions, setShowActions] = useState8(false);
1622
1707
  const actions = [...leftActions ?? [], ...rightActions ?? []];
1623
1708
  if (actions.length === 0) return /* @__PURE__ */ jsx36(Fragment2, { children });
1624
1709
  return /* @__PURE__ */ jsxs28(YStack28, { children: [
@@ -1693,17 +1778,17 @@ function MediaCard({ image, title, subtitle, overlay = "gradient", aspectRatio =
1693
1778
  }
1694
1779
 
1695
1780
  // src/patterns/Carousel.tsx
1696
- import { Children as Children2, useState as useState8 } from "react";
1697
- import { Circle as Circle7, XStack as XStack26, YStack as YStack30 } from "tamagui";
1698
- 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";
1699
1784
  import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
1700
1785
  function Carousel({ children, gap = "$3", snapToInterval, showIndicators = false }) {
1701
- const [activeIndex, setActiveIndex] = useState8(0);
1786
+ const [activeIndex, setActiveIndex] = useState9(0);
1702
1787
  const count = Children2.count(children);
1703
1788
  const gapPx = gap === "$2" ? 8 : gap === "$3" ? 12 : 16;
1704
1789
  return /* @__PURE__ */ jsxs30(YStack30, { gap: "$3", children: [
1705
1790
  /* @__PURE__ */ jsx38(
1706
- ScrollView3,
1791
+ ScrollView4,
1707
1792
  {
1708
1793
  horizontal: true,
1709
1794
  showsHorizontalScrollIndicator: false,
@@ -1716,17 +1801,17 @@ function Carousel({ children, gap = "$3", snapToInterval, showIndicators = false
1716
1801
  children
1717
1802
  }
1718
1803
  ),
1719
- 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)) })
1720
1805
  ] });
1721
1806
  }
1722
1807
 
1723
1808
  // src/patterns/PullToRefresh.tsx
1724
1809
  import { YStack as YStack31 } from "tamagui";
1725
- import { RefreshControl, ScrollView as ScrollView4 } from "react-native";
1810
+ import { RefreshControl, ScrollView as ScrollView5 } from "react-native";
1726
1811
  import { jsx as jsx39 } from "react/jsx-runtime";
1727
1812
  function PullToRefresh({ children, onRefresh, refreshing = false }) {
1728
1813
  return /* @__PURE__ */ jsx39(
1729
- ScrollView4,
1814
+ ScrollView5,
1730
1815
  {
1731
1816
  contentContainerStyle: { flexGrow: 1 },
1732
1817
  refreshControl: /* @__PURE__ */ jsx39(RefreshControl, { refreshing, onRefresh }),
@@ -1734,17 +1819,849 @@ function PullToRefresh({ children, onRefresh, refreshing = false }) {
1734
1819
  }
1735
1820
  );
1736
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
+ }
1737
2653
  export {
1738
2654
  Accordion,
1739
2655
  ActionSheet,
1740
2656
  Adapt,
1741
- AlertDialog2 as AlertDialog,
2657
+ AlertDialog3 as AlertDialog,
1742
2658
  Anchor,
1743
2659
  AnimatePresence,
1744
2660
  AppHeader,
1745
2661
  Article,
1746
2662
  Aside,
1747
2663
  Avatar2 as Avatar,
2664
+ AvatarGroup,
1748
2665
  Badge,
1749
2666
  BlinkAccordion,
1750
2667
  Avatar as BlinkAvatar,
@@ -1758,13 +2675,17 @@ export {
1758
2675
  BlinkToggleGroup,
1759
2676
  BlinkTooltip,
1760
2677
  BottomSheet,
1761
- Button8 as Button,
2678
+ Button11 as Button,
1762
2679
  Card2 as Card,
1763
2680
  Carousel,
1764
2681
  ChatBubble,
1765
2682
  Checkbox,
1766
- Circle8 as Circle,
2683
+ Chip,
2684
+ ChipGroup,
2685
+ Circle9 as Circle,
2686
+ ConfirmDialog,
1767
2687
  Container,
2688
+ CountdownBanner,
1768
2689
  Dialog,
1769
2690
  DialogProvider,
1770
2691
  Divider,
@@ -1776,6 +2697,7 @@ export {
1776
2697
  Form,
1777
2698
  FormField,
1778
2699
  Frame,
2700
+ GlassCard,
1779
2701
  Grid,
1780
2702
  Group,
1781
2703
  H12 as H1,
@@ -1789,7 +2711,7 @@ export {
1789
2711
  ICONS,
1790
2712
  Icon,
1791
2713
  Image2 as Image,
1792
- Input3 as Input,
2714
+ Input5 as Input,
1793
2715
  KeyboardStickyFooter,
1794
2716
  Label,
1795
2717
  ListItem,
@@ -1798,10 +2720,12 @@ export {
1798
2720
  MediaCard,
1799
2721
  Nav,
1800
2722
  NotificationBanner,
2723
+ OTPInput,
1801
2724
  OnboardingCarousel,
1802
2725
  PageContainer,
1803
2726
  PageMainContainer,
1804
2727
  Paragraph,
2728
+ PasswordInput,
1805
2729
  PaywallScreen,
1806
2730
  Popover2 as Popover,
1807
2731
  Portal,
@@ -1809,6 +2733,8 @@ export {
1809
2733
  PortalItem,
1810
2734
  PortalProvider,
1811
2735
  Pressable,
2736
+ PricingTable,
2737
+ ProductCard,
1812
2738
  ProfileHeader,
1813
2739
  Progress,
1814
2740
  ProgressSteps,
@@ -1816,7 +2742,7 @@ export {
1816
2742
  RadioGroup,
1817
2743
  SafeArea,
1818
2744
  ScreenLayout,
1819
- ScrollView5 as ScrollView,
2745
+ ScrollView7 as ScrollView,
1820
2746
  SearchBar,
1821
2747
  Section,
1822
2748
  Select,
@@ -1825,7 +2751,7 @@ export {
1825
2751
  SettingsScreen,
1826
2752
  Sheet3 as Sheet,
1827
2753
  SizableStack,
1828
- SizableText34 as SizableText,
2754
+ SizableText44 as SizableText,
1829
2755
  Skeleton,
1830
2756
  Slider,
1831
2757
  Spacer,
@@ -1834,13 +2760,15 @@ export {
1834
2760
  Stack,
1835
2761
  StepPageLayout,
1836
2762
  SubHeading,
2763
+ SwipeCards,
1837
2764
  SwipeableRow,
1838
2765
  Switch2 as Switch,
1839
2766
  TabBar,
1840
2767
  Tabs,
1841
- Image6 as TamaguiImage,
2768
+ Image9 as TamaguiImage,
1842
2769
  ListItem2 as TamaguiListItem,
1843
2770
  TamaguiProvider,
2771
+ TestimonialCard,
1844
2772
  Text,
1845
2773
  TextArea,
1846
2774
  Theme,
@@ -1852,9 +2780,9 @@ export {
1852
2780
  View6 as View,
1853
2781
  VisuallyHidden,
1854
2782
  XGroup,
1855
- XStack27 as XStack,
2783
+ XStack37 as XStack,
1856
2784
  YGroup,
1857
- YStack32 as YStack,
2785
+ YStack41 as YStack,
1858
2786
  ZStack,
1859
2787
  addTheme,
1860
2788
  blinkConfig,
@@ -1876,7 +2804,7 @@ export {
1876
2804
  isWeb,
1877
2805
  replaceTheme,
1878
2806
  showError,
1879
- styled12 as styled,
2807
+ styled13 as styled,
1880
2808
  defaultConfig2 as tamaguiDefaultConfig,
1881
2809
  toast,
1882
2810
  updateTheme,