@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.d.mts +169 -3
- package/dist/index.d.ts +169 -3
- package/dist/index.js +1082 -168
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1001 -99
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
SizableStack,
|
|
14
14
|
ThemeableStack,
|
|
15
15
|
Frame,
|
|
16
|
-
XStack as
|
|
17
|
-
YStack as
|
|
16
|
+
XStack as XStack37,
|
|
17
|
+
YStack as YStack41,
|
|
18
18
|
ZStack,
|
|
19
|
-
ScrollView as
|
|
20
|
-
Circle as
|
|
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
|
|
41
|
+
SizableText as SizableText44,
|
|
42
42
|
Text,
|
|
43
43
|
Label,
|
|
44
|
-
Button as
|
|
45
|
-
Input as
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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 = "
|
|
1042
|
-
subtitle
|
|
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
|
-
|
|
1095
|
+
onTerms,
|
|
1096
|
+
onPrivacy,
|
|
1097
|
+
continueLabel = "Continue",
|
|
1098
|
+
reassurance = "Cancel anytime",
|
|
1099
|
+
hero,
|
|
1100
|
+
socialProof,
|
|
1101
|
+
countdownMinutes,
|
|
1102
|
+
badge
|
|
1049
1103
|
}) {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
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
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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__ */
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
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
|
-
|
|
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
|
|
1109
|
-
import { Button as Button4, SizableText as SizableText18, XStack as XStack11, YStack as YStack15, Circle as
|
|
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] =
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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] =
|
|
1367
|
-
const [password, setPassword] =
|
|
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
|
|
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(
|
|
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
|
|
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] =
|
|
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
|
|
1723
|
-
import { Circle as
|
|
1724
|
-
import { ScrollView as
|
|
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] =
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2678
|
+
Button11 as Button,
|
|
1788
2679
|
Card2 as Card,
|
|
1789
2680
|
Carousel,
|
|
1790
2681
|
ChatBubble,
|
|
1791
2682
|
Checkbox,
|
|
1792
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2783
|
+
XStack37 as XStack,
|
|
1882
2784
|
YGroup,
|
|
1883
|
-
|
|
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
|
-
|
|
2807
|
+
styled13 as styled,
|
|
1906
2808
|
defaultConfig2 as tamaguiDefaultConfig,
|
|
1907
2809
|
toast,
|
|
1908
2810
|
updateTheme,
|