@olwiba/ui 0.0.28
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/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/index.d.ts +762 -0
- package/dist/index.js +2102 -0
- package/dist/index.js.map +1 -0
- package/package.json +88 -0
- package/src/app/AppShell.tsx +365 -0
- package/src/app/AuthSection.tsx +141 -0
- package/src/app/EmptyState.tsx +34 -0
- package/src/app/ErrorPage.tsx +78 -0
- package/src/app/UpgradePrompt.tsx +203 -0
- package/src/blog/PostCard.tsx +66 -0
- package/src/blog/PostList.tsx +27 -0
- package/src/components/ConfirmDialog.tsx +43 -0
- package/src/components/ContextMenu.tsx +86 -0
- package/src/components/DevBanner.tsx +22 -0
- package/src/components/Dock.tsx +94 -0
- package/src/components/FeatureCard.tsx +45 -0
- package/src/components/GlassCard.tsx +30 -0
- package/src/components/ImageCard.tsx +60 -0
- package/src/components/PageHeader.tsx +101 -0
- package/src/components/PricingCard.tsx +90 -0
- package/src/components/RegisterHotkeys.tsx +43 -0
- package/src/components/RootErrorFallback.tsx +29 -0
- package/src/components/Spinner.tsx +14 -0
- package/src/components/Spotlight.tsx +104 -0
- package/src/components/StatCard.tsx +44 -0
- package/src/components/Suspensed.tsx +17 -0
- package/src/components/TestimonialCard.tsx +64 -0
- package/src/components/ThemeColorUpdater.tsx +23 -0
- package/src/components/ThemeSwitchMinimal.tsx +28 -0
- package/src/components/VersionBanner.tsx +38 -0
- package/src/context/OlwibaUIContext.tsx +58 -0
- package/src/hooks/use-confirm.ts +64 -0
- package/src/hooks/use-controlled-open.ts +33 -0
- package/src/hooks/use-copy-to-clipboard.ts +20 -0
- package/src/hooks/use-debounce.ts +14 -0
- package/src/hooks/use-intersection-observer.ts +25 -0
- package/src/hooks/use-local-storage.ts +31 -0
- package/src/hooks/use-media-query.ts +21 -0
- package/src/hooks/use-mounted.ts +11 -0
- package/src/hooks/use-pagination.ts +31 -0
- package/src/hooks/use-scrolled-past.ts +27 -0
- package/src/index.ts +113 -0
- package/src/lib/utils.ts +6 -0
- package/src/marketing/ContactSection.tsx +110 -0
- package/src/marketing/CtaSection.tsx +74 -0
- package/src/marketing/FaqSection.tsx +44 -0
- package/src/marketing/FeaturesSection.tsx +42 -0
- package/src/marketing/Footer.tsx +87 -0
- package/src/marketing/HeroSection.tsx +112 -0
- package/src/marketing/LogoStrip.tsx +94 -0
- package/src/marketing/Navbar.tsx +142 -0
- package/src/marketing/NewsletterSection.tsx +64 -0
- package/src/marketing/PricingSection.tsx +120 -0
- package/src/marketing/SectionTitle.tsx +30 -0
- package/src/marketing/StatsSection.tsx +63 -0
- package/src/marketing/TeamSection.tsx +110 -0
- package/src/marketing/TestimonialsSection.tsx +56 -0
- package/src/motion/CountUp.tsx +64 -0
- package/src/motion/FadeIn.tsx +69 -0
- package/src/motion/PageTransition.tsx +49 -0
- package/src/motion/StaggerChildren.tsx +74 -0
- package/src/overlays/Overlay.tsx +88 -0
- package/src/overlays/Underlay.tsx +114 -0
- package/src/primitives/Badge.tsx +11 -0
- package/src/primitives/Button.tsx +16 -0
- package/src/primitives/Card.tsx +22 -0
- package/src/primitives/Checkbox.tsx +17 -0
- package/src/primitives/Input.tsx +16 -0
- package/src/primitives/Switch.tsx +16 -0
- package/src/primitives/Textarea.tsx +16 -0
- package/src/primitives/index.ts +7 -0
- package/src/types/external-packages.d.ts +79 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2102 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
import * as React25 from 'react';
|
|
4
|
+
import { useState, useEffect } from 'react';
|
|
5
|
+
import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from '@olwiba/cn';
|
|
6
|
+
export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@olwiba/cn';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { Compass, ArrowLeft, X, ArrowRight, Sparkles, Check, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, Phone, MapPin, MessageSquare, Send, Sun, Moon, Menu, Search, TrendingUp, TrendingDown, ChevronRight, ChevronLeft, AlertTriangle, Loader2, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
// src/lib/utils.ts
|
|
11
|
+
function cn(...inputs) {
|
|
12
|
+
return twMerge(clsx(inputs));
|
|
13
|
+
}
|
|
14
|
+
var OlwibaUIContext = React25.createContext({
|
|
15
|
+
isMobile: false,
|
|
16
|
+
mode: "default"
|
|
17
|
+
});
|
|
18
|
+
function OlwibaUIProvider({ children, isMobile: isMobileProp, mode = "default" }) {
|
|
19
|
+
const detectedMobile = useIsMobile();
|
|
20
|
+
const isMobile = isMobileProp ?? detectedMobile;
|
|
21
|
+
const variant = mode !== "default" ? mode : void 0;
|
|
22
|
+
return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
|
|
23
|
+
}
|
|
24
|
+
function useOlwibaUI() {
|
|
25
|
+
return React25.useContext(OlwibaUIContext);
|
|
26
|
+
}
|
|
27
|
+
function useUIMode() {
|
|
28
|
+
return React25.useContext(OlwibaUIContext).mode;
|
|
29
|
+
}
|
|
30
|
+
var Button = React25.forwardRef(
|
|
31
|
+
({ mode, ...props }, ref) => {
|
|
32
|
+
const ctxMode = useUIMode();
|
|
33
|
+
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
34
|
+
return /* @__PURE__ */ jsx(Button$1, { mode: resolvedMode, ...props, ref });
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
Button.displayName = "Button";
|
|
38
|
+
var Card = React25.forwardRef(
|
|
39
|
+
({ mode, ...props }, ref) => {
|
|
40
|
+
const ctxMode = useUIMode();
|
|
41
|
+
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
42
|
+
return /* @__PURE__ */ jsx(Card$1, { mode: resolvedMode, ...props, ref });
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
Card.displayName = "Card";
|
|
46
|
+
function Badge(props) {
|
|
47
|
+
return /* @__PURE__ */ jsx(Badge$1, { ...props });
|
|
48
|
+
}
|
|
49
|
+
var Input = React25.forwardRef(
|
|
50
|
+
({ mode, ...props }, ref) => {
|
|
51
|
+
const ctxMode = useUIMode();
|
|
52
|
+
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
53
|
+
return /* @__PURE__ */ jsx(Input$1, { mode: resolvedMode, ...props, ref });
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
Input.displayName = "Input";
|
|
57
|
+
var Textarea = React25.forwardRef(
|
|
58
|
+
({ mode, ...props }, ref) => {
|
|
59
|
+
const ctxMode = useUIMode();
|
|
60
|
+
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
61
|
+
return /* @__PURE__ */ jsx(Textarea$1, { mode: resolvedMode, ...props, ref });
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
Textarea.displayName = "Textarea";
|
|
65
|
+
var Checkbox = React25.forwardRef(({ mode, ...props }, ref) => {
|
|
66
|
+
const ctxMode = useUIMode();
|
|
67
|
+
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
68
|
+
return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
|
|
69
|
+
});
|
|
70
|
+
Checkbox.displayName = "Checkbox";
|
|
71
|
+
var Switch = React25.forwardRef(({ mode: _mode, ...props }, ref) => {
|
|
72
|
+
return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
|
|
73
|
+
});
|
|
74
|
+
Switch.displayName = "Switch";
|
|
75
|
+
function NavUser({ user }) {
|
|
76
|
+
const { isMobile } = useSidebar();
|
|
77
|
+
const uiMode = useUIMode();
|
|
78
|
+
const avatarMode = uiMode !== "default" ? uiMode : void 0;
|
|
79
|
+
const initials = (user.name ?? user.email).slice(0, 2).toUpperCase();
|
|
80
|
+
return /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
81
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
82
|
+
SidebarMenuButton,
|
|
83
|
+
{
|
|
84
|
+
size: "lg",
|
|
85
|
+
className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
|
|
86
|
+
children: [
|
|
87
|
+
/* @__PURE__ */ jsxs(Avatar, { mode: avatarMode, size: "sm", className: "grayscale", children: [
|
|
88
|
+
user.avatar && /* @__PURE__ */ jsx(AvatarImage, { src: user.avatar, alt: user.name }),
|
|
89
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: initials })
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
|
|
92
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: user.name ?? user.email }),
|
|
93
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: user.plan ? `${user.plan} plan` : user.email })
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ jsx(MoreVerticalIcon, { className: "ml-auto size-4" })
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
) }),
|
|
99
|
+
/* @__PURE__ */ jsxs(
|
|
100
|
+
DropdownMenuContent,
|
|
101
|
+
{
|
|
102
|
+
className: "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg",
|
|
103
|
+
side: isMobile ? "bottom" : "right",
|
|
104
|
+
align: "end",
|
|
105
|
+
sideOffset: 4,
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx(DropdownMenuLabel, { className: "p-0 font-normal", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-1 py-1.5 text-left text-sm", children: [
|
|
108
|
+
/* @__PURE__ */ jsxs(Avatar, { mode: avatarMode, size: "sm", children: [
|
|
109
|
+
user.avatar && /* @__PURE__ */ jsx(AvatarImage, { src: user.avatar, alt: user.name }),
|
|
110
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: initials })
|
|
111
|
+
] }),
|
|
112
|
+
/* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
|
|
113
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: user.name ?? user.email }),
|
|
114
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
115
|
+
] })
|
|
116
|
+
] }) }),
|
|
117
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
118
|
+
/* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
|
|
119
|
+
user.onBilling && /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: user.onBilling, children: [
|
|
120
|
+
/* @__PURE__ */ jsx(CreditCardIcon, {}),
|
|
121
|
+
"Billing"
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ jsxs(DropdownMenuItem, { children: [
|
|
124
|
+
/* @__PURE__ */ jsx(BellIcon, {}),
|
|
125
|
+
"Notifications"
|
|
126
|
+
] })
|
|
127
|
+
] }),
|
|
128
|
+
user.onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
129
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
130
|
+
/* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: user.onSignOut, children: [
|
|
131
|
+
/* @__PURE__ */ jsx(LogOutIcon, {}),
|
|
132
|
+
"Sign out"
|
|
133
|
+
] })
|
|
134
|
+
] })
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
] }) }) });
|
|
139
|
+
}
|
|
140
|
+
function ShellSidebar({
|
|
141
|
+
brand,
|
|
142
|
+
navItems,
|
|
143
|
+
action,
|
|
144
|
+
user,
|
|
145
|
+
renderLink,
|
|
146
|
+
collapsible,
|
|
147
|
+
side,
|
|
148
|
+
sidebarPosition
|
|
149
|
+
}) {
|
|
150
|
+
return /* @__PURE__ */ jsxs(Sidebar, { side, collapsible, sidebarPosition, children: [
|
|
151
|
+
/* @__PURE__ */ jsx(SidebarHeader, { children: /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, { asChild: true, className: "data-[slot=sidebar-menu-button]:!p-1.5", children: renderLink({
|
|
152
|
+
href: brand.href ?? "#",
|
|
153
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
154
|
+
brand.logo,
|
|
155
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-semibold", children: brand.name })
|
|
156
|
+
] })
|
|
157
|
+
}) }) }) }) }),
|
|
158
|
+
/* @__PURE__ */ jsx(SidebarContent, { children: /* @__PURE__ */ jsx(SidebarGroup, { children: /* @__PURE__ */ jsxs(SidebarGroupContent, { className: "flex flex-col gap-2", children: [
|
|
159
|
+
action && /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: action.href ? /* @__PURE__ */ jsx(
|
|
160
|
+
SidebarMenuButton,
|
|
161
|
+
{
|
|
162
|
+
asChild: true,
|
|
163
|
+
tooltip: action.label,
|
|
164
|
+
className: "min-w-8 bg-primary text-primary-foreground duration-200 ease-linear hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground",
|
|
165
|
+
children: renderLink({
|
|
166
|
+
href: action.href,
|
|
167
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
168
|
+
action.icon && /* @__PURE__ */ jsx(action.icon, {}),
|
|
169
|
+
/* @__PURE__ */ jsx("span", { children: action.label })
|
|
170
|
+
] })
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
) : /* @__PURE__ */ jsxs(
|
|
174
|
+
SidebarMenuButton,
|
|
175
|
+
{
|
|
176
|
+
tooltip: action.label,
|
|
177
|
+
onClick: action.onClick,
|
|
178
|
+
className: "min-w-8 bg-primary text-primary-foreground duration-200 ease-linear hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground",
|
|
179
|
+
children: [
|
|
180
|
+
action.icon && /* @__PURE__ */ jsx(action.icon, {}),
|
|
181
|
+
/* @__PURE__ */ jsx("span", { children: action.label })
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
) }) }),
|
|
185
|
+
/* @__PURE__ */ jsx(SidebarMenu, { children: navItems.map((item) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, { asChild: true, tooltip: item.label, isActive: item.isActive, children: renderLink({
|
|
186
|
+
href: item.href,
|
|
187
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
188
|
+
/* @__PURE__ */ jsx(item.icon, {}),
|
|
189
|
+
/* @__PURE__ */ jsx("span", { children: item.label })
|
|
190
|
+
] })
|
|
191
|
+
}) }) }, item.label)) })
|
|
192
|
+
] }) }) }),
|
|
193
|
+
/* @__PURE__ */ jsx(SidebarFooter, { children: /* @__PURE__ */ jsx(NavUser, { user }) })
|
|
194
|
+
] });
|
|
195
|
+
}
|
|
196
|
+
function ShellHeader({ pageTitle }) {
|
|
197
|
+
return /* @__PURE__ */ jsx("header", { className: "flex h-12 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6", children: [
|
|
198
|
+
/* @__PURE__ */ jsx(SidebarTrigger, { className: "-ml-1" }),
|
|
199
|
+
/* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-2 data-[orientation=vertical]:h-4" }),
|
|
200
|
+
/* @__PURE__ */ jsx("h1", { className: "text-base font-medium", children: pageTitle })
|
|
201
|
+
] }) });
|
|
202
|
+
}
|
|
203
|
+
var defaultBrand = {
|
|
204
|
+
name: "App",
|
|
205
|
+
href: "#"
|
|
206
|
+
};
|
|
207
|
+
var defaultUser = {
|
|
208
|
+
name: "User",
|
|
209
|
+
email: "user@example.com"
|
|
210
|
+
};
|
|
211
|
+
var defaultRenderLink = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
212
|
+
function AppShell({
|
|
213
|
+
brand = defaultBrand,
|
|
214
|
+
navItems = [],
|
|
215
|
+
action,
|
|
216
|
+
user = defaultUser,
|
|
217
|
+
pageTitle = "Dashboard",
|
|
218
|
+
renderLink = defaultRenderLink,
|
|
219
|
+
collapsible = "icon",
|
|
220
|
+
sidebarPosition = "viewport",
|
|
221
|
+
side = "left",
|
|
222
|
+
children
|
|
223
|
+
} = {}) {
|
|
224
|
+
const isContained = sidebarPosition === "contained";
|
|
225
|
+
const inner = /* @__PURE__ */ jsxs(
|
|
226
|
+
SidebarProvider,
|
|
227
|
+
{
|
|
228
|
+
layout: isContained ? "embedded" : "viewport",
|
|
229
|
+
className: isContained ? "overflow-hidden" : "h-svh overflow-hidden",
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
ShellSidebar,
|
|
233
|
+
{
|
|
234
|
+
brand,
|
|
235
|
+
navItems,
|
|
236
|
+
action,
|
|
237
|
+
user,
|
|
238
|
+
renderLink,
|
|
239
|
+
collapsible,
|
|
240
|
+
side,
|
|
241
|
+
sidebarPosition
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
/* @__PURE__ */ jsxs(SidebarInset, { className: isContained ? void 0 : "overflow-y-auto", children: [
|
|
245
|
+
/* @__PURE__ */ jsx(ShellHeader, { pageTitle }),
|
|
246
|
+
children
|
|
247
|
+
] })
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
if (isContained) {
|
|
252
|
+
return /* @__PURE__ */ jsx("div", { className: "relative flex h-full min-h-0 w-full max-w-full flex-col overflow-hidden", children: inner });
|
|
253
|
+
}
|
|
254
|
+
return inner;
|
|
255
|
+
}
|
|
256
|
+
function CenteredAuth({ children }) {
|
|
257
|
+
return /* @__PURE__ */ jsx("section", { className: "flex h-full min-h-[720px] w-full flex-col items-center justify-center gap-6 rounded-2xl bg-muted p-6 md:p-10", children: /* @__PURE__ */ jsx("div", { className: "flex w-full max-w-sm flex-col gap-6", children }) });
|
|
258
|
+
}
|
|
259
|
+
function SplitAuth({ children, panel }) {
|
|
260
|
+
const defaultPanel = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
261
|
+
/* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "w-fit", children: "Welcome" }),
|
|
262
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
263
|
+
/* @__PURE__ */ jsx("h2", { className: "text-3xl font-semibold tracking-tight", children: "Build better, ship faster" }),
|
|
264
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-md text-muted-foreground", children: "Sign in to continue building with ready-made components and layouts." })
|
|
265
|
+
] }),
|
|
266
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm text-muted-foreground", children: [
|
|
267
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
268
|
+
/* @__PURE__ */ jsx(ShieldCheck, { className: "size-4" }),
|
|
269
|
+
" Type-safe components"
|
|
270
|
+
] }),
|
|
271
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
272
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
|
|
273
|
+
" Polished defaults"
|
|
274
|
+
] }),
|
|
275
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
276
|
+
/* @__PURE__ */ jsx(Building2, { className: "size-4" }),
|
|
277
|
+
" Plug-and-play shells"
|
|
278
|
+
] })
|
|
279
|
+
] })
|
|
280
|
+
] });
|
|
281
|
+
return /* @__PURE__ */ jsxs("section", { className: "grid h-full min-h-[560px] overflow-hidden rounded-2xl border bg-card lg:grid-cols-2", children: [
|
|
282
|
+
/* @__PURE__ */ jsxs("div", { className: "relative hidden p-8 pb-16 lg:flex lg:flex-col lg:justify-center", children: [
|
|
283
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-primary/20 via-transparent to-muted" }),
|
|
284
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-10", children: panel ?? defaultPanel })
|
|
285
|
+
] }),
|
|
286
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-6 lg:p-10", children })
|
|
287
|
+
] });
|
|
288
|
+
}
|
|
289
|
+
function DefaultForm({ onSubmit, onSso, signUpHref = "#", forgotPasswordHref = "#", brand }) {
|
|
290
|
+
return /* @__PURE__ */ jsxs(Card$1, { className: "w-full max-w-md", children: [
|
|
291
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
292
|
+
brand && /* @__PURE__ */ jsx("div", { className: "mb-2", children: brand }),
|
|
293
|
+
/* @__PURE__ */ jsx(CardTitle, { children: "Sign in" }),
|
|
294
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Enter your email and password to continue." })
|
|
295
|
+
] }),
|
|
296
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
297
|
+
/* @__PURE__ */ jsxs("form", { onSubmit, className: "space-y-4", children: [
|
|
298
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
299
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-email", children: "Email" }),
|
|
300
|
+
/* @__PURE__ */ jsx(Input$1, { id: "auth-email", type: "email", placeholder: "name@company.com" })
|
|
301
|
+
] }),
|
|
302
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
303
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
304
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-password", children: "Password" }),
|
|
305
|
+
forgotPasswordHref && /* @__PURE__ */ jsx("a", { className: "text-xs text-muted-foreground underline underline-offset-4 hover:text-foreground", href: forgotPasswordHref, children: "Forgot password?" })
|
|
306
|
+
] }),
|
|
307
|
+
/* @__PURE__ */ jsx(Input$1, { id: "auth-password", type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })
|
|
308
|
+
] }),
|
|
309
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
310
|
+
/* @__PURE__ */ jsx(Button$1, { type: "submit", className: "w-full", children: "Continue" }),
|
|
311
|
+
onSso && /* @__PURE__ */ jsx(Button$1, { type: "button", variant: "outline", className: "w-full", onClick: onSso, children: "Use SSO" })
|
|
312
|
+
] })
|
|
313
|
+
] }),
|
|
314
|
+
signUpHref && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
|
|
315
|
+
"New here?",
|
|
316
|
+
" ",
|
|
317
|
+
/* @__PURE__ */ jsx("a", { className: "text-foreground underline underline-offset-4", href: signUpHref, children: "Create an account" })
|
|
318
|
+
] })
|
|
319
|
+
] })
|
|
320
|
+
] });
|
|
321
|
+
}
|
|
322
|
+
function AuthSection({
|
|
323
|
+
layout = "centered",
|
|
324
|
+
children,
|
|
325
|
+
panel,
|
|
326
|
+
className,
|
|
327
|
+
...formProps
|
|
328
|
+
}) {
|
|
329
|
+
const form = children ?? /* @__PURE__ */ jsx(DefaultForm, { ...formProps });
|
|
330
|
+
if (layout === "split") {
|
|
331
|
+
return /* @__PURE__ */ jsx("div", { className: cn$1("h-full", className), children: /* @__PURE__ */ jsx(SplitAuth, { panel, children: form }) });
|
|
332
|
+
}
|
|
333
|
+
return /* @__PURE__ */ jsx("div", { className: cn$1("h-full", className), children: /* @__PURE__ */ jsx(CenteredAuth, { children: form }) });
|
|
334
|
+
}
|
|
335
|
+
function EmptyState({ icon: Icon, title, description, action, className, ...props }) {
|
|
336
|
+
return /* @__PURE__ */ jsxs(
|
|
337
|
+
"div",
|
|
338
|
+
{
|
|
339
|
+
className: cn$1("flex flex-col items-center justify-center gap-3 py-12 text-center", className),
|
|
340
|
+
...props,
|
|
341
|
+
children: [
|
|
342
|
+
Icon && /* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl border bg-muted", children: /* @__PURE__ */ jsx(Icon, { className: "size-6 text-muted-foreground" }) }),
|
|
343
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
344
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold", children: title }),
|
|
345
|
+
description && /* @__PURE__ */ jsx("p", { className: "max-w-xs text-sm text-muted-foreground", children: description })
|
|
346
|
+
] }),
|
|
347
|
+
action
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
var defaultRenderLink2 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
353
|
+
function ErrorPage({
|
|
354
|
+
statusCode = "404",
|
|
355
|
+
title = "Page not found",
|
|
356
|
+
description = "The page you're looking for doesn't exist or has been moved. Check the URL or head back home.",
|
|
357
|
+
action = { label: "Take me home", href: "/" },
|
|
358
|
+
backAction = { label: "Go back" },
|
|
359
|
+
renderLink = defaultRenderLink2
|
|
360
|
+
}) {
|
|
361
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-[560px] flex-col items-center justify-center px-6 py-16 text-center", children: [
|
|
362
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(circle_at_center,hsl(var(--muted)/0.8),transparent_70%)]" }),
|
|
363
|
+
/* @__PURE__ */ jsxs("div", { className: "relative space-y-6", children: [
|
|
364
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border bg-muted", children: /* @__PURE__ */ jsx(Compass, { className: "size-8 text-muted-foreground" }) }) }),
|
|
365
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
366
|
+
/* @__PURE__ */ jsx("div", { className: "text-8xl font-bold tracking-tight text-muted-foreground/20", children: statusCode }),
|
|
367
|
+
/* @__PURE__ */ jsx("h1", { className: "-mt-4 text-2xl font-semibold tracking-tight", children: title }),
|
|
368
|
+
/* @__PURE__ */ jsx("p", { className: "mx-auto max-w-sm text-sm text-muted-foreground", children: description })
|
|
369
|
+
] }),
|
|
370
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3", children: [
|
|
371
|
+
backAction && (backAction.href ? renderLink({
|
|
372
|
+
href: backAction.href,
|
|
373
|
+
children: /* @__PURE__ */ jsxs(Button, { variant: "outline", children: [
|
|
374
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "mr-2 size-4" }),
|
|
375
|
+
backAction.label
|
|
376
|
+
] })
|
|
377
|
+
}) : /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: backAction.onClick, children: [
|
|
378
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "mr-2 size-4" }),
|
|
379
|
+
backAction.label
|
|
380
|
+
] })),
|
|
381
|
+
action && renderLink({
|
|
382
|
+
href: action.href,
|
|
383
|
+
children: /* @__PURE__ */ jsx(Button, { children: action.label })
|
|
384
|
+
})
|
|
385
|
+
] })
|
|
386
|
+
] })
|
|
387
|
+
] }) });
|
|
388
|
+
}
|
|
389
|
+
function UpgradePrompt({
|
|
390
|
+
variant,
|
|
391
|
+
title,
|
|
392
|
+
description,
|
|
393
|
+
primaryActionLabel,
|
|
394
|
+
onPrimaryAction,
|
|
395
|
+
secondaryActionLabel,
|
|
396
|
+
onSecondaryAction,
|
|
397
|
+
eyebrow,
|
|
398
|
+
rows,
|
|
399
|
+
currentPlanLabel = "Current",
|
|
400
|
+
upgradedPlanLabel = "Upgrade",
|
|
401
|
+
footer,
|
|
402
|
+
onClose,
|
|
403
|
+
closeLabel = "Close",
|
|
404
|
+
className
|
|
405
|
+
}) {
|
|
406
|
+
const hasSecondaryAction = Boolean(secondaryActionLabel && onSecondaryAction);
|
|
407
|
+
if (variant === "banner") {
|
|
408
|
+
return /* @__PURE__ */ jsx(
|
|
409
|
+
"section",
|
|
410
|
+
{
|
|
411
|
+
className: cn$1(
|
|
412
|
+
"rounded-xl border border-dashed bg-card/40 p-5 sm:p-6",
|
|
413
|
+
className
|
|
414
|
+
),
|
|
415
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
416
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
417
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold", children: title }),
|
|
418
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 max-w-xl text-sm leading-6 text-muted-foreground", children: description })
|
|
419
|
+
] }),
|
|
420
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
421
|
+
hasSecondaryAction ? /* @__PURE__ */ jsx(
|
|
422
|
+
Button$1,
|
|
423
|
+
{
|
|
424
|
+
type: "button",
|
|
425
|
+
variant: "ghost",
|
|
426
|
+
onClick: onSecondaryAction,
|
|
427
|
+
className: "border",
|
|
428
|
+
children: secondaryActionLabel
|
|
429
|
+
}
|
|
430
|
+
) : null,
|
|
431
|
+
/* @__PURE__ */ jsx(Button$1, { type: "button", onClick: onPrimaryAction, children: primaryActionLabel })
|
|
432
|
+
] })
|
|
433
|
+
] })
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
return /* @__PURE__ */ jsxs(
|
|
438
|
+
"section",
|
|
439
|
+
{
|
|
440
|
+
className: cn$1(
|
|
441
|
+
"w-full max-w-3xl rounded-2xl border bg-card p-6 shadow-sm sm:p-7",
|
|
442
|
+
className
|
|
443
|
+
),
|
|
444
|
+
children: [
|
|
445
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
446
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
447
|
+
eyebrow ? /* @__PURE__ */ jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-primary", children: eyebrow }) : null,
|
|
448
|
+
/* @__PURE__ */ jsx(
|
|
449
|
+
"h3",
|
|
450
|
+
{
|
|
451
|
+
className: cn$1(
|
|
452
|
+
"text-2xl font-semibold sm:text-[1.75rem]",
|
|
453
|
+
eyebrow ? "mt-3" : ""
|
|
454
|
+
),
|
|
455
|
+
children: title
|
|
456
|
+
}
|
|
457
|
+
),
|
|
458
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 max-w-2xl text-sm leading-6 text-muted-foreground sm:text-base", children: description })
|
|
459
|
+
] }),
|
|
460
|
+
onClose ? /* @__PURE__ */ jsx(
|
|
461
|
+
"button",
|
|
462
|
+
{
|
|
463
|
+
type: "button",
|
|
464
|
+
onClick: onClose,
|
|
465
|
+
className: "inline-flex size-9 shrink-0 items-center justify-center rounded-lg border transition-colors hover:bg-muted",
|
|
466
|
+
"aria-label": closeLabel,
|
|
467
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
468
|
+
}
|
|
469
|
+
) : null
|
|
470
|
+
] }),
|
|
471
|
+
rows && rows.length > 0 ? /* @__PURE__ */ jsx("div", { className: "mt-6 overflow-hidden rounded-xl border bg-muted/30", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
472
|
+
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
473
|
+
/* @__PURE__ */ jsx(TableHead, {}),
|
|
474
|
+
/* @__PURE__ */ jsx(TableHead, { className: "font-mono text-xs uppercase tracking-wider text-muted-foreground", children: currentPlanLabel }),
|
|
475
|
+
/* @__PURE__ */ jsx(TableHead, { className: "font-mono text-xs uppercase tracking-wider text-primary", children: upgradedPlanLabel })
|
|
476
|
+
] }) }),
|
|
477
|
+
/* @__PURE__ */ jsx(TableBody, { children: rows.map((row, index) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
478
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-medium", children: row.label }),
|
|
479
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-muted-foreground", children: row.currentValue }),
|
|
480
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono", children: row.upgradedValue })
|
|
481
|
+
] }, row.key ?? index)) })
|
|
482
|
+
] }) }) : null,
|
|
483
|
+
/* @__PURE__ */ jsxs(
|
|
484
|
+
"div",
|
|
485
|
+
{
|
|
486
|
+
className: cn$1(
|
|
487
|
+
"mt-6 flex flex-col gap-3 sm:flex-row sm:items-center",
|
|
488
|
+
footer || hasSecondaryAction ? "sm:justify-between" : "sm:justify-end"
|
|
489
|
+
),
|
|
490
|
+
children: [
|
|
491
|
+
footer ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: footer }) : null,
|
|
492
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
493
|
+
hasSecondaryAction ? /* @__PURE__ */ jsx(
|
|
494
|
+
Button$1,
|
|
495
|
+
{
|
|
496
|
+
type: "button",
|
|
497
|
+
variant: "ghost",
|
|
498
|
+
onClick: onSecondaryAction,
|
|
499
|
+
className: "border",
|
|
500
|
+
children: secondaryActionLabel
|
|
501
|
+
}
|
|
502
|
+
) : null,
|
|
503
|
+
/* @__PURE__ */ jsx(Button$1, { type: "button", onClick: onPrimaryAction, children: primaryActionLabel })
|
|
504
|
+
] })
|
|
505
|
+
]
|
|
506
|
+
}
|
|
507
|
+
)
|
|
508
|
+
]
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
var translateMap = {
|
|
513
|
+
up: "translate-y-6",
|
|
514
|
+
down: "-translate-y-6",
|
|
515
|
+
left: "translate-x-6",
|
|
516
|
+
right: "-translate-x-6",
|
|
517
|
+
none: ""
|
|
518
|
+
};
|
|
519
|
+
function FadeIn({
|
|
520
|
+
delay = 0,
|
|
521
|
+
duration = 600,
|
|
522
|
+
direction = "up",
|
|
523
|
+
once = true,
|
|
524
|
+
children,
|
|
525
|
+
className,
|
|
526
|
+
style,
|
|
527
|
+
...props
|
|
528
|
+
}) {
|
|
529
|
+
const ref = React25.useRef(null);
|
|
530
|
+
const [visible, setVisible] = React25.useState(false);
|
|
531
|
+
React25.useEffect(() => {
|
|
532
|
+
const el = ref.current;
|
|
533
|
+
if (!el) return;
|
|
534
|
+
const observer = new IntersectionObserver(
|
|
535
|
+
([entry]) => {
|
|
536
|
+
if (entry.isIntersecting) {
|
|
537
|
+
setVisible(true);
|
|
538
|
+
if (once) observer.disconnect();
|
|
539
|
+
} else if (!once) {
|
|
540
|
+
setVisible(false);
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
{ threshold: 0.1 }
|
|
544
|
+
);
|
|
545
|
+
observer.observe(el);
|
|
546
|
+
return () => observer.disconnect();
|
|
547
|
+
}, [once]);
|
|
548
|
+
return /* @__PURE__ */ jsx(
|
|
549
|
+
"div",
|
|
550
|
+
{
|
|
551
|
+
ref,
|
|
552
|
+
className: cn$1(
|
|
553
|
+
"transition-all",
|
|
554
|
+
visible ? "opacity-100 translate-x-0 translate-y-0" : `opacity-0 ${translateMap[direction]}`,
|
|
555
|
+
className
|
|
556
|
+
),
|
|
557
|
+
style: { transitionDuration: `${duration}ms`, transitionDelay: `${delay}ms`, ...style },
|
|
558
|
+
...props,
|
|
559
|
+
children
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
function SectionTitle({ title, description, badge, className }) {
|
|
564
|
+
return /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: cn$1("text-center", className), children: [
|
|
565
|
+
badge && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
|
|
566
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
567
|
+
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-2xl text-pretty text-muted-foreground", children: description })
|
|
568
|
+
] }) });
|
|
569
|
+
}
|
|
570
|
+
var defaultRenderLink3 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
571
|
+
function HeroSection({
|
|
572
|
+
heading,
|
|
573
|
+
badge,
|
|
574
|
+
description,
|
|
575
|
+
primaryCta,
|
|
576
|
+
secondaryCta,
|
|
577
|
+
heroImage,
|
|
578
|
+
avatarUrls,
|
|
579
|
+
socialProofText,
|
|
580
|
+
renderLink = defaultRenderLink3
|
|
581
|
+
}) {
|
|
582
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
583
|
+
/* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-2 lg:gap-16", children: [
|
|
584
|
+
/* @__PURE__ */ jsx(FadeIn, { direction: "left", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
585
|
+
badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge }) }),
|
|
586
|
+
/* @__PURE__ */ jsx("h1", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl", children: heading }),
|
|
587
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-lg text-pretty text-lg text-muted-foreground", children: description }),
|
|
588
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
589
|
+
renderLink({
|
|
590
|
+
href: primaryCta.href,
|
|
591
|
+
children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
|
|
592
|
+
primaryCta.label,
|
|
593
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
594
|
+
] })
|
|
595
|
+
}),
|
|
596
|
+
secondaryCta && renderLink({
|
|
597
|
+
href: secondaryCta.href,
|
|
598
|
+
children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
599
|
+
})
|
|
600
|
+
] })
|
|
601
|
+
] }) }),
|
|
602
|
+
/* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-2xl", children: heroImage }) })
|
|
603
|
+
] }),
|
|
604
|
+
(avatarUrls?.length || socialProofText) && /* @__PURE__ */ jsx(FadeIn, { direction: "up", delay: 400, children: /* @__PURE__ */ jsxs("div", { className: "mx-auto mt-12 flex max-w-6xl items-center gap-4", children: [
|
|
605
|
+
avatarUrls && avatarUrls.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex -space-x-3", children: avatarUrls.map((url, i) => /* @__PURE__ */ jsxs(Avatar, { className: "size-9 border-2 border-background", children: [
|
|
606
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: url, alt: "" }),
|
|
607
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: String.fromCharCode(65 + i % 26) })
|
|
608
|
+
] }, i)) }),
|
|
609
|
+
socialProofText && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: socialProofText })
|
|
610
|
+
] }) })
|
|
611
|
+
] }) });
|
|
612
|
+
}
|
|
613
|
+
function FeatureCard({ icon: Icon, title, description, href, className, ...props }) {
|
|
614
|
+
const content = /* @__PURE__ */ jsxs(
|
|
615
|
+
"div",
|
|
616
|
+
{
|
|
617
|
+
className: cn$1(
|
|
618
|
+
"group relative flex flex-col gap-4 rounded-2xl border bg-card p-6 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md",
|
|
619
|
+
href && "cursor-pointer",
|
|
620
|
+
className
|
|
621
|
+
),
|
|
622
|
+
...props,
|
|
623
|
+
children: [
|
|
624
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-primary/10 text-primary transition-colors group-hover:bg-primary group-hover:text-primary-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-5" }) }),
|
|
625
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
626
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
627
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold leading-tight", children: title }),
|
|
628
|
+
href && /* @__PURE__ */ jsx(ArrowRight, { className: "size-4 shrink-0 text-muted-foreground opacity-0 transition-all group-hover:translate-x-0.5 group-hover:opacity-100" })
|
|
629
|
+
] }),
|
|
630
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed text-muted-foreground", children: description })
|
|
631
|
+
] })
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
if (href) {
|
|
636
|
+
return /* @__PURE__ */ jsx("a", { href, className: "block", children: content });
|
|
637
|
+
}
|
|
638
|
+
return content;
|
|
639
|
+
}
|
|
640
|
+
var translateMap2 = {
|
|
641
|
+
up: [0, 20],
|
|
642
|
+
down: [0, -20],
|
|
643
|
+
left: [20, 0],
|
|
644
|
+
right: [-20, 0],
|
|
645
|
+
none: [0, 0]
|
|
646
|
+
};
|
|
647
|
+
function StaggerChildren({
|
|
648
|
+
stagger = 80,
|
|
649
|
+
delay = 0,
|
|
650
|
+
duration = 500,
|
|
651
|
+
direction = "up",
|
|
652
|
+
once = true,
|
|
653
|
+
children,
|
|
654
|
+
className,
|
|
655
|
+
...props
|
|
656
|
+
}) {
|
|
657
|
+
const ref = React25.useRef(null);
|
|
658
|
+
const [visible, setVisible] = React25.useState(false);
|
|
659
|
+
React25.useEffect(() => {
|
|
660
|
+
const el = ref.current;
|
|
661
|
+
if (!el) return;
|
|
662
|
+
const observer = new IntersectionObserver(
|
|
663
|
+
([entry]) => {
|
|
664
|
+
if (entry.isIntersecting) {
|
|
665
|
+
setVisible(true);
|
|
666
|
+
if (once) observer.disconnect();
|
|
667
|
+
} else if (!once) {
|
|
668
|
+
setVisible(false);
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
{ threshold: 0.1 }
|
|
672
|
+
);
|
|
673
|
+
observer.observe(el);
|
|
674
|
+
return () => observer.disconnect();
|
|
675
|
+
}, [once]);
|
|
676
|
+
const [tx, ty] = translateMap2[direction];
|
|
677
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React25.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
|
|
678
|
+
"div",
|
|
679
|
+
{
|
|
680
|
+
style: {
|
|
681
|
+
transition: `opacity ${duration}ms ease, transform ${duration}ms ease`,
|
|
682
|
+
transitionDelay: `${delay + i * stagger}ms`,
|
|
683
|
+
opacity: visible ? 1 : 0,
|
|
684
|
+
transform: visible ? "translate(0,0)" : `translate(${tx}px,${ty}px)`
|
|
685
|
+
},
|
|
686
|
+
children: child
|
|
687
|
+
}
|
|
688
|
+
)) });
|
|
689
|
+
}
|
|
690
|
+
function FeaturesSection({
|
|
691
|
+
title = "Everything you need to ship fast",
|
|
692
|
+
description = "A complete system of components, blocks, and hooks designed to work together - and get out of your way.",
|
|
693
|
+
badge = "Features",
|
|
694
|
+
features
|
|
695
|
+
}) {
|
|
696
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl", children: [
|
|
697
|
+
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
698
|
+
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: features.map((feature) => /* @__PURE__ */ jsx(
|
|
699
|
+
FeatureCard,
|
|
700
|
+
{
|
|
701
|
+
icon: feature.icon,
|
|
702
|
+
title: feature.title,
|
|
703
|
+
description: feature.description,
|
|
704
|
+
href: feature.href
|
|
705
|
+
},
|
|
706
|
+
feature.title
|
|
707
|
+
)) })
|
|
708
|
+
] }) }) });
|
|
709
|
+
}
|
|
710
|
+
var defaultRenderLink4 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
711
|
+
function CtaSection({
|
|
712
|
+
heading,
|
|
713
|
+
description,
|
|
714
|
+
primaryCta,
|
|
715
|
+
secondaryCta,
|
|
716
|
+
footnote,
|
|
717
|
+
renderLink = defaultRenderLink4
|
|
718
|
+
}) {
|
|
719
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
|
|
720
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
|
|
721
|
+
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
|
|
722
|
+
/* @__PURE__ */ jsx("div", { className: "mb-4 inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
|
|
723
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
|
|
724
|
+
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
725
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
|
|
726
|
+
renderLink({
|
|
727
|
+
href: primaryCta.href,
|
|
728
|
+
children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
|
|
729
|
+
primaryCta.label,
|
|
730
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
731
|
+
] })
|
|
732
|
+
}),
|
|
733
|
+
secondaryCta && renderLink({
|
|
734
|
+
href: secondaryCta.href,
|
|
735
|
+
children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
736
|
+
})
|
|
737
|
+
] }),
|
|
738
|
+
footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
|
|
739
|
+
] }) })
|
|
740
|
+
] }) });
|
|
741
|
+
}
|
|
742
|
+
function PricingCard({
|
|
743
|
+
name,
|
|
744
|
+
price,
|
|
745
|
+
period = "/mo",
|
|
746
|
+
description,
|
|
747
|
+
features,
|
|
748
|
+
cta,
|
|
749
|
+
highlighted = false,
|
|
750
|
+
badge,
|
|
751
|
+
onSelect,
|
|
752
|
+
className,
|
|
753
|
+
...props
|
|
754
|
+
}) {
|
|
755
|
+
return /* @__PURE__ */ jsxs(
|
|
756
|
+
"div",
|
|
757
|
+
{
|
|
758
|
+
className: cn$1(
|
|
759
|
+
"relative flex flex-col rounded-2xl border p-6",
|
|
760
|
+
highlighted ? "border-primary bg-primary/5 shadow-sm" : "bg-card",
|
|
761
|
+
className
|
|
762
|
+
),
|
|
763
|
+
...props,
|
|
764
|
+
children: [
|
|
765
|
+
badge && /* @__PURE__ */ jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx(Badge$1, { children: badge }) }),
|
|
766
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
767
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: name }),
|
|
768
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1", children: [
|
|
769
|
+
/* @__PURE__ */ jsx("span", { className: "text-4xl font-bold tracking-tight", children: price }),
|
|
770
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1 text-sm text-muted-foreground", children: period })
|
|
771
|
+
] }),
|
|
772
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
|
|
773
|
+
] }),
|
|
774
|
+
/* @__PURE__ */ jsx(
|
|
775
|
+
Button$1,
|
|
776
|
+
{
|
|
777
|
+
variant: highlighted ? "default" : "outline",
|
|
778
|
+
className: "mt-6 w-full",
|
|
779
|
+
onClick: onSelect,
|
|
780
|
+
children: cta
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
/* @__PURE__ */ jsx(Separator, { className: "my-6" }),
|
|
784
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-3 text-sm", children: features.map((feat) => /* @__PURE__ */ jsx("li", { className: "flex items-center gap-2", children: feat.included ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
785
|
+
/* @__PURE__ */ jsx(Check, { className: "size-4 shrink-0 text-primary" }),
|
|
786
|
+
/* @__PURE__ */ jsx("span", { children: feat.label })
|
|
787
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
788
|
+
/* @__PURE__ */ jsx(Minus, { className: "size-4 shrink-0 text-muted-foreground/40" }),
|
|
789
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: feat.label })
|
|
790
|
+
] }) }, feat.label)) })
|
|
791
|
+
]
|
|
792
|
+
}
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
796
|
+
function PricingSection({
|
|
797
|
+
title = "Simple, transparent pricing",
|
|
798
|
+
description = "Start for free. Scale as you grow. No hidden fees.",
|
|
799
|
+
badge = "Pricing",
|
|
800
|
+
plans,
|
|
801
|
+
saveBadge = "Save 34%",
|
|
802
|
+
isAuthenticated,
|
|
803
|
+
renderLink = defaultRenderLink5,
|
|
804
|
+
footnote
|
|
805
|
+
}) {
|
|
806
|
+
const [annual, setAnnual] = React25.useState(false);
|
|
807
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
|
|
808
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
809
|
+
badge && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
|
|
810
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
811
|
+
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description }),
|
|
812
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 inline-flex items-center gap-3 rounded-full border bg-muted p-1", children: [
|
|
813
|
+
/* @__PURE__ */ jsx(
|
|
814
|
+
"button",
|
|
815
|
+
{
|
|
816
|
+
onClick: () => setAnnual(false),
|
|
817
|
+
className: cn$1(
|
|
818
|
+
"rounded-full px-4 py-1.5 text-sm font-medium transition-colors",
|
|
819
|
+
!annual ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
|
|
820
|
+
),
|
|
821
|
+
children: "Monthly"
|
|
822
|
+
}
|
|
823
|
+
),
|
|
824
|
+
/* @__PURE__ */ jsxs(
|
|
825
|
+
"button",
|
|
826
|
+
{
|
|
827
|
+
onClick: () => setAnnual(true),
|
|
828
|
+
className: cn$1(
|
|
829
|
+
"flex items-center gap-2 rounded-full px-4 py-1.5 text-sm font-medium transition-colors",
|
|
830
|
+
annual ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
|
|
831
|
+
),
|
|
832
|
+
children: [
|
|
833
|
+
"Annual",
|
|
834
|
+
saveBadge && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "text-xs", children: saveBadge })
|
|
835
|
+
]
|
|
836
|
+
}
|
|
837
|
+
)
|
|
838
|
+
] })
|
|
839
|
+
] }),
|
|
840
|
+
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-10 grid gap-4 lg:grid-cols-3", children: plans.map((plan) => {
|
|
841
|
+
const price = annual ? plan.annual : plan.monthly;
|
|
842
|
+
return /* @__PURE__ */ jsx(
|
|
843
|
+
PricingCard,
|
|
844
|
+
{
|
|
845
|
+
name: plan.name,
|
|
846
|
+
price: `$${price}`,
|
|
847
|
+
period: price > 0 ? "/mo" : "",
|
|
848
|
+
description: plan.description,
|
|
849
|
+
features: plan.features,
|
|
850
|
+
cta: plan.cta,
|
|
851
|
+
highlighted: plan.highlighted,
|
|
852
|
+
badge: plan.highlighted ? "Most popular" : void 0
|
|
853
|
+
},
|
|
854
|
+
plan.name
|
|
855
|
+
);
|
|
856
|
+
}) }),
|
|
857
|
+
footnote && /* @__PURE__ */ jsx("p", { className: "mt-8 text-center text-sm text-muted-foreground", children: footnote })
|
|
858
|
+
] }) }) });
|
|
859
|
+
}
|
|
860
|
+
function TestimonialCard({
|
|
861
|
+
quote,
|
|
862
|
+
name,
|
|
863
|
+
role,
|
|
864
|
+
company,
|
|
865
|
+
avatar,
|
|
866
|
+
initials,
|
|
867
|
+
rating,
|
|
868
|
+
className,
|
|
869
|
+
...props
|
|
870
|
+
}) {
|
|
871
|
+
return /* @__PURE__ */ jsxs(
|
|
872
|
+
"div",
|
|
873
|
+
{
|
|
874
|
+
className: cn$1("flex flex-col gap-4 rounded-2xl border bg-card p-6", className),
|
|
875
|
+
...props,
|
|
876
|
+
children: [
|
|
877
|
+
/* @__PURE__ */ jsx(Quote, { className: "size-4 text-muted-foreground/40" }),
|
|
878
|
+
rating != null && /* @__PURE__ */ jsx("div", { className: "flex gap-0.5", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx(
|
|
879
|
+
Star,
|
|
880
|
+
{
|
|
881
|
+
className: cn$1(
|
|
882
|
+
"size-4",
|
|
883
|
+
i < rating ? "fill-current text-amber-400" : "text-muted-foreground/30"
|
|
884
|
+
)
|
|
885
|
+
},
|
|
886
|
+
i
|
|
887
|
+
)) }),
|
|
888
|
+
/* @__PURE__ */ jsx("p", { className: "flex-1 text-sm leading-relaxed", children: quote }),
|
|
889
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
890
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-9 rounded-xl", children: [
|
|
891
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: name }),
|
|
892
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "rounded-xl text-xs", children: initials ?? name.slice(0, 2).toUpperCase() })
|
|
893
|
+
] }),
|
|
894
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
895
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium leading-tight", children: name }),
|
|
896
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
897
|
+
role,
|
|
898
|
+
company ? ` \xB7 ${company}` : ""
|
|
899
|
+
] })
|
|
900
|
+
] })
|
|
901
|
+
] })
|
|
902
|
+
]
|
|
903
|
+
}
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
function TestimonialsSection({
|
|
907
|
+
title = "Trusted by teams shipping real products",
|
|
908
|
+
description = "Here's what engineers and product teams say after using Olwiba in production.",
|
|
909
|
+
badge = "Testimonials",
|
|
910
|
+
testimonials
|
|
911
|
+
}) {
|
|
912
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
|
|
913
|
+
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
914
|
+
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 columns-1 gap-4 sm:columns-2 lg:columns-3", children: testimonials.map((t) => /* @__PURE__ */ jsx(
|
|
915
|
+
"div",
|
|
916
|
+
{
|
|
917
|
+
className: "mb-4 break-inside-avoid transition-transform hover:-translate-y-0.5",
|
|
918
|
+
children: /* @__PURE__ */ jsx(
|
|
919
|
+
TestimonialCard,
|
|
920
|
+
{
|
|
921
|
+
quote: t.quote,
|
|
922
|
+
name: t.name,
|
|
923
|
+
role: t.role,
|
|
924
|
+
company: t.company,
|
|
925
|
+
avatar: t.avatar,
|
|
926
|
+
initials: t.initials,
|
|
927
|
+
rating: t.rating
|
|
928
|
+
}
|
|
929
|
+
)
|
|
930
|
+
},
|
|
931
|
+
t.name
|
|
932
|
+
)) })
|
|
933
|
+
] }) }) });
|
|
934
|
+
}
|
|
935
|
+
var team = [
|
|
936
|
+
{
|
|
937
|
+
name: "Olivia Reed",
|
|
938
|
+
role: "Co-Founder & CEO",
|
|
939
|
+
bio: "Previously led product at two YC companies. Obsessed with design systems and developer experience.",
|
|
940
|
+
avatar: "https://ui.shadcn.com/avatars/01.png",
|
|
941
|
+
initials: "OR",
|
|
942
|
+
social: { twitter: "#", github: "#", linkedin: "#" }
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: "Marcus Webb",
|
|
946
|
+
role: "Co-Founder & CTO",
|
|
947
|
+
bio: "Full-stack engineer with 12 years building UI infrastructure. Open source maintainer.",
|
|
948
|
+
avatar: "https://ui.shadcn.com/avatars/02.png",
|
|
949
|
+
initials: "MW",
|
|
950
|
+
social: { twitter: "#", github: "#", linkedin: "#" }
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
name: "Priya Nair",
|
|
954
|
+
role: "Head of Design",
|
|
955
|
+
bio: "Interaction designer turned design systems engineer. Bridges the gap between Figma and production.",
|
|
956
|
+
avatar: "https://ui.shadcn.com/avatars/03.png",
|
|
957
|
+
initials: "PN",
|
|
958
|
+
social: { twitter: "#", github: null, linkedin: "#" }
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
name: "Daniel Frost",
|
|
962
|
+
role: "Senior Engineer",
|
|
963
|
+
bio: "Accessibility and performance specialist. If it doesn't work with a keyboard, it's broken.",
|
|
964
|
+
avatar: "https://ui.shadcn.com/avatars/04.png",
|
|
965
|
+
initials: "DF",
|
|
966
|
+
social: { twitter: null, github: "#", linkedin: "#" }
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
name: "Aisha Okafor",
|
|
970
|
+
role: "Developer Relations",
|
|
971
|
+
bio: "Writes docs, builds examples, and makes sure every engineer can ship their first block in under an hour.",
|
|
972
|
+
avatar: "https://ui.shadcn.com/avatars/05.png",
|
|
973
|
+
initials: "AO",
|
|
974
|
+
social: { twitter: "#", github: "#", linkedin: "#" }
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
name: "Tom Halvorsen",
|
|
978
|
+
role: "Growth",
|
|
979
|
+
bio: "Turns happy users into loud advocates. Runs the community and keeps the changelog sharp.",
|
|
980
|
+
avatar: "https://ui.shadcn.com/avatars/06.png",
|
|
981
|
+
initials: "TH",
|
|
982
|
+
social: { twitter: "#", github: null, linkedin: "#" }
|
|
983
|
+
}
|
|
984
|
+
];
|
|
985
|
+
function TeamSection() {
|
|
986
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
|
|
987
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
988
|
+
/* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: "Team" }),
|
|
989
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: "The people behind Olwiba" }),
|
|
990
|
+
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: "A small team with deep experience in design systems, open source, and developer tooling." })
|
|
991
|
+
] }),
|
|
992
|
+
/* @__PURE__ */ jsx("div", { className: "mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3", children: team.map((member) => /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border bg-muted/40 p-5", children: [
|
|
993
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
|
|
994
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-12 rounded-xl", children: [
|
|
995
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: member.avatar, alt: member.name }),
|
|
996
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "rounded-xl", children: member.initials })
|
|
997
|
+
] }),
|
|
998
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
999
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold leading-tight", children: member.name }),
|
|
1000
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: member.role })
|
|
1001
|
+
] })
|
|
1002
|
+
] }),
|
|
1003
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-sm leading-relaxed text-muted-foreground", children: member.bio }),
|
|
1004
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2", children: [
|
|
1005
|
+
member.social.twitter && /* @__PURE__ */ jsx("a", { href: member.social.twitter, "aria-label": "Twitter", className: "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground", children: /* @__PURE__ */ jsx(Twitter, { className: "size-3.5" }) }),
|
|
1006
|
+
member.social.github && /* @__PURE__ */ jsx("a", { href: member.social.github, "aria-label": "GitHub", className: "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground", children: /* @__PURE__ */ jsx(Github, { className: "size-3.5" }) }),
|
|
1007
|
+
member.social.linkedin && /* @__PURE__ */ jsx("a", { href: member.social.linkedin, "aria-label": "LinkedIn", className: "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground", children: /* @__PURE__ */ jsx(Linkedin, { className: "size-3.5" }) })
|
|
1008
|
+
] })
|
|
1009
|
+
] }, member.name)) })
|
|
1010
|
+
] }) }) });
|
|
1011
|
+
}
|
|
1012
|
+
function FaqSection({
|
|
1013
|
+
title = "Frequently asked questions",
|
|
1014
|
+
description = "Everything you need to know before getting started.",
|
|
1015
|
+
badge = "FAQ",
|
|
1016
|
+
items
|
|
1017
|
+
}) {
|
|
1018
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl", children: [
|
|
1019
|
+
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1020
|
+
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "mt-10 w-full", children: items.map((item, i) => /* @__PURE__ */ jsxs(AccordionItem, { value: `item-${i}`, children: [
|
|
1021
|
+
/* @__PURE__ */ jsx(AccordionTrigger, { className: "text-left text-sm font-medium", children: item.question }),
|
|
1022
|
+
/* @__PURE__ */ jsx(AccordionContent, { className: "text-sm text-muted-foreground", children: item.answer })
|
|
1023
|
+
] }, i)) }) })
|
|
1024
|
+
] }) }) });
|
|
1025
|
+
}
|
|
1026
|
+
function CountUp({
|
|
1027
|
+
from = 0,
|
|
1028
|
+
to,
|
|
1029
|
+
duration = 1500,
|
|
1030
|
+
decimals = 0,
|
|
1031
|
+
prefix = "",
|
|
1032
|
+
suffix = "",
|
|
1033
|
+
once = true,
|
|
1034
|
+
className,
|
|
1035
|
+
...props
|
|
1036
|
+
}) {
|
|
1037
|
+
const ref = React25.useRef(null);
|
|
1038
|
+
const [value, setValue] = React25.useState(from);
|
|
1039
|
+
const hasStarted = React25.useRef(false);
|
|
1040
|
+
React25.useEffect(() => {
|
|
1041
|
+
const el = ref.current;
|
|
1042
|
+
if (!el) return;
|
|
1043
|
+
const observer = new IntersectionObserver(
|
|
1044
|
+
([entry]) => {
|
|
1045
|
+
if (entry.isIntersecting && (!once || !hasStarted.current)) {
|
|
1046
|
+
hasStarted.current = true;
|
|
1047
|
+
const start = performance.now();
|
|
1048
|
+
const tick = (now) => {
|
|
1049
|
+
const elapsed = now - start;
|
|
1050
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
1051
|
+
const eased = 1 - Math.pow(1 - progress, 3);
|
|
1052
|
+
setValue(from + (to - from) * eased);
|
|
1053
|
+
if (progress < 1) requestAnimationFrame(tick);
|
|
1054
|
+
};
|
|
1055
|
+
requestAnimationFrame(tick);
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
{ threshold: 0.5 }
|
|
1059
|
+
);
|
|
1060
|
+
observer.observe(el);
|
|
1061
|
+
return () => observer.disconnect();
|
|
1062
|
+
}, [from, to, duration, once]);
|
|
1063
|
+
return /* @__PURE__ */ jsxs("span", { ref, className: cn$1(className), ...props, children: [
|
|
1064
|
+
prefix,
|
|
1065
|
+
value.toFixed(decimals),
|
|
1066
|
+
suffix
|
|
1067
|
+
] });
|
|
1068
|
+
}
|
|
1069
|
+
function parseStatValue(value) {
|
|
1070
|
+
const match = value.match(/^(\d+(?:\.\d+)?)(.*)/);
|
|
1071
|
+
if (!match) return null;
|
|
1072
|
+
const num = parseFloat(match[1]);
|
|
1073
|
+
const suffix = match[2] || "";
|
|
1074
|
+
const decimals = match[1].includes(".") ? match[1].split(".")[1].length : 0;
|
|
1075
|
+
return { num, suffix, decimals };
|
|
1076
|
+
}
|
|
1077
|
+
function StatValue({ value }) {
|
|
1078
|
+
const parsed = parseStatValue(value);
|
|
1079
|
+
if (!parsed) return /* @__PURE__ */ jsx(Fragment, { children: value });
|
|
1080
|
+
return /* @__PURE__ */ jsx(CountUp, { to: parsed.num, decimals: parsed.decimals, suffix: parsed.suffix });
|
|
1081
|
+
}
|
|
1082
|
+
function StatsSection({
|
|
1083
|
+
title = "Built for scale, used in production",
|
|
1084
|
+
description,
|
|
1085
|
+
badge = "By the numbers",
|
|
1086
|
+
stats
|
|
1087
|
+
}) {
|
|
1088
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
1089
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,hsl(var(--primary)/0.08),transparent_60%)]" }),
|
|
1090
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-4xl", children: [
|
|
1091
|
+
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1092
|
+
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-px overflow-hidden rounded-2xl border bg-border sm:grid-cols-2 lg:grid-cols-4", children: stats.map((stat) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 bg-card px-6 py-8", children: [
|
|
1093
|
+
/* @__PURE__ */ jsx("div", { className: "text-4xl font-bold tracking-tight", children: /* @__PURE__ */ jsx(StatValue, { value: stat.value }) }),
|
|
1094
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium", children: stat.label }),
|
|
1095
|
+
stat.description && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1096
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
1097
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: stat.description })
|
|
1098
|
+
] })
|
|
1099
|
+
] }, stat.label)) })
|
|
1100
|
+
] })
|
|
1101
|
+
] }) });
|
|
1102
|
+
}
|
|
1103
|
+
function NewsletterSection() {
|
|
1104
|
+
const [email, setEmail] = React25.useState("");
|
|
1105
|
+
const [submitted, setSubmitted] = React25.useState(false);
|
|
1106
|
+
function handleSubmit(e) {
|
|
1107
|
+
e.preventDefault();
|
|
1108
|
+
if (!email) return;
|
|
1109
|
+
setSubmitted(true);
|
|
1110
|
+
}
|
|
1111
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
1112
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.1),transparent_60%)]" }),
|
|
1113
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-xl text-center", children: [
|
|
1114
|
+
/* @__PURE__ */ jsx("div", { className: "mb-4 inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Mail, { className: "size-5" }) }),
|
|
1115
|
+
/* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-3", children: "Newsletter" }),
|
|
1116
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: "Stay in the loop" }),
|
|
1117
|
+
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-sm text-pretty text-muted-foreground", children: "New components, release notes, and product tips \u2014 straight to your inbox. No spam, unsubscribe any time." }),
|
|
1118
|
+
submitted ? /* @__PURE__ */ jsxs("div", { className: "mt-8 rounded-2xl border bg-muted/50 px-6 py-5 text-sm", children: [
|
|
1119
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "You're on the list." }),
|
|
1120
|
+
" ",
|
|
1121
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "We'll be in touch soon." })
|
|
1122
|
+
] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "mt-8 flex flex-col gap-3 sm:flex-row", children: [
|
|
1123
|
+
/* @__PURE__ */ jsx(
|
|
1124
|
+
Input$1,
|
|
1125
|
+
{
|
|
1126
|
+
type: "email",
|
|
1127
|
+
placeholder: "you@company.com",
|
|
1128
|
+
value: email,
|
|
1129
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1130
|
+
required: true,
|
|
1131
|
+
className: "flex-1"
|
|
1132
|
+
}
|
|
1133
|
+
),
|
|
1134
|
+
/* @__PURE__ */ jsxs(Button$1, { type: "submit", children: [
|
|
1135
|
+
"Subscribe",
|
|
1136
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1137
|
+
] })
|
|
1138
|
+
] }),
|
|
1139
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-4 text-xs text-muted-foreground", children: [
|
|
1140
|
+
"By subscribing you agree to our",
|
|
1141
|
+
" ",
|
|
1142
|
+
/* @__PURE__ */ jsx("a", { href: "#", className: "underline underline-offset-4 hover:text-foreground", children: "Privacy Policy" }),
|
|
1143
|
+
"."
|
|
1144
|
+
] })
|
|
1145
|
+
] })
|
|
1146
|
+
] }) });
|
|
1147
|
+
}
|
|
1148
|
+
var contactInfo = [
|
|
1149
|
+
{ Icon: Mail, label: "Email", value: "hello@olwiba.com" },
|
|
1150
|
+
{ Icon: Phone, label: "Phone", value: "+1 (555) 000-0000" },
|
|
1151
|
+
{ Icon: MapPin, label: "Office", value: "San Francisco, CA" }
|
|
1152
|
+
];
|
|
1153
|
+
function ContactSection() {
|
|
1154
|
+
const [submitted, setSubmitted] = React25.useState(false);
|
|
1155
|
+
function handleSubmit(e) {
|
|
1156
|
+
e.preventDefault();
|
|
1157
|
+
setSubmitted(true);
|
|
1158
|
+
}
|
|
1159
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "grid lg:grid-cols-[1fr_1.4fr]", children: [
|
|
1160
|
+
/* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden bg-primary px-8 py-12 text-primary-foreground", children: [
|
|
1161
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(circle_at_top_right,hsl(var(--primary-foreground)/0.1),transparent_60%)]" }),
|
|
1162
|
+
/* @__PURE__ */ jsxs("div", { className: "relative space-y-6", children: [
|
|
1163
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1164
|
+
/* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-3 bg-primary-foreground/15 text-primary-foreground hover:bg-primary-foreground/20", children: "Contact us" }),
|
|
1165
|
+
/* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold", children: "Let's talk" }),
|
|
1166
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-primary-foreground/70", children: "Have a question or want to work together? Fill in the form and we'll get back to you within one business day." })
|
|
1167
|
+
] }),
|
|
1168
|
+
/* @__PURE__ */ jsx(Separator, { className: "bg-primary-foreground/20" }),
|
|
1169
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-4", children: contactInfo.map(({ Icon, label, value }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1170
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary-foreground/10", children: /* @__PURE__ */ jsx(Icon, { className: "size-4" }) }),
|
|
1171
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1172
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-primary-foreground/60", children: label }),
|
|
1173
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: value })
|
|
1174
|
+
] })
|
|
1175
|
+
] }, label)) })
|
|
1176
|
+
] })
|
|
1177
|
+
] }),
|
|
1178
|
+
/* @__PURE__ */ jsx("div", { className: "p-8", children: submitted ? /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-[320px] flex-col items-center justify-center gap-4 text-center", children: [
|
|
1179
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(MessageSquare, { className: "size-6" }) }),
|
|
1180
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1181
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "Message sent" }),
|
|
1182
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Thanks for reaching out. We'll get back to you within one business day." })
|
|
1183
|
+
] }),
|
|
1184
|
+
/* @__PURE__ */ jsx(Button$1, { variant: "outline", onClick: () => setSubmitted(false), children: "Send another" })
|
|
1185
|
+
] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
|
|
1186
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
1187
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1188
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-first", children: "First name" }),
|
|
1189
|
+
/* @__PURE__ */ jsx(Input$1, { id: "contact-first", placeholder: "Olivia", required: true })
|
|
1190
|
+
] }),
|
|
1191
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1192
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-last", children: "Last name" }),
|
|
1193
|
+
/* @__PURE__ */ jsx(Input$1, { id: "contact-last", placeholder: "Reed", required: true })
|
|
1194
|
+
] })
|
|
1195
|
+
] }),
|
|
1196
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1197
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-email", children: "Email" }),
|
|
1198
|
+
/* @__PURE__ */ jsx(Input$1, { id: "contact-email", type: "email", placeholder: "olivia@company.com", required: true })
|
|
1199
|
+
] }),
|
|
1200
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1201
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-subject", children: "Subject" }),
|
|
1202
|
+
/* @__PURE__ */ jsx(Input$1, { id: "contact-subject", placeholder: "How can we help?", required: true })
|
|
1203
|
+
] }),
|
|
1204
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1205
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-message", children: "Message" }),
|
|
1206
|
+
/* @__PURE__ */ jsx(
|
|
1207
|
+
Textarea$1,
|
|
1208
|
+
{
|
|
1209
|
+
id: "contact-message",
|
|
1210
|
+
placeholder: "Tell us what you're working on...",
|
|
1211
|
+
className: "min-h-28 resize-none",
|
|
1212
|
+
required: true
|
|
1213
|
+
}
|
|
1214
|
+
)
|
|
1215
|
+
] }),
|
|
1216
|
+
/* @__PURE__ */ jsxs(Button$1, { type: "submit", className: "w-full", children: [
|
|
1217
|
+
"Send message",
|
|
1218
|
+
/* @__PURE__ */ jsx(Send, { className: "ml-2 size-4" })
|
|
1219
|
+
] })
|
|
1220
|
+
] }) })
|
|
1221
|
+
] }) });
|
|
1222
|
+
}
|
|
1223
|
+
function ThemeSwitchMinimal() {
|
|
1224
|
+
const [theme, setThemeState] = React25.useState("dark");
|
|
1225
|
+
React25.useEffect(() => {
|
|
1226
|
+
const stored = localStorage.getItem("theme");
|
|
1227
|
+
const resolved = stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
|
|
1228
|
+
setThemeState(resolved);
|
|
1229
|
+
}, []);
|
|
1230
|
+
const toggle = () => {
|
|
1231
|
+
const next = theme === "dark" ? "light" : "dark";
|
|
1232
|
+
setThemeState(next);
|
|
1233
|
+
localStorage.setItem("theme", next);
|
|
1234
|
+
document.documentElement.classList.toggle("dark", next === "dark");
|
|
1235
|
+
};
|
|
1236
|
+
return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: toggle, className: "size-8", "aria-label": "Toggle theme", children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "size-4" }) : /* @__PURE__ */ jsx(Moon, { className: "size-4" }) });
|
|
1237
|
+
}
|
|
1238
|
+
var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1239
|
+
function Navbar({
|
|
1240
|
+
brand,
|
|
1241
|
+
navLinks,
|
|
1242
|
+
cta,
|
|
1243
|
+
showThemeToggle = false,
|
|
1244
|
+
renderLink = defaultRenderLink6
|
|
1245
|
+
}) {
|
|
1246
|
+
const [open, setOpen] = React25.useState(false);
|
|
1247
|
+
const brandHref = brand.href ?? "/";
|
|
1248
|
+
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("nav", { className: "grid h-16 grid-cols-[1fr_auto_1fr] items-center px-6", children: [
|
|
1249
|
+
renderLink({
|
|
1250
|
+
href: brandHref,
|
|
1251
|
+
children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 font-semibold", children: [
|
|
1252
|
+
brand.logo && /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: brand.logo }),
|
|
1253
|
+
/* @__PURE__ */ jsx("span", { children: brand.name })
|
|
1254
|
+
] })
|
|
1255
|
+
}),
|
|
1256
|
+
/* @__PURE__ */ jsx("div", { className: "hidden items-center gap-1 md:flex", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { children: renderLink({
|
|
1257
|
+
href: link.href,
|
|
1258
|
+
className: "rounded-md px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
1259
|
+
children: link.label
|
|
1260
|
+
}) }, link.label)) }),
|
|
1261
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-3 md:flex", children: [
|
|
1262
|
+
cta?.secondary && renderLink({
|
|
1263
|
+
href: cta.secondary.href,
|
|
1264
|
+
children: /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "sm", children: cta.secondary.label })
|
|
1265
|
+
}),
|
|
1266
|
+
cta?.primary && renderLink({
|
|
1267
|
+
href: cta.primary.href,
|
|
1268
|
+
children: /* @__PURE__ */ jsx(Button$1, { size: "sm", children: cta.primary.label })
|
|
1269
|
+
}),
|
|
1270
|
+
showThemeToggle && /* @__PURE__ */ jsx(ThemeSwitchMinimal, {})
|
|
1271
|
+
] }),
|
|
1272
|
+
/* @__PURE__ */ jsxs(Sheet, { open, onOpenChange: setOpen, children: [
|
|
1273
|
+
/* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button$1, { variant: "ghost", size: "icon", className: "justify-self-end md:hidden", children: [
|
|
1274
|
+
/* @__PURE__ */ jsx(Menu, { className: "size-5" }),
|
|
1275
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Open menu" })
|
|
1276
|
+
] }) }),
|
|
1277
|
+
/* @__PURE__ */ jsxs(SheetContent, { side: "right", className: "w-72", children: [
|
|
1278
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pb-4", children: [
|
|
1279
|
+
/* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), className: "cursor-pointer", children: renderLink({
|
|
1280
|
+
href: brandHref,
|
|
1281
|
+
children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 font-semibold", children: [
|
|
1282
|
+
brand.logo && /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: brand.logo }),
|
|
1283
|
+
/* @__PURE__ */ jsx("span", { children: brand.name })
|
|
1284
|
+
] })
|
|
1285
|
+
}) }),
|
|
1286
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1287
|
+
showThemeToggle && /* @__PURE__ */ jsx(ThemeSwitchMinimal, {}),
|
|
1288
|
+
/* @__PURE__ */ jsxs(Button$1, { variant: "ghost", size: "icon", onClick: () => setOpen(false), children: [
|
|
1289
|
+
/* @__PURE__ */ jsx(X, { className: "size-4" }),
|
|
1290
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close menu" })
|
|
1291
|
+
] })
|
|
1292
|
+
] })
|
|
1293
|
+
] }),
|
|
1294
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
1295
|
+
/* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
|
|
1296
|
+
href: link.href,
|
|
1297
|
+
className: "block rounded-md px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
1298
|
+
children: link.label
|
|
1299
|
+
}) }, link.label)) }),
|
|
1300
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-col gap-2", children: [
|
|
1301
|
+
cta?.secondary && renderLink({
|
|
1302
|
+
href: cta.secondary.href,
|
|
1303
|
+
children: /* @__PURE__ */ jsx(Button$1, { variant: "outline", className: "w-full", children: cta.secondary.label })
|
|
1304
|
+
}),
|
|
1305
|
+
cta?.primary && renderLink({
|
|
1306
|
+
href: cta.primary.href,
|
|
1307
|
+
children: /* @__PURE__ */ jsx(Button$1, { className: "w-full", children: cta.primary.label })
|
|
1308
|
+
})
|
|
1309
|
+
] })
|
|
1310
|
+
] })
|
|
1311
|
+
] })
|
|
1312
|
+
] }) });
|
|
1313
|
+
}
|
|
1314
|
+
var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1315
|
+
function Footer({
|
|
1316
|
+
brand,
|
|
1317
|
+
navLinks,
|
|
1318
|
+
socialLinks,
|
|
1319
|
+
legal,
|
|
1320
|
+
renderLink = defaultRenderLink7
|
|
1321
|
+
}) {
|
|
1322
|
+
const brandHref = brand.href ?? "/";
|
|
1323
|
+
const copyrightText = legal ?? `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${brand.name}. All rights reserved.`;
|
|
1324
|
+
return /* @__PURE__ */ jsx("footer", { className: "bg-gray-900", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-7xl overflow-hidden px-6 py-20 sm:py-24 lg:px-8", children: [
|
|
1325
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center", children: renderLink({
|
|
1326
|
+
href: brandHref,
|
|
1327
|
+
className: "flex items-center gap-2",
|
|
1328
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1329
|
+
brand.logo && /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-white/10 text-white", children: brand.logo }),
|
|
1330
|
+
/* @__PURE__ */ jsx("span", { className: "text-lg font-semibold text-white", children: brand.name })
|
|
1331
|
+
] })
|
|
1332
|
+
}) }),
|
|
1333
|
+
navLinks && navLinks.length > 0 && /* @__PURE__ */ jsx("nav", { className: "mt-10 flex flex-wrap justify-center gap-x-12 gap-y-3", "aria-label": "Footer", children: navLinks.map(({ label, href }) => /* @__PURE__ */ jsx("div", { children: renderLink({
|
|
1334
|
+
href,
|
|
1335
|
+
className: "text-sm/6 text-gray-400 hover:text-white transition-colors",
|
|
1336
|
+
children: label
|
|
1337
|
+
}) }, label)) }),
|
|
1338
|
+
socialLinks && socialLinks.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-10 flex justify-center gap-x-10", children: socialLinks.map(({ label, href, icon }) => /* @__PURE__ */ jsx(
|
|
1339
|
+
"a",
|
|
1340
|
+
{
|
|
1341
|
+
href,
|
|
1342
|
+
"aria-label": label,
|
|
1343
|
+
className: "text-gray-400 hover:text-gray-300 transition-colors",
|
|
1344
|
+
children: icon
|
|
1345
|
+
},
|
|
1346
|
+
label
|
|
1347
|
+
)) }),
|
|
1348
|
+
/* @__PURE__ */ jsx("p", { className: "mt-10 text-center text-sm/6 text-gray-500", children: copyrightText })
|
|
1349
|
+
] }) });
|
|
1350
|
+
}
|
|
1351
|
+
var speedDuration = {
|
|
1352
|
+
slow: "60s",
|
|
1353
|
+
normal: "40s",
|
|
1354
|
+
fast: "25s"
|
|
1355
|
+
};
|
|
1356
|
+
function LogoStrip({
|
|
1357
|
+
logos,
|
|
1358
|
+
label,
|
|
1359
|
+
speed = "normal"
|
|
1360
|
+
}) {
|
|
1361
|
+
const duration = speedDuration[speed];
|
|
1362
|
+
return /* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
|
|
1363
|
+
/* @__PURE__ */ jsxs("section", { className: "overflow-hidden py-8", children: [
|
|
1364
|
+
label && /* @__PURE__ */ jsx("p", { className: "mb-6 text-center text-sm text-muted-foreground", children: label }),
|
|
1365
|
+
/* @__PURE__ */ jsx(
|
|
1366
|
+
"div",
|
|
1367
|
+
{
|
|
1368
|
+
className: "relative",
|
|
1369
|
+
style: {
|
|
1370
|
+
maskImage: "linear-gradient(to right, transparent, black 10%, black 90%, transparent)",
|
|
1371
|
+
WebkitMaskImage: "linear-gradient(to right, transparent, black 10%, black 90%, transparent)"
|
|
1372
|
+
},
|
|
1373
|
+
children: /* @__PURE__ */ jsx(
|
|
1374
|
+
"div",
|
|
1375
|
+
{
|
|
1376
|
+
className: "flex w-max gap-12",
|
|
1377
|
+
style: {
|
|
1378
|
+
animation: `logo-strip-scroll ${duration} linear infinite`
|
|
1379
|
+
},
|
|
1380
|
+
children: [...logos, ...logos].map((logo, i) => {
|
|
1381
|
+
const img = logo.darkSrc ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1382
|
+
/* @__PURE__ */ jsx(
|
|
1383
|
+
"img",
|
|
1384
|
+
{
|
|
1385
|
+
src: logo.src,
|
|
1386
|
+
alt: logo.alt,
|
|
1387
|
+
className: "h-8 w-auto object-contain opacity-60 grayscale transition-all hover:opacity-100 hover:grayscale-0 dark:hidden"
|
|
1388
|
+
}
|
|
1389
|
+
),
|
|
1390
|
+
/* @__PURE__ */ jsx(
|
|
1391
|
+
"img",
|
|
1392
|
+
{
|
|
1393
|
+
src: logo.darkSrc,
|
|
1394
|
+
alt: logo.alt,
|
|
1395
|
+
className: "hidden h-8 w-auto object-contain opacity-60 grayscale transition-all hover:opacity-100 hover:grayscale-0 dark:block"
|
|
1396
|
+
}
|
|
1397
|
+
)
|
|
1398
|
+
] }) : /* @__PURE__ */ jsx(
|
|
1399
|
+
"img",
|
|
1400
|
+
{
|
|
1401
|
+
src: logo.src,
|
|
1402
|
+
alt: logo.alt,
|
|
1403
|
+
className: "h-8 w-auto object-contain opacity-60 grayscale transition-all hover:opacity-100 hover:grayscale-0"
|
|
1404
|
+
}
|
|
1405
|
+
);
|
|
1406
|
+
return logo.href ? /* @__PURE__ */ jsx(
|
|
1407
|
+
"a",
|
|
1408
|
+
{
|
|
1409
|
+
href: logo.href,
|
|
1410
|
+
target: "_blank",
|
|
1411
|
+
rel: "noopener noreferrer",
|
|
1412
|
+
className: "flex shrink-0 items-center",
|
|
1413
|
+
children: img
|
|
1414
|
+
},
|
|
1415
|
+
`${logo.alt}-${i}`
|
|
1416
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center", children: img }, `${logo.alt}-${i}`);
|
|
1417
|
+
})
|
|
1418
|
+
}
|
|
1419
|
+
)
|
|
1420
|
+
}
|
|
1421
|
+
)
|
|
1422
|
+
] }),
|
|
1423
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
1424
|
+
@keyframes logo-strip-scroll {
|
|
1425
|
+
from { transform: translateX(0); }
|
|
1426
|
+
to { transform: translateX(-50%); }
|
|
1427
|
+
}
|
|
1428
|
+
` })
|
|
1429
|
+
] });
|
|
1430
|
+
}
|
|
1431
|
+
function GridUnderlay({ variant, size = 24, className, ...props }) {
|
|
1432
|
+
const id = React25.useId();
|
|
1433
|
+
const pattern = variant === "dots" ? /* @__PURE__ */ jsx("circle", { cx: size / 2, cy: size / 2, r: 1, fill: "currentColor" }) : variant === "lines" ? /* @__PURE__ */ jsx("path", { d: `M ${size} 0 L 0 0 0 ${size}`, fill: "none", stroke: "currentColor", strokeWidth: 0.5 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1434
|
+
/* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
|
|
1435
|
+
/* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
|
|
1436
|
+
] });
|
|
1437
|
+
return /* @__PURE__ */ jsxs(
|
|
1438
|
+
"svg",
|
|
1439
|
+
{
|
|
1440
|
+
"aria-hidden": "true",
|
|
1441
|
+
className: cn$1("pointer-events-none absolute inset-0 h-full w-full text-foreground/15", className),
|
|
1442
|
+
...props,
|
|
1443
|
+
children: [
|
|
1444
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", { id, width: size, height: size, patternUnits: "userSpaceOnUse", children: pattern }) }),
|
|
1445
|
+
/* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` })
|
|
1446
|
+
]
|
|
1447
|
+
}
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
var blurClass = { sm: "blur-xl", md: "blur-2xl", lg: "blur-3xl", xl: "blur-[80px]" };
|
|
1451
|
+
function GlowUnderlay({
|
|
1452
|
+
size = 400,
|
|
1453
|
+
color = "color-mix(in oklch, var(--primary) 45%, transparent)",
|
|
1454
|
+
followCursor = false,
|
|
1455
|
+
blur = "lg",
|
|
1456
|
+
className,
|
|
1457
|
+
style,
|
|
1458
|
+
...props
|
|
1459
|
+
}) {
|
|
1460
|
+
const [pos, setPos] = React25.useState({ x: "50%", y: "50%" });
|
|
1461
|
+
const containerRef = React25.useRef(null);
|
|
1462
|
+
React25.useEffect(() => {
|
|
1463
|
+
if (!followCursor) return;
|
|
1464
|
+
const handleMouseMove = (e) => {
|
|
1465
|
+
const el = containerRef.current;
|
|
1466
|
+
if (!el) return;
|
|
1467
|
+
const rect = el.getBoundingClientRect();
|
|
1468
|
+
setPos({ x: `${e.clientX - rect.left}px`, y: `${e.clientY - rect.top}px` });
|
|
1469
|
+
};
|
|
1470
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
1471
|
+
return () => window.removeEventListener("mousemove", handleMouseMove);
|
|
1472
|
+
}, [followCursor]);
|
|
1473
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, className: cn$1("pointer-events-none absolute inset-0 overflow-hidden", className), ...props, children: /* @__PURE__ */ jsx(
|
|
1474
|
+
"div",
|
|
1475
|
+
{
|
|
1476
|
+
"aria-hidden": "true",
|
|
1477
|
+
className: cn$1("absolute rounded-full", blurClass[blur]),
|
|
1478
|
+
style: {
|
|
1479
|
+
width: size,
|
|
1480
|
+
height: size,
|
|
1481
|
+
background: color,
|
|
1482
|
+
left: pos.x,
|
|
1483
|
+
top: pos.y,
|
|
1484
|
+
transform: "translate(-50%, -50%)",
|
|
1485
|
+
...style
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
) });
|
|
1489
|
+
}
|
|
1490
|
+
function Underlay(props) {
|
|
1491
|
+
if (props.variant === "glow") {
|
|
1492
|
+
return /* @__PURE__ */ jsx(GlowUnderlay, { ...props });
|
|
1493
|
+
}
|
|
1494
|
+
return /* @__PURE__ */ jsx(GridUnderlay, { ...props });
|
|
1495
|
+
}
|
|
1496
|
+
function DimOverlay({ opacity = 0.5, className, style, ...props }) {
|
|
1497
|
+
return /* @__PURE__ */ jsx(
|
|
1498
|
+
"div",
|
|
1499
|
+
{
|
|
1500
|
+
"aria-hidden": "true",
|
|
1501
|
+
className: cn$1("absolute inset-0 bg-background", className),
|
|
1502
|
+
style: { opacity, ...style },
|
|
1503
|
+
...props
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
var scrimGradients = {
|
|
1508
|
+
bottom: "to bottom",
|
|
1509
|
+
top: "to top",
|
|
1510
|
+
left: "to left",
|
|
1511
|
+
right: "to right"
|
|
1512
|
+
};
|
|
1513
|
+
function ScrimOverlay({ direction = "bottom", color = "var(--background)", className, style, ...props }) {
|
|
1514
|
+
return /* @__PURE__ */ jsx(
|
|
1515
|
+
"div",
|
|
1516
|
+
{
|
|
1517
|
+
"aria-hidden": "true",
|
|
1518
|
+
className: cn$1("pointer-events-none absolute inset-x-0 bottom-0 h-1/2", className),
|
|
1519
|
+
style: {
|
|
1520
|
+
background: `linear-gradient(${scrimGradients[direction]}, transparent, ${color})`,
|
|
1521
|
+
...style
|
|
1522
|
+
},
|
|
1523
|
+
...props
|
|
1524
|
+
}
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
function LoadingOverlay({ label, className, ...props }) {
|
|
1528
|
+
return /* @__PURE__ */ jsxs(
|
|
1529
|
+
"div",
|
|
1530
|
+
{
|
|
1531
|
+
role: "status",
|
|
1532
|
+
className: cn$1("absolute inset-0 flex flex-col items-center justify-center gap-2 bg-background/80 backdrop-blur-sm", className),
|
|
1533
|
+
...props,
|
|
1534
|
+
children: [
|
|
1535
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-5 animate-spin text-muted-foreground" }),
|
|
1536
|
+
label && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: label })
|
|
1537
|
+
]
|
|
1538
|
+
}
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
function Overlay(props) {
|
|
1542
|
+
if (props.variant === "dim") return /* @__PURE__ */ jsx(DimOverlay, { ...props });
|
|
1543
|
+
if (props.variant === "scrim") return /* @__PURE__ */ jsx(ScrimOverlay, { ...props });
|
|
1544
|
+
return /* @__PURE__ */ jsx(LoadingOverlay, { ...props });
|
|
1545
|
+
}
|
|
1546
|
+
function PageTransition({
|
|
1547
|
+
variant = "fade",
|
|
1548
|
+
duration = 300,
|
|
1549
|
+
children,
|
|
1550
|
+
className,
|
|
1551
|
+
style,
|
|
1552
|
+
...props
|
|
1553
|
+
}) {
|
|
1554
|
+
const [mounted, setMounted] = React25.useState(false);
|
|
1555
|
+
React25.useEffect(() => {
|
|
1556
|
+
const id = requestAnimationFrame(() => setMounted(true));
|
|
1557
|
+
return () => cancelAnimationFrame(id);
|
|
1558
|
+
}, []);
|
|
1559
|
+
const initial = variant === "slide-up" ? { opacity: 0, transform: "translateY(16px)" } : variant === "slide-down" ? { opacity: 0, transform: "translateY(-16px)" } : { opacity: 0 };
|
|
1560
|
+
const entered = { opacity: 1, transform: "translateY(0)" };
|
|
1561
|
+
return /* @__PURE__ */ jsx(
|
|
1562
|
+
"div",
|
|
1563
|
+
{
|
|
1564
|
+
className: cn$1(className),
|
|
1565
|
+
style: {
|
|
1566
|
+
transition: `opacity ${duration}ms ease, transform ${duration}ms ease`,
|
|
1567
|
+
...mounted ? entered : initial,
|
|
1568
|
+
...style
|
|
1569
|
+
},
|
|
1570
|
+
...props,
|
|
1571
|
+
children
|
|
1572
|
+
}
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
function Spotlight({
|
|
1576
|
+
groups,
|
|
1577
|
+
placeholder = "Search anything...",
|
|
1578
|
+
trigger,
|
|
1579
|
+
triggerClassName,
|
|
1580
|
+
shortcut = "\u2318K"
|
|
1581
|
+
}) {
|
|
1582
|
+
const [open, setOpen] = React25.useState(false);
|
|
1583
|
+
React25.useEffect(() => {
|
|
1584
|
+
const down = (e) => {
|
|
1585
|
+
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
|
1586
|
+
e.preventDefault();
|
|
1587
|
+
setOpen((v) => !v);
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
document.addEventListener("keydown", down);
|
|
1591
|
+
return () => document.removeEventListener("keydown", down);
|
|
1592
|
+
}, []);
|
|
1593
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1594
|
+
trigger ? /* @__PURE__ */ jsx("div", { onClick: () => setOpen(true), children: trigger }) : /* @__PURE__ */ jsxs(
|
|
1595
|
+
Button$1,
|
|
1596
|
+
{
|
|
1597
|
+
variant: "outline",
|
|
1598
|
+
onClick: () => setOpen(true),
|
|
1599
|
+
className: cn$1("w-64 justify-between text-muted-foreground", triggerClassName),
|
|
1600
|
+
children: [
|
|
1601
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
1602
|
+
/* @__PURE__ */ jsx(Search, { className: "size-4" }),
|
|
1603
|
+
placeholder
|
|
1604
|
+
] }),
|
|
1605
|
+
/* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "font-mono text-xs", children: shortcut })
|
|
1606
|
+
]
|
|
1607
|
+
}
|
|
1608
|
+
),
|
|
1609
|
+
/* @__PURE__ */ jsxs(CommandDialog, { open, onOpenChange: setOpen, children: [
|
|
1610
|
+
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
1611
|
+
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
1612
|
+
/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
|
|
1613
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React25.Fragment, { children: [
|
|
1614
|
+
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
1615
|
+
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
1616
|
+
CommandItem,
|
|
1617
|
+
{
|
|
1618
|
+
onSelect: () => {
|
|
1619
|
+
item.onSelect();
|
|
1620
|
+
setOpen(false);
|
|
1621
|
+
},
|
|
1622
|
+
children: [
|
|
1623
|
+
item.icon && /* @__PURE__ */ jsx("span", { className: "mr-2", children: item.icon }),
|
|
1624
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
1625
|
+
item.description && /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: item.description })
|
|
1626
|
+
]
|
|
1627
|
+
},
|
|
1628
|
+
item.id
|
|
1629
|
+
)) })
|
|
1630
|
+
] }, group.heading))
|
|
1631
|
+
] })
|
|
1632
|
+
] })
|
|
1633
|
+
] });
|
|
1634
|
+
}
|
|
1635
|
+
var BASE = 40;
|
|
1636
|
+
var MAGNIFY_RADIUS = 80;
|
|
1637
|
+
function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
|
|
1638
|
+
const [mouseX, setMouseX] = React25.useState(null);
|
|
1639
|
+
const containerRef = React25.useRef(null);
|
|
1640
|
+
function getScale(index) {
|
|
1641
|
+
if (mouseX === null || !containerRef.current) return 1;
|
|
1642
|
+
const container = containerRef.current;
|
|
1643
|
+
const buttons = container.querySelectorAll("[data-dock-item]");
|
|
1644
|
+
const btn = buttons[index];
|
|
1645
|
+
if (!btn) return 1;
|
|
1646
|
+
const rect = btn.getBoundingClientRect();
|
|
1647
|
+
const centerX = rect.left + rect.width / 2;
|
|
1648
|
+
const distance = Math.abs(mouseX - centerX);
|
|
1649
|
+
if (distance > MAGNIFY_RADIUS) return 1;
|
|
1650
|
+
const t = 1 - distance / MAGNIFY_RADIUS;
|
|
1651
|
+
return 1 + (magnification - 1) * t;
|
|
1652
|
+
}
|
|
1653
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx(
|
|
1654
|
+
"div",
|
|
1655
|
+
{
|
|
1656
|
+
ref: containerRef,
|
|
1657
|
+
onMouseMove: (e) => setMouseX(e.clientX),
|
|
1658
|
+
onMouseLeave: () => setMouseX(null),
|
|
1659
|
+
className: cn$1(
|
|
1660
|
+
"inline-flex items-end gap-2 rounded-2xl border bg-background/80 px-4 py-3 shadow-lg backdrop-blur-md",
|
|
1661
|
+
className
|
|
1662
|
+
),
|
|
1663
|
+
...props,
|
|
1664
|
+
children: items.map((item, i) => {
|
|
1665
|
+
const scale = getScale(i);
|
|
1666
|
+
const size = iconSize * scale;
|
|
1667
|
+
const button = /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1668
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1669
|
+
"button",
|
|
1670
|
+
{
|
|
1671
|
+
"data-dock-item": true,
|
|
1672
|
+
onClick: item.onClick,
|
|
1673
|
+
className: "flex items-center justify-center rounded-xl bg-muted text-foreground transition-colors hover:bg-accent",
|
|
1674
|
+
style: {
|
|
1675
|
+
width: size,
|
|
1676
|
+
height: size,
|
|
1677
|
+
transition: "width 100ms ease, height 100ms ease",
|
|
1678
|
+
transformOrigin: "bottom center"
|
|
1679
|
+
},
|
|
1680
|
+
"aria-label": item.label,
|
|
1681
|
+
children: /* @__PURE__ */ jsx(item.icon, { style: { width: size * 0.5, height: size * 0.5 } })
|
|
1682
|
+
}
|
|
1683
|
+
) }),
|
|
1684
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 8, children: /* @__PURE__ */ jsx("p", { children: item.label }) })
|
|
1685
|
+
] }, item.label);
|
|
1686
|
+
if (item.href) {
|
|
1687
|
+
return /* @__PURE__ */ jsx("a", { href: item.href, children: button }, item.label);
|
|
1688
|
+
}
|
|
1689
|
+
return button;
|
|
1690
|
+
})
|
|
1691
|
+
}
|
|
1692
|
+
) });
|
|
1693
|
+
}
|
|
1694
|
+
function renderItems(items) {
|
|
1695
|
+
return items.map((item, i) => {
|
|
1696
|
+
if (item.type === "separator") return /* @__PURE__ */ jsx(ContextMenuSeparator, {}, i);
|
|
1697
|
+
if (item.type === "label") return /* @__PURE__ */ jsx(ContextMenuLabel, { children: item.label }, i);
|
|
1698
|
+
if (item.type === "sub") {
|
|
1699
|
+
return /* @__PURE__ */ jsxs(ContextMenuSub, { children: [
|
|
1700
|
+
/* @__PURE__ */ jsx(ContextMenuSubTrigger, { children: item.label }),
|
|
1701
|
+
/* @__PURE__ */ jsx(ContextMenuSubContent, { children: renderItems(item.items) })
|
|
1702
|
+
] }, i);
|
|
1703
|
+
}
|
|
1704
|
+
return /* @__PURE__ */ jsxs(ContextMenuItem, { disabled: item.disabled, onSelect: item.onSelect, children: [
|
|
1705
|
+
item.label,
|
|
1706
|
+
item.shortcut && /* @__PURE__ */ jsx(ContextMenuShortcut, { children: item.shortcut })
|
|
1707
|
+
] }, i);
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
function ContextMenu({ items, children }) {
|
|
1711
|
+
return /* @__PURE__ */ jsxs(ContextMenu$1, { children: [
|
|
1712
|
+
/* @__PURE__ */ jsx(ContextMenuTrigger, { asChild: true, children }),
|
|
1713
|
+
/* @__PURE__ */ jsx(ContextMenuContent, { children: renderItems(items) })
|
|
1714
|
+
] });
|
|
1715
|
+
}
|
|
1716
|
+
function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructive = false }) {
|
|
1717
|
+
return /* @__PURE__ */ jsx(AlertDialog, { open: isOpen, onOpenChange: (open) => {
|
|
1718
|
+
if (!open) handleCancel();
|
|
1719
|
+
}, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
1720
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
1721
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: options.title ?? "Are you sure?" }),
|
|
1722
|
+
options.description && /* @__PURE__ */ jsx(AlertDialogDescription, { children: options.description })
|
|
1723
|
+
] }),
|
|
1724
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
|
|
1725
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { onClick: handleCancel, children: options.cancelLabel ?? "Cancel" }),
|
|
1726
|
+
/* @__PURE__ */ jsx(
|
|
1727
|
+
AlertDialogAction,
|
|
1728
|
+
{
|
|
1729
|
+
onClick: handleConfirm,
|
|
1730
|
+
className: destructive ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : void 0,
|
|
1731
|
+
children: options.confirmLabel ?? "Confirm"
|
|
1732
|
+
}
|
|
1733
|
+
)
|
|
1734
|
+
] })
|
|
1735
|
+
] }) });
|
|
1736
|
+
}
|
|
1737
|
+
var blurClass2 = {
|
|
1738
|
+
sm: "backdrop-blur-sm",
|
|
1739
|
+
md: "backdrop-blur-md",
|
|
1740
|
+
lg: "backdrop-blur-lg"
|
|
1741
|
+
};
|
|
1742
|
+
function GlassCard({ blur = "md", className, children, ...props }) {
|
|
1743
|
+
return /* @__PURE__ */ jsx(
|
|
1744
|
+
"div",
|
|
1745
|
+
{
|
|
1746
|
+
className: cn$1(
|
|
1747
|
+
"rounded-2xl border border-white/20 bg-background/50 shadow-sm",
|
|
1748
|
+
blurClass2[blur],
|
|
1749
|
+
className
|
|
1750
|
+
),
|
|
1751
|
+
...props,
|
|
1752
|
+
children
|
|
1753
|
+
}
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
function StatCard({ value, label, delta, trend = "neutral", description, className, ...props }) {
|
|
1757
|
+
return /* @__PURE__ */ jsxs(
|
|
1758
|
+
"div",
|
|
1759
|
+
{
|
|
1760
|
+
className: cn$1("rounded-2xl border bg-card p-5", className),
|
|
1761
|
+
...props,
|
|
1762
|
+
children: [
|
|
1763
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: label }),
|
|
1764
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-end justify-between gap-3", children: [
|
|
1765
|
+
/* @__PURE__ */ jsx("div", { className: "text-3xl font-bold tracking-tight", children: value }),
|
|
1766
|
+
delta && /* @__PURE__ */ jsxs(
|
|
1767
|
+
"div",
|
|
1768
|
+
{
|
|
1769
|
+
className: cn$1(
|
|
1770
|
+
"flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium",
|
|
1771
|
+
trend === "up" && "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
|
|
1772
|
+
trend === "down" && "bg-red-500/10 text-red-600 dark:text-red-400",
|
|
1773
|
+
trend === "neutral" && "bg-muted text-muted-foreground"
|
|
1774
|
+
),
|
|
1775
|
+
children: [
|
|
1776
|
+
trend === "up" && /* @__PURE__ */ jsx(TrendingUp, { className: "size-3" }),
|
|
1777
|
+
trend === "down" && /* @__PURE__ */ jsx(TrendingDown, { className: "size-3" }),
|
|
1778
|
+
delta
|
|
1779
|
+
]
|
|
1780
|
+
}
|
|
1781
|
+
)
|
|
1782
|
+
] }),
|
|
1783
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground", children: description })
|
|
1784
|
+
]
|
|
1785
|
+
}
|
|
1786
|
+
);
|
|
1787
|
+
}
|
|
1788
|
+
var overlayClass = {
|
|
1789
|
+
none: "",
|
|
1790
|
+
subtle: "bg-gradient-to-t from-black/60 via-black/10 to-transparent",
|
|
1791
|
+
strong: "bg-gradient-to-t from-black/80 via-black/30 to-black/10"
|
|
1792
|
+
};
|
|
1793
|
+
var aspectClass = {
|
|
1794
|
+
video: "aspect-video",
|
|
1795
|
+
square: "aspect-square",
|
|
1796
|
+
portrait: "aspect-[3/4]",
|
|
1797
|
+
auto: ""
|
|
1798
|
+
};
|
|
1799
|
+
function ImageCard({
|
|
1800
|
+
src,
|
|
1801
|
+
alt = "",
|
|
1802
|
+
overlay = "subtle",
|
|
1803
|
+
aspectRatio = "video",
|
|
1804
|
+
children,
|
|
1805
|
+
className,
|
|
1806
|
+
...props
|
|
1807
|
+
}) {
|
|
1808
|
+
return /* @__PURE__ */ jsxs(
|
|
1809
|
+
"div",
|
|
1810
|
+
{
|
|
1811
|
+
className: cn$1(
|
|
1812
|
+
"group relative overflow-hidden rounded-2xl bg-muted",
|
|
1813
|
+
aspectClass[aspectRatio],
|
|
1814
|
+
className
|
|
1815
|
+
),
|
|
1816
|
+
...props,
|
|
1817
|
+
children: [
|
|
1818
|
+
/* @__PURE__ */ jsx(
|
|
1819
|
+
"img",
|
|
1820
|
+
{
|
|
1821
|
+
src,
|
|
1822
|
+
alt,
|
|
1823
|
+
className: "h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
|
1824
|
+
}
|
|
1825
|
+
),
|
|
1826
|
+
overlay !== "none" && /* @__PURE__ */ jsx("div", { className: cn$1("absolute inset-0", overlayClass[overlay]) }),
|
|
1827
|
+
children && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col justify-end p-5 text-white", children })
|
|
1828
|
+
]
|
|
1829
|
+
}
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
function FullPageSpinner({ message }) {
|
|
1833
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4", children: [
|
|
1834
|
+
/* @__PURE__ */ jsx(Spinner, { className: "size-8" }),
|
|
1835
|
+
message && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
|
|
1836
|
+
] });
|
|
1837
|
+
}
|
|
1838
|
+
var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1839
|
+
function PageHeader({
|
|
1840
|
+
title,
|
|
1841
|
+
description,
|
|
1842
|
+
breadcrumbs,
|
|
1843
|
+
backButton,
|
|
1844
|
+
renderLink = defaultRenderLink8,
|
|
1845
|
+
className,
|
|
1846
|
+
children
|
|
1847
|
+
}) {
|
|
1848
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
|
|
1849
|
+
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-2 flex items-center gap-1 text-sm text-muted-foreground", children: breadcrumbs.map((crumb, i) => {
|
|
1850
|
+
const isLast = i === breadcrumbs.length - 1;
|
|
1851
|
+
return /* @__PURE__ */ jsxs(React25.Fragment, { children: [
|
|
1852
|
+
i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
|
|
1853
|
+
isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
|
|
1854
|
+
href: crumb.href,
|
|
1855
|
+
children: crumb.label,
|
|
1856
|
+
className: "hover:text-foreground transition-colors"
|
|
1857
|
+
})
|
|
1858
|
+
] }, i);
|
|
1859
|
+
}) }),
|
|
1860
|
+
backButton && /* @__PURE__ */ jsx("div", { className: "mb-2", children: backButton.href ? renderLink({
|
|
1861
|
+
href: backButton.href,
|
|
1862
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1863
|
+
/* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
|
|
1864
|
+
/* @__PURE__ */ jsx("span", { children: backButton.label ?? "Back" })
|
|
1865
|
+
] }),
|
|
1866
|
+
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
1867
|
+
}) : /* @__PURE__ */ jsxs(
|
|
1868
|
+
"button",
|
|
1869
|
+
{
|
|
1870
|
+
type: "button",
|
|
1871
|
+
onClick: backButton.onClick,
|
|
1872
|
+
className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
1873
|
+
children: [
|
|
1874
|
+
/* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
|
|
1875
|
+
/* @__PURE__ */ jsx("span", { children: backButton.label ?? "Back" })
|
|
1876
|
+
]
|
|
1877
|
+
}
|
|
1878
|
+
) }),
|
|
1879
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
1880
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
|
|
1881
|
+
children && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children })
|
|
1882
|
+
] }),
|
|
1883
|
+
description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
|
|
1884
|
+
] });
|
|
1885
|
+
}
|
|
1886
|
+
function Suspensed({ children, fallback }) {
|
|
1887
|
+
return /* @__PURE__ */ jsx(React25.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
|
|
1888
|
+
}
|
|
1889
|
+
function ThemeColorUpdater({ colorTheme }) {
|
|
1890
|
+
React25.useEffect(() => {
|
|
1891
|
+
if (colorTheme) {
|
|
1892
|
+
document.documentElement.setAttribute("data-theme", colorTheme);
|
|
1893
|
+
} else {
|
|
1894
|
+
document.documentElement.removeAttribute("data-theme");
|
|
1895
|
+
}
|
|
1896
|
+
}, [colorTheme]);
|
|
1897
|
+
return null;
|
|
1898
|
+
}
|
|
1899
|
+
function VersionBanner({ version, message, className, onDismiss }) {
|
|
1900
|
+
const [dismissed, setDismissed] = React25.useState(false);
|
|
1901
|
+
if (dismissed) return null;
|
|
1902
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
|
|
1903
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1904
|
+
/* @__PURE__ */ jsxs("span", { className: "rounded bg-primary/20 px-1.5 py-0.5 font-mono text-xs font-semibold text-primary", children: [
|
|
1905
|
+
"v",
|
|
1906
|
+
version
|
|
1907
|
+
] }),
|
|
1908
|
+
message && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: message })
|
|
1909
|
+
] }),
|
|
1910
|
+
onDismiss && /* @__PURE__ */ jsx(
|
|
1911
|
+
"button",
|
|
1912
|
+
{
|
|
1913
|
+
onClick: () => {
|
|
1914
|
+
setDismissed(true);
|
|
1915
|
+
onDismiss();
|
|
1916
|
+
},
|
|
1917
|
+
className: "text-muted-foreground transition-colors hover:text-foreground",
|
|
1918
|
+
"aria-label": "Dismiss",
|
|
1919
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
1920
|
+
}
|
|
1921
|
+
)
|
|
1922
|
+
] });
|
|
1923
|
+
}
|
|
1924
|
+
function DevBanner({ environment = "development", className }) {
|
|
1925
|
+
return /* @__PURE__ */ jsxs(
|
|
1926
|
+
"div",
|
|
1927
|
+
{
|
|
1928
|
+
className: cn(
|
|
1929
|
+
"flex items-center justify-center gap-2 bg-amber-500/15 px-4 py-1 text-xs font-medium text-amber-700 dark:text-amber-400",
|
|
1930
|
+
className
|
|
1931
|
+
),
|
|
1932
|
+
children: [
|
|
1933
|
+
/* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-amber-500" }),
|
|
1934
|
+
/* @__PURE__ */ jsx("span", { children: environment })
|
|
1935
|
+
]
|
|
1936
|
+
}
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
function RegisterHotkeys({ hotkeys }) {
|
|
1940
|
+
React25.useEffect(() => {
|
|
1941
|
+
const handleKeyDown = (event) => {
|
|
1942
|
+
for (const hotkey of hotkeys) {
|
|
1943
|
+
const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
|
|
1944
|
+
if (matches) {
|
|
1945
|
+
event.preventDefault();
|
|
1946
|
+
hotkey.handler();
|
|
1947
|
+
break;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1952
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1953
|
+
}, [hotkeys]);
|
|
1954
|
+
return null;
|
|
1955
|
+
}
|
|
1956
|
+
function RootErrorFallback({ error, resetErrorBoundary }) {
|
|
1957
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-svh flex-col items-center justify-center gap-6 p-8 text-center", children: [
|
|
1958
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
1959
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "size-12 text-destructive" }),
|
|
1960
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold", children: "Something went wrong" }),
|
|
1961
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-md text-sm text-muted-foreground", children: error.message || "An unexpected error occurred." })
|
|
1962
|
+
] }),
|
|
1963
|
+
resetErrorBoundary && /* @__PURE__ */ jsx(Button$1, { onClick: resetErrorBoundary, variant: "outline", children: "Try again" })
|
|
1964
|
+
] });
|
|
1965
|
+
}
|
|
1966
|
+
function useMounted() {
|
|
1967
|
+
const [mounted, setMounted] = useState(false);
|
|
1968
|
+
useEffect(() => {
|
|
1969
|
+
setMounted(true);
|
|
1970
|
+
}, []);
|
|
1971
|
+
return mounted;
|
|
1972
|
+
}
|
|
1973
|
+
function useConfirm() {
|
|
1974
|
+
const [isOpen, setIsOpen] = React25.useState(false);
|
|
1975
|
+
const [options, setOptions] = React25.useState({});
|
|
1976
|
+
const resolveRef = React25.useRef(null);
|
|
1977
|
+
const confirm = React25.useCallback((opts = {}) => {
|
|
1978
|
+
setOptions(opts);
|
|
1979
|
+
setIsOpen(true);
|
|
1980
|
+
return new Promise((resolve) => {
|
|
1981
|
+
resolveRef.current = resolve;
|
|
1982
|
+
});
|
|
1983
|
+
}, []);
|
|
1984
|
+
const handleConfirm = React25.useCallback(() => {
|
|
1985
|
+
resolveRef.current?.(true);
|
|
1986
|
+
setIsOpen(false);
|
|
1987
|
+
}, []);
|
|
1988
|
+
const handleCancel = React25.useCallback(() => {
|
|
1989
|
+
resolveRef.current?.(false);
|
|
1990
|
+
setIsOpen(false);
|
|
1991
|
+
}, []);
|
|
1992
|
+
return { confirm, isOpen, options, handleConfirm, handleCancel };
|
|
1993
|
+
}
|
|
1994
|
+
function useControlledOpen(defaultOpen = false) {
|
|
1995
|
+
const [isOpen, setIsOpen] = React25.useState(defaultOpen);
|
|
1996
|
+
const open = React25.useCallback(() => setIsOpen(true), []);
|
|
1997
|
+
const close = React25.useCallback(() => setIsOpen(false), []);
|
|
1998
|
+
const toggle = React25.useCallback(() => setIsOpen((prev) => !prev), []);
|
|
1999
|
+
return { isOpen, open, close, toggle, setIsOpen };
|
|
2000
|
+
}
|
|
2001
|
+
function useScrolledPast(threshold) {
|
|
2002
|
+
const [scrolledPast, setScrolledPast] = React25.useState(false);
|
|
2003
|
+
React25.useEffect(() => {
|
|
2004
|
+
const handleScroll = () => {
|
|
2005
|
+
setScrolledPast(window.scrollY > threshold);
|
|
2006
|
+
};
|
|
2007
|
+
handleScroll();
|
|
2008
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
2009
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
2010
|
+
}, [threshold]);
|
|
2011
|
+
return scrolledPast;
|
|
2012
|
+
}
|
|
2013
|
+
function useCopyToClipboard() {
|
|
2014
|
+
const [copied, setCopied] = React25.useState(false);
|
|
2015
|
+
const timeoutRef = React25.useRef(void 0);
|
|
2016
|
+
const copy = React25.useCallback((text) => {
|
|
2017
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
2018
|
+
setCopied(true);
|
|
2019
|
+
clearTimeout(timeoutRef.current);
|
|
2020
|
+
timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
2021
|
+
});
|
|
2022
|
+
}, []);
|
|
2023
|
+
React25.useEffect(() => () => clearTimeout(timeoutRef.current), []);
|
|
2024
|
+
return [copied, copy];
|
|
2025
|
+
}
|
|
2026
|
+
function useDebounce(value, delay) {
|
|
2027
|
+
const [debounced, setDebounced] = React25.useState(value);
|
|
2028
|
+
React25.useEffect(() => {
|
|
2029
|
+
const timer = setTimeout(() => setDebounced(value), delay);
|
|
2030
|
+
return () => clearTimeout(timer);
|
|
2031
|
+
}, [value, delay]);
|
|
2032
|
+
return debounced;
|
|
2033
|
+
}
|
|
2034
|
+
function useIntersectionObserver(options) {
|
|
2035
|
+
const ref = React25.useRef(null);
|
|
2036
|
+
const [isIntersecting, setIsIntersecting] = React25.useState(false);
|
|
2037
|
+
React25.useEffect(() => {
|
|
2038
|
+
const el = ref.current;
|
|
2039
|
+
if (!el) return;
|
|
2040
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
2041
|
+
setIsIntersecting(entry.isIntersecting);
|
|
2042
|
+
}, options);
|
|
2043
|
+
observer.observe(el);
|
|
2044
|
+
return () => observer.disconnect();
|
|
2045
|
+
}, []);
|
|
2046
|
+
return [ref, isIntersecting];
|
|
2047
|
+
}
|
|
2048
|
+
function useLocalStorage(key, initialValue) {
|
|
2049
|
+
const [storedValue, setStoredValue] = React25.useState(() => {
|
|
2050
|
+
if (typeof window === "undefined") return initialValue;
|
|
2051
|
+
try {
|
|
2052
|
+
const item = window.localStorage.getItem(key);
|
|
2053
|
+
return item ? JSON.parse(item) : initialValue;
|
|
2054
|
+
} catch {
|
|
2055
|
+
return initialValue;
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
const setValue = React25.useCallback(
|
|
2059
|
+
(value) => {
|
|
2060
|
+
try {
|
|
2061
|
+
setStoredValue(value);
|
|
2062
|
+
if (typeof window !== "undefined") {
|
|
2063
|
+
window.localStorage.setItem(key, JSON.stringify(value));
|
|
2064
|
+
}
|
|
2065
|
+
} catch {
|
|
2066
|
+
}
|
|
2067
|
+
},
|
|
2068
|
+
[key]
|
|
2069
|
+
);
|
|
2070
|
+
return [storedValue, setValue];
|
|
2071
|
+
}
|
|
2072
|
+
function useMediaQuery(query) {
|
|
2073
|
+
const [matches, setMatches] = React25.useState(() => {
|
|
2074
|
+
if (typeof window === "undefined") return false;
|
|
2075
|
+
return window.matchMedia(query).matches;
|
|
2076
|
+
});
|
|
2077
|
+
React25.useEffect(() => {
|
|
2078
|
+
if (typeof window === "undefined") return;
|
|
2079
|
+
const mql = window.matchMedia(query);
|
|
2080
|
+
setMatches(mql.matches);
|
|
2081
|
+
const handler = (e) => setMatches(e.matches);
|
|
2082
|
+
mql.addEventListener("change", handler);
|
|
2083
|
+
return () => mql.removeEventListener("change", handler);
|
|
2084
|
+
}, [query]);
|
|
2085
|
+
return matches;
|
|
2086
|
+
}
|
|
2087
|
+
function usePagination(total, pageSize) {
|
|
2088
|
+
const [page, setPage] = React25.useState(1);
|
|
2089
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
2090
|
+
const offset = (page - 1) * pageSize;
|
|
2091
|
+
const goTo = React25.useCallback(
|
|
2092
|
+
(p) => setPage(Math.min(Math.max(1, p), totalPages)),
|
|
2093
|
+
[totalPages]
|
|
2094
|
+
);
|
|
2095
|
+
const next = React25.useCallback(() => goTo(page + 1), [page, goTo]);
|
|
2096
|
+
const prev = React25.useCallback(() => goTo(page - 1), [page, goTo]);
|
|
2097
|
+
return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
export { AppShell, AuthSection, Badge, Button, Card, Checkbox, ConfirmDialog, ContactSection, ContextMenu, CountUp, CtaSection, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeaturesSection, Footer, FullPageSpinner, GlassCard, HeroSection, ImageCard, Input, LogoStrip, Navbar, NewsletterSection, OlwibaUIProvider, Overlay, PageHeader, PageTransition, PricingCard, PricingSection, RegisterHotkeys, RootErrorFallback, SectionTitle, Spotlight, StaggerChildren, StatCard, StatsSection, Suspensed, Switch, TeamSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, cn, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
|
|
2101
|
+
//# sourceMappingURL=index.js.map
|
|
2102
|
+
//# sourceMappingURL=index.js.map
|