@olwiba/ui 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +328 -36
- package/dist/index.js +1160 -326
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/app/AuthSection.tsx +158 -56
- package/src/app/BillingPanel.tsx +155 -0
- package/src/app/OnboardingWizard.tsx +128 -0
- package/src/app/SettingsSection.tsx +29 -0
- package/src/app/TeamMembersPanel.tsx +184 -0
- package/src/components/ActivityFeed.tsx +82 -0
- package/src/components/CommandMenu.tsx +106 -0
- package/src/components/DataTable.tsx +212 -0
- package/src/components/FileUpload.tsx +214 -0
- package/src/components/NotificationsPopover.tsx +146 -0
- package/src/components/Notify.tsx +96 -0
- package/src/index.ts +38 -2
- package/src/marketing/ContactSection.tsx +13 -2
- package/src/marketing/CtaSection.tsx +93 -9
- package/src/marketing/FeaturesSection.tsx +27 -12
- package/src/marketing/HeroSection.tsx +5 -2
- package/src/marketing/NewsletterSection.tsx +12 -2
- package/src/marketing/TeamSection.tsx +10 -2
- package/src/mechanics/Carousel.tsx +112 -0
- package/src/marketing/CarouselSection.tsx +0 -126
- package/src/marketing/CtaCardSection.tsx +0 -95
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { cn } from './chunk-AMWLSHD6.js';
|
|
2
2
|
export { MdxContent, cn } from './chunk-AMWLSHD6.js';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React31 from 'react';
|
|
4
4
|
import { useRef, useEffect, useState } 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, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, StatusIndicator, 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,
|
|
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, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Dialog, DialogTrigger, Progress, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, StatusIndicator, 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, CommandShortcut, Popover, PopoverTrigger, PopoverContent, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, InputOTP, InputOTPGroup, InputOTPSlot, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup } from '@olwiba/cn';
|
|
6
6
|
export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Enchanted } from '@olwiba/cn';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
-
import {
|
|
8
|
+
import { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, Loader2, ArrowLeft, ArrowUpRight, X, ArrowRight, Sparkles, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, CheckCircle2, AlertCircle, Bell, Inbox, Activity, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Rocket, Info, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
|
|
9
|
+
import { useReactTable, getPaginationRowModel, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
|
|
10
|
+
import { toast } from 'sonner';
|
|
9
11
|
|
|
10
|
-
var OlwibaUIContext =
|
|
12
|
+
var OlwibaUIContext = React31.createContext({
|
|
11
13
|
isMobile: false,
|
|
12
14
|
mode: "default",
|
|
13
15
|
setMode: () => {
|
|
@@ -16,17 +18,17 @@ var OlwibaUIContext = React29.createContext({
|
|
|
16
18
|
function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
|
|
17
19
|
const detectedMobile = useIsMobile();
|
|
18
20
|
const isMobile = isMobileProp ?? detectedMobile;
|
|
19
|
-
const [mode, setMode] =
|
|
21
|
+
const [mode, setMode] = React31.useState(initialMode);
|
|
20
22
|
const variant = mode !== "default" ? mode : void 0;
|
|
21
23
|
return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
|
|
22
24
|
}
|
|
23
25
|
function useOlwibaUI() {
|
|
24
|
-
return
|
|
26
|
+
return React31.useContext(OlwibaUIContext);
|
|
25
27
|
}
|
|
26
28
|
function useUIMode() {
|
|
27
|
-
return
|
|
29
|
+
return React31.useContext(OlwibaUIContext).mode;
|
|
28
30
|
}
|
|
29
|
-
var Button =
|
|
31
|
+
var Button = React31.forwardRef(
|
|
30
32
|
({ mode, ...props }, ref) => {
|
|
31
33
|
const ctxMode = useUIMode();
|
|
32
34
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -34,7 +36,7 @@ var Button = React29.forwardRef(
|
|
|
34
36
|
}
|
|
35
37
|
);
|
|
36
38
|
Button.displayName = "Button";
|
|
37
|
-
var Card =
|
|
39
|
+
var Card = React31.forwardRef(
|
|
38
40
|
({ mode, ...props }, ref) => {
|
|
39
41
|
const ctxMode = useUIMode();
|
|
40
42
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -45,7 +47,7 @@ Card.displayName = "Card";
|
|
|
45
47
|
function Badge(props) {
|
|
46
48
|
return /* @__PURE__ */ jsx(Badge$1, { ...props });
|
|
47
49
|
}
|
|
48
|
-
var Input =
|
|
50
|
+
var Input = React31.forwardRef(
|
|
49
51
|
({ mode, ...props }, ref) => {
|
|
50
52
|
const ctxMode = useUIMode();
|
|
51
53
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -53,7 +55,7 @@ var Input = React29.forwardRef(
|
|
|
53
55
|
}
|
|
54
56
|
);
|
|
55
57
|
Input.displayName = "Input";
|
|
56
|
-
var Textarea =
|
|
58
|
+
var Textarea = React31.forwardRef(
|
|
57
59
|
({ mode, ...props }, ref) => {
|
|
58
60
|
const ctxMode = useUIMode();
|
|
59
61
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -61,13 +63,13 @@ var Textarea = React29.forwardRef(
|
|
|
61
63
|
}
|
|
62
64
|
);
|
|
63
65
|
Textarea.displayName = "Textarea";
|
|
64
|
-
var Checkbox =
|
|
66
|
+
var Checkbox = React31.forwardRef(({ mode, ...props }, ref) => {
|
|
65
67
|
const ctxMode = useUIMode();
|
|
66
68
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
67
69
|
return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
|
|
68
70
|
});
|
|
69
71
|
Checkbox.displayName = "Checkbox";
|
|
70
|
-
var Switch =
|
|
72
|
+
var Switch = React31.forwardRef(({ mode: _mode, ...props }, ref) => {
|
|
71
73
|
return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
|
|
72
74
|
});
|
|
73
75
|
Switch.displayName = "Switch";
|
|
@@ -481,6 +483,13 @@ function SplitAuth({ children, panel }) {
|
|
|
481
483
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-6 lg:p-10", children })
|
|
482
484
|
] });
|
|
483
485
|
}
|
|
486
|
+
var authCopy = {
|
|
487
|
+
signin: { title: "Sign in", description: "Enter your email and password to continue.", submit: "Sign in" },
|
|
488
|
+
signup: { title: "Create an account", description: "Enter your details to create your account.", submit: "Create account" },
|
|
489
|
+
"forgot-password": { title: "Reset your password", description: "Enter the email on your account and we\u2019ll send you a link to reset your password.", submit: "Send reset link" },
|
|
490
|
+
"reset-password": { title: "Choose a new password", description: "Your new password must be different from previous passwords.", submit: "Reset password" },
|
|
491
|
+
verify: { title: "Enter your code", description: "We sent a verification code to your email.", submit: "Verify" }
|
|
492
|
+
};
|
|
484
493
|
function DefaultForm({
|
|
485
494
|
mode = "signin",
|
|
486
495
|
onSubmit,
|
|
@@ -488,8 +497,12 @@ function DefaultForm({
|
|
|
488
497
|
signUpHref = "#",
|
|
489
498
|
signInHref = "#",
|
|
490
499
|
forgotPasswordHref = "#",
|
|
500
|
+
onResend,
|
|
501
|
+
destination,
|
|
502
|
+
codeLength = 6,
|
|
491
503
|
brand,
|
|
492
504
|
error,
|
|
505
|
+
success,
|
|
493
506
|
loading,
|
|
494
507
|
renderLink = defaultRenderLink2,
|
|
495
508
|
footer,
|
|
@@ -497,15 +510,24 @@ function DefaultForm({
|
|
|
497
510
|
defaultPassword
|
|
498
511
|
}) {
|
|
499
512
|
const isSignUp = mode === "signup";
|
|
513
|
+
const isForgotPassword = mode === "forgot-password";
|
|
514
|
+
const isResetPassword = mode === "reset-password";
|
|
515
|
+
const isVerify = mode === "verify";
|
|
516
|
+
const [code, setCode] = React31.useState("");
|
|
500
517
|
const hasPrefill = !!(defaultEmail || defaultPassword);
|
|
501
518
|
const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
|
|
519
|
+
const copy = authCopy[mode];
|
|
502
520
|
return /* @__PURE__ */ jsxs(Card, { className: "w-full", children: [
|
|
503
521
|
hasPrefill && // Theme tokens hold full color values (oklch), so opacity must come from color-mix, not hsl(var()/a)
|
|
504
522
|
/* @__PURE__ */ jsx("style", { children: `@keyframes auth-prefill{0%{box-shadow:0 0 0 0 transparent}40%{box-shadow:0 0 0 3px color-mix(in oklab,var(--primary) 45%,transparent),0 0 16px 2px color-mix(in oklab,var(--primary) 35%,transparent)}100%{box-shadow:0 0 0 0 transparent}}` }),
|
|
505
523
|
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
506
524
|
brand && /* @__PURE__ */ jsx("div", { className: "mb-2", children: brand }),
|
|
507
|
-
/* @__PURE__ */ jsx(CardTitle, { children:
|
|
508
|
-
/* @__PURE__ */ jsx(CardDescription, { children:
|
|
525
|
+
/* @__PURE__ */ jsx(CardTitle, { children: copy.title }),
|
|
526
|
+
/* @__PURE__ */ jsx(CardDescription, { children: isVerify && destination ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
527
|
+
"We sent a verification code to ",
|
|
528
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: destination }),
|
|
529
|
+
"."
|
|
530
|
+
] }) : copy.description })
|
|
509
531
|
] }),
|
|
510
532
|
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
511
533
|
/* @__PURE__ */ jsxs("form", { onSubmit, className: "space-y-4", children: [
|
|
@@ -513,7 +535,7 @@ function DefaultForm({
|
|
|
513
535
|
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-name", children: "Name" }),
|
|
514
536
|
/* @__PURE__ */ jsx(Input, { id: "auth-name", name: "name", type: "text", placeholder: "Your name", autoComplete: "name" })
|
|
515
537
|
] }),
|
|
516
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
538
|
+
(mode === "signin" || mode === "signup" || isForgotPassword) && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
517
539
|
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-email", children: "Email address" }),
|
|
518
540
|
/* @__PURE__ */ jsx(
|
|
519
541
|
Input,
|
|
@@ -528,7 +550,7 @@ function DefaultForm({
|
|
|
528
550
|
}
|
|
529
551
|
)
|
|
530
552
|
] }),
|
|
531
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
553
|
+
(mode === "signin" || mode === "signup") && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
532
554
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
533
555
|
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-password", children: "Password" }),
|
|
534
556
|
!isSignUp && forgotPasswordHref && renderLink({
|
|
@@ -550,13 +572,36 @@ function DefaultForm({
|
|
|
550
572
|
}
|
|
551
573
|
)
|
|
552
574
|
] }),
|
|
553
|
-
|
|
575
|
+
isResetPassword && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
576
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
577
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-new-password", children: "New password" }),
|
|
578
|
+
/* @__PURE__ */ jsx(Input, { id: "auth-new-password", name: "password", type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "new-password" })
|
|
579
|
+
] }),
|
|
580
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
581
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "auth-confirm-password", children: "Confirm password" }),
|
|
582
|
+
/* @__PURE__ */ jsx(Input, { id: "auth-confirm-password", name: "confirmPassword", type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "new-password" })
|
|
583
|
+
] })
|
|
584
|
+
] }),
|
|
585
|
+
isVerify && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
586
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: "code", value: code }),
|
|
587
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx(InputOTP, { maxLength: codeLength, value: code, onChange: setCode, containerClassName: "justify-center", children: /* @__PURE__ */ jsx(InputOTPGroup, { children: Array.from({ length: codeLength }).map((_, i) => /* @__PURE__ */ jsx(InputOTPSlot, { index: i }, i)) }) }) })
|
|
588
|
+
] }),
|
|
589
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error }),
|
|
590
|
+
success && /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm font-medium text-primary", children: success }),
|
|
554
591
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
555
|
-
/* @__PURE__ */
|
|
556
|
-
|
|
592
|
+
/* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: [
|
|
593
|
+
loading && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
|
|
594
|
+
loading ? "Please wait\u2026" : copy.submit
|
|
595
|
+
] }),
|
|
596
|
+
onSso && (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
|
|
557
597
|
] })
|
|
558
598
|
] }),
|
|
559
|
-
/* @__PURE__ */
|
|
599
|
+
isVerify && onResend && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
|
|
600
|
+
"Didn\u2019t get a code?",
|
|
601
|
+
" ",
|
|
602
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onResend, className: "text-foreground underline underline-offset-4", children: "Resend" })
|
|
603
|
+
] }),
|
|
604
|
+
(mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: isSignUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
560
605
|
"Already have an account?",
|
|
561
606
|
" ",
|
|
562
607
|
renderLink({ href: signInHref, className: "text-foreground underline underline-offset-4", children: "Sign in" })
|
|
@@ -565,6 +610,7 @@ function DefaultForm({
|
|
|
565
610
|
" ",
|
|
566
611
|
renderLink({ href: signUpHref, className: "text-foreground underline underline-offset-4", children: "Create an account" })
|
|
567
612
|
] }) }),
|
|
613
|
+
(isForgotPassword || isResetPassword || isVerify) && /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: renderLink({ href: signInHref, className: "text-foreground underline underline-offset-4", children: "Back to sign in" }) }),
|
|
568
614
|
footer && /* @__PURE__ */ jsx("div", { children: footer })
|
|
569
615
|
] })
|
|
570
616
|
] });
|
|
@@ -584,6 +630,435 @@ function AuthSection({
|
|
|
584
630
|
const form = children ?? /* @__PURE__ */ jsx(DefaultForm, { ...restFormProps });
|
|
585
631
|
return /* @__PURE__ */ jsx(CenteredAuth, { brand, className, children: form });
|
|
586
632
|
}
|
|
633
|
+
function SettingsSection({ title, description, children, danger = false, className }) {
|
|
634
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("grid grid-cols-1 gap-x-8 gap-y-6 py-10 sm:grid-cols-3", className), children: [
|
|
635
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
636
|
+
/* @__PURE__ */ jsx("h2", { className: cn$1("text-base font-semibold", danger && "text-destructive"), children: title }),
|
|
637
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
|
|
638
|
+
] }),
|
|
639
|
+
/* @__PURE__ */ jsx("div", { className: "sm:col-span-2", children })
|
|
640
|
+
] });
|
|
641
|
+
}
|
|
642
|
+
var selectColumn = {
|
|
643
|
+
id: "__select",
|
|
644
|
+
header: ({ table }) => /* @__PURE__ */ jsx(
|
|
645
|
+
Checkbox$1,
|
|
646
|
+
{
|
|
647
|
+
checked: table.getIsAllPageRowsSelected() || table.getIsSomePageRowsSelected() && "indeterminate",
|
|
648
|
+
onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
|
|
649
|
+
"aria-label": "Select all"
|
|
650
|
+
}
|
|
651
|
+
),
|
|
652
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(
|
|
653
|
+
Checkbox$1,
|
|
654
|
+
{
|
|
655
|
+
checked: row.getIsSelected(),
|
|
656
|
+
onCheckedChange: (value) => row.toggleSelected(!!value),
|
|
657
|
+
onClick: (e) => e.stopPropagation(),
|
|
658
|
+
"aria-label": "Select row"
|
|
659
|
+
}
|
|
660
|
+
),
|
|
661
|
+
enableSorting: false
|
|
662
|
+
};
|
|
663
|
+
function DataTable({
|
|
664
|
+
columns,
|
|
665
|
+
data,
|
|
666
|
+
searchKey,
|
|
667
|
+
searchPlaceholder = "Search\u2026",
|
|
668
|
+
selectable = false,
|
|
669
|
+
onSelectionChange,
|
|
670
|
+
pageSize = 10,
|
|
671
|
+
toolbar,
|
|
672
|
+
onRowClick,
|
|
673
|
+
emptyMessage = "No results.",
|
|
674
|
+
className
|
|
675
|
+
}) {
|
|
676
|
+
const [sorting, setSorting] = React31.useState([]);
|
|
677
|
+
const [rowSelection, setRowSelection] = React31.useState({});
|
|
678
|
+
const [globalFilter, setGlobalFilter] = React31.useState("");
|
|
679
|
+
const allColumns = React31.useMemo(
|
|
680
|
+
() => selectable ? [selectColumn, ...columns] : columns,
|
|
681
|
+
[selectable, columns]
|
|
682
|
+
);
|
|
683
|
+
const table = useReactTable({
|
|
684
|
+
data,
|
|
685
|
+
columns: allColumns,
|
|
686
|
+
state: { sorting, rowSelection, globalFilter },
|
|
687
|
+
onSortingChange: setSorting,
|
|
688
|
+
onRowSelectionChange: setRowSelection,
|
|
689
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
690
|
+
globalFilterFn: (row, _columnId, filterValue) => {
|
|
691
|
+
if (!searchKey) return true;
|
|
692
|
+
const value = row.getValue(searchKey);
|
|
693
|
+
return String(value ?? "").toLowerCase().includes(String(filterValue).toLowerCase());
|
|
694
|
+
},
|
|
695
|
+
getCoreRowModel: getCoreRowModel(),
|
|
696
|
+
getSortedRowModel: getSortedRowModel(),
|
|
697
|
+
...pageSize > 0 ? { getPaginationRowModel: getPaginationRowModel() } : {},
|
|
698
|
+
initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
|
|
699
|
+
enableRowSelection: selectable
|
|
700
|
+
});
|
|
701
|
+
React31.useEffect(() => {
|
|
702
|
+
if (!onSelectionChange) return;
|
|
703
|
+
onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
|
|
704
|
+
}, [rowSelection]);
|
|
705
|
+
const rows = table.getRowModel().rows;
|
|
706
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-4", className), children: [
|
|
707
|
+
(searchKey || toolbar) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
708
|
+
searchKey ? /* @__PURE__ */ jsxs("div", { className: "relative max-w-sm flex-1", children: [
|
|
709
|
+
/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }),
|
|
710
|
+
/* @__PURE__ */ jsx(
|
|
711
|
+
Input,
|
|
712
|
+
{
|
|
713
|
+
value: globalFilter,
|
|
714
|
+
onChange: (e) => setGlobalFilter(e.target.value),
|
|
715
|
+
placeholder: searchPlaceholder,
|
|
716
|
+
className: "pl-8"
|
|
717
|
+
}
|
|
718
|
+
)
|
|
719
|
+
] }) : /* @__PURE__ */ jsx("div", {}),
|
|
720
|
+
toolbar
|
|
721
|
+
] }),
|
|
722
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
723
|
+
/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
|
|
724
|
+
const canSort = header.column.getCanSort();
|
|
725
|
+
const sortDir = header.column.getIsSorted();
|
|
726
|
+
return /* @__PURE__ */ jsx(
|
|
727
|
+
TableHead,
|
|
728
|
+
{
|
|
729
|
+
"aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : canSort ? "none" : void 0,
|
|
730
|
+
children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
|
|
731
|
+
"button",
|
|
732
|
+
{
|
|
733
|
+
type: "button",
|
|
734
|
+
className: "flex items-center gap-1.5 font-medium hover:text-foreground",
|
|
735
|
+
onClick: header.column.getToggleSortingHandler(),
|
|
736
|
+
children: [
|
|
737
|
+
flexRender(header.column.columnDef.header, header.getContext()),
|
|
738
|
+
sortDir === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3.5" }) : sortDir === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-3.5" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-3.5 text-muted-foreground/50" })
|
|
739
|
+
]
|
|
740
|
+
}
|
|
741
|
+
) : flexRender(header.column.columnDef.header, header.getContext())
|
|
742
|
+
},
|
|
743
|
+
header.id
|
|
744
|
+
);
|
|
745
|
+
}) }, headerGroup.id)) }),
|
|
746
|
+
/* @__PURE__ */ jsx(TableBody, { children: rows.length ? rows.map((row) => /* @__PURE__ */ jsx(
|
|
747
|
+
TableRow,
|
|
748
|
+
{
|
|
749
|
+
"data-state": row.getIsSelected() ? "selected" : void 0,
|
|
750
|
+
onClick: () => onRowClick?.(row.original),
|
|
751
|
+
tabIndex: onRowClick ? 0 : void 0,
|
|
752
|
+
onKeyDown: onRowClick ? (e) => {
|
|
753
|
+
if (e.key === "Enter" && e.target === e.currentTarget) onRowClick(row.original);
|
|
754
|
+
} : void 0,
|
|
755
|
+
className: cn$1(onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
|
|
756
|
+
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
|
|
757
|
+
},
|
|
758
|
+
row.id
|
|
759
|
+
)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
|
|
760
|
+
] }) }),
|
|
761
|
+
(selectable || pageSize > 0 && table.getPageCount() > 1) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
762
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
763
|
+
selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`,
|
|
764
|
+
selectable && pageSize > 0 && table.getPageCount() > 1 && " \xB7 ",
|
|
765
|
+
pageSize > 0 && table.getPageCount() > 1 && `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`
|
|
766
|
+
] }),
|
|
767
|
+
pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
768
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
|
|
769
|
+
/* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
|
|
770
|
+
" Previous"
|
|
771
|
+
] }),
|
|
772
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: [
|
|
773
|
+
"Next ",
|
|
774
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
775
|
+
] })
|
|
776
|
+
] })
|
|
777
|
+
] })
|
|
778
|
+
] });
|
|
779
|
+
}
|
|
780
|
+
function InviteDialog({ roles, onInvite, onClose }) {
|
|
781
|
+
const [email, setEmail] = React31.useState("");
|
|
782
|
+
const [role, setRole] = React31.useState(roles[roles.length - 1] ?? roles[0]);
|
|
783
|
+
return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
|
|
784
|
+
"form",
|
|
785
|
+
{
|
|
786
|
+
onSubmit: (e) => {
|
|
787
|
+
e.preventDefault();
|
|
788
|
+
onInvite?.(email, role);
|
|
789
|
+
onClose();
|
|
790
|
+
},
|
|
791
|
+
children: [
|
|
792
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
793
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
|
|
794
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
|
|
795
|
+
] }),
|
|
796
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
|
|
797
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
798
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
|
|
799
|
+
/* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", required: true, placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
|
|
800
|
+
] }),
|
|
801
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
802
|
+
/* @__PURE__ */ jsx(Label, { children: "Role" }),
|
|
803
|
+
/* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
|
|
804
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
805
|
+
/* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
|
|
806
|
+
] })
|
|
807
|
+
] })
|
|
808
|
+
] }),
|
|
809
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
810
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", children: "Cancel" }) }),
|
|
811
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", children: "Send invite" })
|
|
812
|
+
] })
|
|
813
|
+
]
|
|
814
|
+
}
|
|
815
|
+
) });
|
|
816
|
+
}
|
|
817
|
+
function TeamMembersPanel({
|
|
818
|
+
members,
|
|
819
|
+
roles = ["Owner", "Admin", "Member"],
|
|
820
|
+
onInvite,
|
|
821
|
+
onRoleChange,
|
|
822
|
+
onRemove,
|
|
823
|
+
title = "Team members",
|
|
824
|
+
description = "Manage who has access to this workspace."
|
|
825
|
+
}) {
|
|
826
|
+
const [inviteOpen, setInviteOpen] = React31.useState(false);
|
|
827
|
+
const columns = React31.useMemo(() => [
|
|
828
|
+
{
|
|
829
|
+
accessorKey: "name",
|
|
830
|
+
header: "Name",
|
|
831
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
832
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
|
|
833
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: row.original.avatar, alt: "" }),
|
|
834
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: row.original.name.slice(0, 2).toUpperCase() })
|
|
835
|
+
] }),
|
|
836
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
837
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-none", children: row.original.name }),
|
|
838
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: row.original.email })
|
|
839
|
+
] })
|
|
840
|
+
] })
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
accessorKey: "role",
|
|
844
|
+
header: "Role",
|
|
845
|
+
cell: ({ row }) => onRoleChange ? /* @__PURE__ */ jsxs(Select, { value: row.original.role, onValueChange: (value) => onRoleChange(row.original.id, value), children: [
|
|
846
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-32", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
847
|
+
/* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
|
|
848
|
+
] }) : /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: row.original.role })
|
|
849
|
+
},
|
|
850
|
+
...onRemove ? [{
|
|
851
|
+
id: "actions",
|
|
852
|
+
header: "",
|
|
853
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
854
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "size-8", children: [
|
|
855
|
+
/* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }),
|
|
856
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Row actions" })
|
|
857
|
+
] }) }),
|
|
858
|
+
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove(row.original.id), children: "Remove member" }) })
|
|
859
|
+
] }),
|
|
860
|
+
enableSorting: false
|
|
861
|
+
}] : []
|
|
862
|
+
], [roles, onRoleChange, onRemove]);
|
|
863
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
864
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
|
|
865
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
866
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
|
|
867
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
|
|
868
|
+
] }),
|
|
869
|
+
onInvite && /* @__PURE__ */ jsxs(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: [
|
|
870
|
+
/* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { children: [
|
|
871
|
+
/* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
|
|
872
|
+
" Invite member"
|
|
873
|
+
] }) }),
|
|
874
|
+
/* @__PURE__ */ jsx(InviteDialog, { roles, onInvite, onClose: () => setInviteOpen(false) }, String(inviteOpen))
|
|
875
|
+
] })
|
|
876
|
+
] }),
|
|
877
|
+
/* @__PURE__ */ jsx(DataTable, { columns, data: members, searchKey: "name", searchPlaceholder: "Search members\u2026", pageSize: 0 })
|
|
878
|
+
] });
|
|
879
|
+
}
|
|
880
|
+
var invoiceStatusVariant = {
|
|
881
|
+
paid: "secondary",
|
|
882
|
+
open: "outline",
|
|
883
|
+
void: "destructive"
|
|
884
|
+
};
|
|
885
|
+
function BillingPanel({
|
|
886
|
+
planName,
|
|
887
|
+
planPrice,
|
|
888
|
+
renewalDate,
|
|
889
|
+
usage,
|
|
890
|
+
paymentMethod,
|
|
891
|
+
invoices,
|
|
892
|
+
onManagePlan,
|
|
893
|
+
onUpdatePaymentMethod,
|
|
894
|
+
className
|
|
895
|
+
}) {
|
|
896
|
+
const invoiceColumns = React31.useMemo(() => [
|
|
897
|
+
{ accessorKey: "date", header: "Date", enableSorting: false },
|
|
898
|
+
{ accessorKey: "amount", header: "Amount", enableSorting: false },
|
|
899
|
+
{
|
|
900
|
+
accessorKey: "status",
|
|
901
|
+
header: "Status",
|
|
902
|
+
enableSorting: false,
|
|
903
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(Badge$1, { variant: invoiceStatusVariant[row.original.status], className: "capitalize", children: row.original.status })
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
id: "download",
|
|
907
|
+
header: "",
|
|
908
|
+
cell: ({ row }) => row.original.downloadUrl ? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "size-8", asChild: true, children: /* @__PURE__ */ jsxs("a", { href: row.original.downloadUrl, download: true, children: [
|
|
909
|
+
/* @__PURE__ */ jsx(Download, { className: "size-4" }),
|
|
910
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Download invoice" })
|
|
911
|
+
] }) }) : null,
|
|
912
|
+
enableSorting: false
|
|
913
|
+
}
|
|
914
|
+
], []);
|
|
915
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-border", children: [
|
|
916
|
+
/* @__PURE__ */ jsxs(SettingsSection, { title: "Current plan", description: "Your subscription and usage against plan limits.", children: [
|
|
917
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 rounded-lg border bg-card/60 p-4", children: [
|
|
918
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
919
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
920
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: planName }),
|
|
921
|
+
planPrice && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: planPrice })
|
|
922
|
+
] }),
|
|
923
|
+
renewalDate && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
|
|
924
|
+
"Renews ",
|
|
925
|
+
renewalDate
|
|
926
|
+
] })
|
|
927
|
+
] }),
|
|
928
|
+
onManagePlan && /* @__PURE__ */ jsx(Button, { onClick: onManagePlan, children: "Manage plan" })
|
|
929
|
+
] }),
|
|
930
|
+
usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => {
|
|
931
|
+
const overLimit = metric.limit > 0 && metric.used > metric.limit;
|
|
932
|
+
const percent = metric.limit > 0 ? Math.min(100, metric.used / metric.limit * 100) : 0;
|
|
933
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
934
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
|
|
935
|
+
/* @__PURE__ */ jsx("span", { children: metric.label }),
|
|
936
|
+
/* @__PURE__ */ jsxs("span", { className: overLimit ? "font-medium text-destructive" : "text-muted-foreground", children: [
|
|
937
|
+
metric.used,
|
|
938
|
+
metric.unit,
|
|
939
|
+
" / ",
|
|
940
|
+
metric.limit,
|
|
941
|
+
metric.unit
|
|
942
|
+
] })
|
|
943
|
+
] }),
|
|
944
|
+
/* @__PURE__ */ jsx(Progress, { value: percent, className: overLimit ? "[&>div]:bg-destructive" : void 0 })
|
|
945
|
+
] }, metric.label);
|
|
946
|
+
}) })
|
|
947
|
+
] }),
|
|
948
|
+
paymentMethod && /* @__PURE__ */ jsx(SettingsSection, { title: "Payment method", description: "Used for your subscription renewal.", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border bg-card/60 p-4", children: [
|
|
949
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
950
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "size-5 text-muted-foreground" }),
|
|
951
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
952
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium capitalize", children: paymentMethod.brand }),
|
|
953
|
+
" ending in ",
|
|
954
|
+
paymentMethod.last4,
|
|
955
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
956
|
+
" \xB7 expires ",
|
|
957
|
+
paymentMethod.expiry
|
|
958
|
+
] })
|
|
959
|
+
] })
|
|
960
|
+
] }),
|
|
961
|
+
onUpdatePaymentMethod && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onUpdatePaymentMethod, children: "Update" })
|
|
962
|
+
] }) }),
|
|
963
|
+
invoices && invoices.length > 0 && /* @__PURE__ */ jsx(SettingsSection, { title: "Billing history", description: "Download past invoices for your records.", children: /* @__PURE__ */ jsx(DataTable, { columns: invoiceColumns, data: invoices, pageSize: 5 }) })
|
|
964
|
+
] }) });
|
|
965
|
+
}
|
|
966
|
+
function OnboardingWizard({
|
|
967
|
+
steps,
|
|
968
|
+
onComplete,
|
|
969
|
+
onStepChange,
|
|
970
|
+
step: stepProp,
|
|
971
|
+
onStepIndexChange,
|
|
972
|
+
completeLabel = "Finish",
|
|
973
|
+
className
|
|
974
|
+
}) {
|
|
975
|
+
const [internalStep, setInternalStep] = React31.useState(0);
|
|
976
|
+
const [error, setError] = React31.useState(null);
|
|
977
|
+
const [pending, setPending] = React31.useState(false);
|
|
978
|
+
const index = stepProp ?? internalStep;
|
|
979
|
+
const current = steps[index];
|
|
980
|
+
const isLast = index === steps.length - 1;
|
|
981
|
+
const goTo = (next) => {
|
|
982
|
+
setError(null);
|
|
983
|
+
if (stepProp === void 0) setInternalStep(next);
|
|
984
|
+
onStepIndexChange?.(next);
|
|
985
|
+
onStepChange?.(next);
|
|
986
|
+
};
|
|
987
|
+
const handleNext = async () => {
|
|
988
|
+
if (current.onNext) {
|
|
989
|
+
setPending(true);
|
|
990
|
+
let result;
|
|
991
|
+
try {
|
|
992
|
+
result = await current.onNext();
|
|
993
|
+
} catch (err) {
|
|
994
|
+
setError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
|
|
995
|
+
return;
|
|
996
|
+
} finally {
|
|
997
|
+
setPending(false);
|
|
998
|
+
}
|
|
999
|
+
if (result === false) {
|
|
1000
|
+
setError("Please complete this step before continuing.");
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (typeof result === "string") {
|
|
1004
|
+
setError(result);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (isLast) {
|
|
1009
|
+
onComplete?.();
|
|
1010
|
+
} else {
|
|
1011
|
+
goTo(index + 1);
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
const handleBack = () => {
|
|
1015
|
+
if (index > 0) goTo(index - 1);
|
|
1016
|
+
};
|
|
1017
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full max-w-lg space-y-8", className), children: [
|
|
1018
|
+
/* @__PURE__ */ jsx("ol", { className: "flex items-center", "aria-label": "Progress", children: steps.map((step, i) => /* @__PURE__ */ jsxs(
|
|
1019
|
+
"li",
|
|
1020
|
+
{
|
|
1021
|
+
"aria-current": i === index ? "step" : void 0,
|
|
1022
|
+
className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"),
|
|
1023
|
+
children: [
|
|
1024
|
+
/* @__PURE__ */ jsxs(
|
|
1025
|
+
"div",
|
|
1026
|
+
{
|
|
1027
|
+
className: cn$1(
|
|
1028
|
+
"flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
|
|
1029
|
+
i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
|
|
1030
|
+
),
|
|
1031
|
+
children: [
|
|
1032
|
+
i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1,
|
|
1033
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
1034
|
+
step.title,
|
|
1035
|
+
i < index ? " (completed)" : ""
|
|
1036
|
+
] })
|
|
1037
|
+
]
|
|
1038
|
+
}
|
|
1039
|
+
),
|
|
1040
|
+
i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
|
|
1041
|
+
]
|
|
1042
|
+
},
|
|
1043
|
+
step.id
|
|
1044
|
+
)) }),
|
|
1045
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1047
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: current.title }),
|
|
1048
|
+
current.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: current.description })
|
|
1049
|
+
] }),
|
|
1050
|
+
/* @__PURE__ */ jsx("div", { children: current.content }),
|
|
1051
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error })
|
|
1052
|
+
] }),
|
|
1053
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1054
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: handleBack, disabled: index === 0 || pending, children: "Back" }),
|
|
1055
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleNext, disabled: pending, children: [
|
|
1056
|
+
pending && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
|
|
1057
|
+
pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue"
|
|
1058
|
+
] })
|
|
1059
|
+
] })
|
|
1060
|
+
] });
|
|
1061
|
+
}
|
|
587
1062
|
function EmptyState({ icon: Icon, title, description, action, className, ...props }) {
|
|
588
1063
|
return /* @__PURE__ */ jsxs(
|
|
589
1064
|
"div",
|
|
@@ -833,9 +1308,9 @@ function FadeIn({
|
|
|
833
1308
|
style,
|
|
834
1309
|
...props
|
|
835
1310
|
}) {
|
|
836
|
-
const ref =
|
|
837
|
-
const [visible, setVisible] =
|
|
838
|
-
|
|
1311
|
+
const ref = React31.useRef(null);
|
|
1312
|
+
const [visible, setVisible] = React31.useState(false);
|
|
1313
|
+
React31.useEffect(() => {
|
|
839
1314
|
const el = ref.current;
|
|
840
1315
|
if (!el) return;
|
|
841
1316
|
const observer = new IntersectionObserver(
|
|
@@ -942,27 +1417,28 @@ function HeroSection({
|
|
|
942
1417
|
socialProofText,
|
|
943
1418
|
renderLink = defaultRenderLink4
|
|
944
1419
|
}) {
|
|
1420
|
+
const mode = useUIVariant();
|
|
945
1421
|
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: marketingSectionSpacing.hero, children: [
|
|
946
1422
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-2 lg:gap-16", children: [
|
|
947
1423
|
/* @__PURE__ */ jsx(FadeIn, { direction: "left", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
948
|
-
badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge
|
|
1424
|
+
badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
|
|
949
1425
|
/* @__PURE__ */ jsx("h1", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl", children: heading }),
|
|
950
1426
|
/* @__PURE__ */ jsx("p", { className: "max-w-lg text-pretty text-lg text-muted-foreground", children: description }),
|
|
951
1427
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
952
1428
|
renderLink({
|
|
953
1429
|
href: primaryCta.href,
|
|
954
|
-
children: /* @__PURE__ */ jsxs(Button
|
|
1430
|
+
children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
|
|
955
1431
|
primaryCta.label,
|
|
956
1432
|
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
957
1433
|
] })
|
|
958
1434
|
}),
|
|
959
1435
|
secondaryCta && renderLink({
|
|
960
1436
|
href: secondaryCta.href,
|
|
961
|
-
children: /* @__PURE__ */ jsx(Button
|
|
1437
|
+
children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
962
1438
|
})
|
|
963
1439
|
] })
|
|
964
1440
|
] }) }),
|
|
965
|
-
media !== "none" && /* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: media === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(PhoneFrame, { size: phoneSize, children: heroImage }) }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-2xl", children: heroImage }) })
|
|
1441
|
+
media !== "none" && /* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: media === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(PhoneFrame, { size: phoneSize, children: heroImage }) }) : /* @__PURE__ */ jsx("div", { className: cn$1("overflow-hidden", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: heroImage }) })
|
|
966
1442
|
] }),
|
|
967
1443
|
(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: [
|
|
968
1444
|
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: [
|
|
@@ -1021,6 +1497,88 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
|
|
|
1021
1497
|
}
|
|
1022
1498
|
return content;
|
|
1023
1499
|
}
|
|
1500
|
+
function Carousel({
|
|
1501
|
+
children,
|
|
1502
|
+
itemClassName = "w-[280px] sm:w-[320px]",
|
|
1503
|
+
controls = "top-right",
|
|
1504
|
+
ariaLabel,
|
|
1505
|
+
className
|
|
1506
|
+
}) {
|
|
1507
|
+
const mode = useUIVariant();
|
|
1508
|
+
const trackRef = React31.useRef(null);
|
|
1509
|
+
const [canPrev, setCanPrev] = React31.useState(false);
|
|
1510
|
+
const [canNext, setCanNext] = React31.useState(true);
|
|
1511
|
+
const updateScrollState = React31.useCallback(() => {
|
|
1512
|
+
const track = trackRef.current;
|
|
1513
|
+
if (!track) return;
|
|
1514
|
+
setCanPrev(track.scrollLeft > 8);
|
|
1515
|
+
setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
|
|
1516
|
+
}, []);
|
|
1517
|
+
React31.useEffect(() => {
|
|
1518
|
+
const track = trackRef.current;
|
|
1519
|
+
if (!track) return;
|
|
1520
|
+
updateScrollState();
|
|
1521
|
+
track.addEventListener("scroll", updateScrollState, { passive: true });
|
|
1522
|
+
const observer = new ResizeObserver(updateScrollState);
|
|
1523
|
+
observer.observe(track);
|
|
1524
|
+
return () => {
|
|
1525
|
+
track.removeEventListener("scroll", updateScrollState);
|
|
1526
|
+
observer.disconnect();
|
|
1527
|
+
};
|
|
1528
|
+
}, [updateScrollState]);
|
|
1529
|
+
const scrollByItem = (direction) => {
|
|
1530
|
+
const track = trackRef.current;
|
|
1531
|
+
if (!track) return;
|
|
1532
|
+
const item = track.querySelector("[data-carousel-item]");
|
|
1533
|
+
const gap = parseFloat(getComputedStyle(track).columnGap) || 24;
|
|
1534
|
+
const step = item ? item.offsetWidth + gap : track.clientWidth * 0.8;
|
|
1535
|
+
track.scrollBy({ left: direction * step, behavior: "smooth" });
|
|
1536
|
+
};
|
|
1537
|
+
const controlClasses = cn$1(
|
|
1538
|
+
"flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
|
|
1539
|
+
"hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
|
|
1540
|
+
mode === "smooth" ? "rounded-2xl" : "rounded-xl"
|
|
1541
|
+
);
|
|
1542
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1543
|
+
controls === "top-right" && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-end gap-2", children: [
|
|
1544
|
+
/* @__PURE__ */ jsx(
|
|
1545
|
+
"button",
|
|
1546
|
+
{
|
|
1547
|
+
type: "button",
|
|
1548
|
+
"aria-label": "Previous",
|
|
1549
|
+
className: controlClasses,
|
|
1550
|
+
onClick: () => scrollByItem(-1),
|
|
1551
|
+
disabled: !canPrev,
|
|
1552
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
1553
|
+
}
|
|
1554
|
+
),
|
|
1555
|
+
/* @__PURE__ */ jsx(
|
|
1556
|
+
"button",
|
|
1557
|
+
{
|
|
1558
|
+
type: "button",
|
|
1559
|
+
"aria-label": "Next",
|
|
1560
|
+
className: controlClasses,
|
|
1561
|
+
onClick: () => scrollByItem(1),
|
|
1562
|
+
disabled: !canNext,
|
|
1563
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
1564
|
+
}
|
|
1565
|
+
)
|
|
1566
|
+
] }),
|
|
1567
|
+
/* @__PURE__ */ jsx(
|
|
1568
|
+
"div",
|
|
1569
|
+
{
|
|
1570
|
+
ref: trackRef,
|
|
1571
|
+
role: "region",
|
|
1572
|
+
"aria-label": ariaLabel,
|
|
1573
|
+
className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1574
|
+
children: React31.Children.map(
|
|
1575
|
+
children,
|
|
1576
|
+
(child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
|
|
1577
|
+
)
|
|
1578
|
+
}
|
|
1579
|
+
)
|
|
1580
|
+
] });
|
|
1581
|
+
}
|
|
1024
1582
|
var translateMap2 = {
|
|
1025
1583
|
up: [0, 20],
|
|
1026
1584
|
down: [0, -20],
|
|
@@ -1038,9 +1596,9 @@ function StaggerChildren({
|
|
|
1038
1596
|
className,
|
|
1039
1597
|
...props
|
|
1040
1598
|
}) {
|
|
1041
|
-
const ref =
|
|
1042
|
-
const [visible, setVisible] =
|
|
1043
|
-
|
|
1599
|
+
const ref = React31.useRef(null);
|
|
1600
|
+
const [visible, setVisible] = React31.useState(false);
|
|
1601
|
+
React31.useEffect(() => {
|
|
1044
1602
|
const el = ref.current;
|
|
1045
1603
|
if (!el) return;
|
|
1046
1604
|
const observer = new IntersectionObserver(
|
|
@@ -1058,7 +1616,7 @@ function StaggerChildren({
|
|
|
1058
1616
|
return () => observer.disconnect();
|
|
1059
1617
|
}, [once]);
|
|
1060
1618
|
const [tx, ty] = translateMap2[direction];
|
|
1061
|
-
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children:
|
|
1619
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React31.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
|
|
1062
1620
|
"div",
|
|
1063
1621
|
{
|
|
1064
1622
|
className: "h-full",
|
|
@@ -1076,7 +1634,8 @@ function FeaturesSection({
|
|
|
1076
1634
|
title = "Everything you need to ship fast",
|
|
1077
1635
|
description = "A complete system of components, blocks, and hooks designed to work together - and get out of your way.",
|
|
1078
1636
|
badge = "Features",
|
|
1079
|
-
features
|
|
1637
|
+
features,
|
|
1638
|
+
layout = "grid"
|
|
1080
1639
|
}) {
|
|
1081
1640
|
const mode = useUIVariant();
|
|
1082
1641
|
const sectionClasses = cn$1(
|
|
@@ -1085,18 +1644,19 @@ function FeaturesSection({
|
|
|
1085
1644
|
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1086
1645
|
!mode && "rounded-2xl border"
|
|
1087
1646
|
);
|
|
1088
|
-
|
|
1647
|
+
const cards = features.map((feature) => /* @__PURE__ */ jsx(
|
|
1648
|
+
FeatureCard,
|
|
1649
|
+
{
|
|
1650
|
+
icon: feature.icon,
|
|
1651
|
+
title: feature.title,
|
|
1652
|
+
description: feature.description,
|
|
1653
|
+
href: feature.href
|
|
1654
|
+
},
|
|
1655
|
+
feature.title
|
|
1656
|
+
));
|
|
1657
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: cn$1("mx-auto", layout === "carousel" ? "max-w-5xl" : "max-w-4xl"), children: [
|
|
1089
1658
|
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1090
|
-
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children:
|
|
1091
|
-
FeatureCard,
|
|
1092
|
-
{
|
|
1093
|
-
icon: feature.icon,
|
|
1094
|
-
title: feature.title,
|
|
1095
|
-
description: feature.description,
|
|
1096
|
-
href: feature.href
|
|
1097
|
-
},
|
|
1098
|
-
feature.title
|
|
1099
|
-
)) })
|
|
1659
|
+
layout === "carousel" ? /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsx(Carousel, { className: "mt-10", ariaLabel: title, children: cards }) }) : /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: cards })
|
|
1100
1660
|
] }) }) });
|
|
1101
1661
|
}
|
|
1102
1662
|
function GroupedFeaturesSection({
|
|
@@ -1226,106 +1786,6 @@ function TechStackSection({
|
|
|
1226
1786
|
}) })
|
|
1227
1787
|
] }) }) });
|
|
1228
1788
|
}
|
|
1229
|
-
function CarouselSection({
|
|
1230
|
-
title = "Explore the platform",
|
|
1231
|
-
description,
|
|
1232
|
-
badge = "Features",
|
|
1233
|
-
features
|
|
1234
|
-
}) {
|
|
1235
|
-
const mode = useUIVariant();
|
|
1236
|
-
const trackRef = React29.useRef(null);
|
|
1237
|
-
const [canPrev, setCanPrev] = React29.useState(false);
|
|
1238
|
-
const [canNext, setCanNext] = React29.useState(true);
|
|
1239
|
-
const updateScrollState = React29.useCallback(() => {
|
|
1240
|
-
const track = trackRef.current;
|
|
1241
|
-
if (!track) return;
|
|
1242
|
-
setCanPrev(track.scrollLeft > 8);
|
|
1243
|
-
setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
|
|
1244
|
-
}, []);
|
|
1245
|
-
React29.useEffect(() => {
|
|
1246
|
-
const track = trackRef.current;
|
|
1247
|
-
if (!track) return;
|
|
1248
|
-
updateScrollState();
|
|
1249
|
-
track.addEventListener("scroll", updateScrollState, { passive: true });
|
|
1250
|
-
const observer = new ResizeObserver(updateScrollState);
|
|
1251
|
-
observer.observe(track);
|
|
1252
|
-
return () => {
|
|
1253
|
-
track.removeEventListener("scroll", updateScrollState);
|
|
1254
|
-
observer.disconnect();
|
|
1255
|
-
};
|
|
1256
|
-
}, [updateScrollState]);
|
|
1257
|
-
const scrollByCard = (direction) => {
|
|
1258
|
-
const track = trackRef.current;
|
|
1259
|
-
if (!track) return;
|
|
1260
|
-
const card = track.querySelector("[data-carousel-item]");
|
|
1261
|
-
const step = card ? card.offsetWidth + 24 : track.clientWidth * 0.8;
|
|
1262
|
-
track.scrollBy({ left: direction * step, behavior: "smooth" });
|
|
1263
|
-
};
|
|
1264
|
-
const sectionClasses = cn$1(
|
|
1265
|
-
"overflow-hidden bg-card",
|
|
1266
|
-
mode === "smooth" && "rounded-3xl border",
|
|
1267
|
-
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1268
|
-
!mode && "rounded-2xl border"
|
|
1269
|
-
);
|
|
1270
|
-
const controlClasses = cn$1(
|
|
1271
|
-
"flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
|
|
1272
|
-
"hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
|
|
1273
|
-
mode === "smooth" ? "rounded-2xl" : "rounded-xl"
|
|
1274
|
-
);
|
|
1275
|
-
return /* @__PURE__ */ jsx("section", { className: sectionClasses, 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: [
|
|
1276
|
-
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1277
|
-
/* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
|
|
1278
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-end gap-2", children: [
|
|
1279
|
-
/* @__PURE__ */ jsx(
|
|
1280
|
-
"button",
|
|
1281
|
-
{
|
|
1282
|
-
type: "button",
|
|
1283
|
-
"aria-label": "Previous",
|
|
1284
|
-
className: controlClasses,
|
|
1285
|
-
onClick: () => scrollByCard(-1),
|
|
1286
|
-
disabled: !canPrev,
|
|
1287
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
1288
|
-
}
|
|
1289
|
-
),
|
|
1290
|
-
/* @__PURE__ */ jsx(
|
|
1291
|
-
"button",
|
|
1292
|
-
{
|
|
1293
|
-
type: "button",
|
|
1294
|
-
"aria-label": "Next",
|
|
1295
|
-
className: controlClasses,
|
|
1296
|
-
onClick: () => scrollByCard(1),
|
|
1297
|
-
disabled: !canNext,
|
|
1298
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
1299
|
-
}
|
|
1300
|
-
)
|
|
1301
|
-
] }),
|
|
1302
|
-
/* @__PURE__ */ jsx(
|
|
1303
|
-
"div",
|
|
1304
|
-
{
|
|
1305
|
-
ref: trackRef,
|
|
1306
|
-
className: "mt-4 flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1307
|
-
children: features.map((feature) => /* @__PURE__ */ jsx(
|
|
1308
|
-
"div",
|
|
1309
|
-
{
|
|
1310
|
-
"data-carousel-item": true,
|
|
1311
|
-
className: "w-[280px] shrink-0 snap-start sm:w-[320px]",
|
|
1312
|
-
children: /* @__PURE__ */ jsx(
|
|
1313
|
-
FeatureCard,
|
|
1314
|
-
{
|
|
1315
|
-
icon: feature.icon,
|
|
1316
|
-
title: feature.title,
|
|
1317
|
-
description: feature.description,
|
|
1318
|
-
href: feature.href
|
|
1319
|
-
}
|
|
1320
|
-
)
|
|
1321
|
-
},
|
|
1322
|
-
feature.title
|
|
1323
|
-
))
|
|
1324
|
-
}
|
|
1325
|
-
)
|
|
1326
|
-
] })
|
|
1327
|
-
] }) }) });
|
|
1328
|
-
}
|
|
1329
1789
|
var speedDuration = {
|
|
1330
1790
|
slow: "280s",
|
|
1331
1791
|
normal: "180s",
|
|
@@ -1406,79 +1866,42 @@ function FeatureMarqueeSection({
|
|
|
1406
1866
|
` })
|
|
1407
1867
|
] });
|
|
1408
1868
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
})
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1423
|
-
!mode && "rounded-2xl border"
|
|
1424
|
-
);
|
|
1425
|
-
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
|
|
1426
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
|
|
1427
|
-
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
|
|
1428
|
-
/* @__PURE__ */ jsx("div", { className: cn$1("mb-4 inline-flex h-10 w-10 items-center justify-center bg-primary/10 text-primary", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
|
|
1429
|
-
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
|
|
1430
|
-
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1431
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
|
|
1432
|
-
renderLink({
|
|
1433
|
-
href: primaryCta.href,
|
|
1434
|
-
children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
|
|
1435
|
-
primaryCta.label,
|
|
1436
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1437
|
-
] })
|
|
1438
|
-
}),
|
|
1439
|
-
secondaryCta && renderLink({
|
|
1440
|
-
href: secondaryCta.href,
|
|
1441
|
-
children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
1442
|
-
})
|
|
1443
|
-
] }),
|
|
1444
|
-
footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
|
|
1445
|
-
] }) })
|
|
1446
|
-
] }) });
|
|
1869
|
+
function useIntersectionObserver(options) {
|
|
1870
|
+
const ref = React31.useRef(null);
|
|
1871
|
+
const [isIntersecting, setIsIntersecting] = React31.useState(false);
|
|
1872
|
+
React31.useEffect(() => {
|
|
1873
|
+
const el = ref.current;
|
|
1874
|
+
if (!el) return;
|
|
1875
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
1876
|
+
setIsIntersecting(entry.isIntersecting);
|
|
1877
|
+
}, options);
|
|
1878
|
+
observer.observe(el);
|
|
1879
|
+
return () => observer.disconnect();
|
|
1880
|
+
}, []);
|
|
1881
|
+
return [ref, isIntersecting];
|
|
1447
1882
|
}
|
|
1448
|
-
var
|
|
1449
|
-
function
|
|
1883
|
+
var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1884
|
+
function ShowcaseCta({
|
|
1450
1885
|
heading,
|
|
1451
1886
|
description,
|
|
1452
1887
|
primaryCta,
|
|
1453
1888
|
footnote,
|
|
1454
1889
|
icon,
|
|
1455
|
-
renderLink =
|
|
1890
|
+
renderLink = defaultRenderLink5,
|
|
1891
|
+
sectionClasses
|
|
1456
1892
|
}) {
|
|
1457
|
-
const ref =
|
|
1458
|
-
const [visible, setVisible] =
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
const observer = new IntersectionObserver(
|
|
1463
|
-
([entry]) => {
|
|
1464
|
-
if (entry.isIntersecting) {
|
|
1465
|
-
setVisible(true);
|
|
1466
|
-
observer.disconnect();
|
|
1467
|
-
}
|
|
1468
|
-
},
|
|
1469
|
-
{ threshold: 0.15 }
|
|
1470
|
-
);
|
|
1471
|
-
observer.observe(el);
|
|
1472
|
-
return () => observer.disconnect();
|
|
1473
|
-
}, []);
|
|
1893
|
+
const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
|
|
1894
|
+
const [visible, setVisible] = React31.useState(false);
|
|
1895
|
+
React31.useEffect(() => {
|
|
1896
|
+
if (intersecting) setVisible(true);
|
|
1897
|
+
}, [intersecting]);
|
|
1474
1898
|
return /* @__PURE__ */ jsxs(
|
|
1475
1899
|
"section",
|
|
1476
1900
|
{
|
|
1477
1901
|
ref,
|
|
1478
1902
|
className: cn$1(
|
|
1479
|
-
|
|
1480
|
-
"
|
|
1481
|
-
"transition-[opacity,transform] duration-700 ease-out",
|
|
1903
|
+
sectionClasses,
|
|
1904
|
+
"relative transition-[opacity,transform] duration-700 ease-out",
|
|
1482
1905
|
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6"
|
|
1483
1906
|
),
|
|
1484
1907
|
children: [
|
|
@@ -1487,11 +1910,11 @@ function CtaCardSection({
|
|
|
1487
1910
|
{
|
|
1488
1911
|
className: "pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
1489
1912
|
style: {
|
|
1490
|
-
opacity: visible ? 0.
|
|
1913
|
+
opacity: visible ? 0.1 : 0,
|
|
1491
1914
|
transform: visible ? "scale(1) rotate(0deg)" : "scale(0.9) rotate(-5deg)",
|
|
1492
1915
|
transition: "opacity 700ms ease 200ms, transform 700ms ease 200ms"
|
|
1493
1916
|
},
|
|
1494
|
-
children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground
|
|
1917
|
+
children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground", strokeWidth: 0.75 })
|
|
1495
1918
|
}
|
|
1496
1919
|
),
|
|
1497
1920
|
/* @__PURE__ */ jsxs("div", { className: "relative z-10 px-6 py-20 text-center sm:px-10 sm:py-28", children: [
|
|
@@ -1499,9 +1922,9 @@ function CtaCardSection({
|
|
|
1499
1922
|
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1500
1923
|
/* @__PURE__ */ jsx("div", { className: "mt-8", children: renderLink({
|
|
1501
1924
|
href: primaryCta.href,
|
|
1502
|
-
children: /* @__PURE__ */ jsxs(
|
|
1925
|
+
children: /* @__PURE__ */ jsxs(Button, { size: "lg", className: "rounded-full px-6", children: [
|
|
1503
1926
|
primaryCta.label,
|
|
1504
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
|
|
1927
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1505
1928
|
] })
|
|
1506
1929
|
}) }),
|
|
1507
1930
|
footnote && /* @__PURE__ */ jsx("p", { className: "mt-5 text-sm text-muted-foreground", children: footnote })
|
|
@@ -1510,6 +1933,49 @@ function CtaCardSection({
|
|
|
1510
1933
|
}
|
|
1511
1934
|
);
|
|
1512
1935
|
}
|
|
1936
|
+
function CtaSection(props) {
|
|
1937
|
+
const {
|
|
1938
|
+
heading,
|
|
1939
|
+
description,
|
|
1940
|
+
primaryCta,
|
|
1941
|
+
secondaryCta,
|
|
1942
|
+
footnote,
|
|
1943
|
+
variant = "default",
|
|
1944
|
+
renderLink = defaultRenderLink5
|
|
1945
|
+
} = props;
|
|
1946
|
+
const mode = useUIVariant();
|
|
1947
|
+
const sectionClasses = cn$1(
|
|
1948
|
+
"overflow-hidden bg-card",
|
|
1949
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1950
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1951
|
+
!mode && "rounded-2xl border"
|
|
1952
|
+
);
|
|
1953
|
+
if (variant === "showcase") {
|
|
1954
|
+
return /* @__PURE__ */ jsx(ShowcaseCta, { ...props, sectionClasses });
|
|
1955
|
+
}
|
|
1956
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
|
|
1957
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
|
|
1958
|
+
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
|
|
1959
|
+
/* @__PURE__ */ jsx("div", { className: cn$1("mb-4 inline-flex h-10 w-10 items-center justify-center bg-primary/10 text-primary", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
|
|
1960
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
|
|
1961
|
+
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1962
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
|
|
1963
|
+
renderLink({
|
|
1964
|
+
href: primaryCta.href,
|
|
1965
|
+
children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
|
|
1966
|
+
primaryCta.label,
|
|
1967
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1968
|
+
] })
|
|
1969
|
+
}),
|
|
1970
|
+
secondaryCta && renderLink({
|
|
1971
|
+
href: secondaryCta.href,
|
|
1972
|
+
children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
1973
|
+
})
|
|
1974
|
+
] }),
|
|
1975
|
+
footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
|
|
1976
|
+
] }) })
|
|
1977
|
+
] }) });
|
|
1978
|
+
}
|
|
1513
1979
|
function PricingCard({
|
|
1514
1980
|
name,
|
|
1515
1981
|
price,
|
|
@@ -1603,8 +2069,8 @@ function CountdownTimer({
|
|
|
1603
2069
|
className,
|
|
1604
2070
|
compact = false
|
|
1605
2071
|
}) {
|
|
1606
|
-
const [timeLeft, setTimeLeft] =
|
|
1607
|
-
|
|
2072
|
+
const [timeLeft, setTimeLeft] = React31.useState(() => getTimeLeft(deadline));
|
|
2073
|
+
React31.useEffect(() => {
|
|
1608
2074
|
setTimeLeft(getTimeLeft(deadline));
|
|
1609
2075
|
const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
|
|
1610
2076
|
return () => clearInterval(id);
|
|
@@ -1630,7 +2096,7 @@ function CountdownTimer({
|
|
|
1630
2096
|
];
|
|
1631
2097
|
return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
|
|
1632
2098
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
1633
|
-
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(
|
|
2099
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
|
|
1634
2100
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
1635
2101
|
String(value).padStart(2, "0"),
|
|
1636
2102
|
/* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
|
|
@@ -1639,7 +2105,7 @@ function CountdownTimer({
|
|
|
1639
2105
|
] }, unit)) })
|
|
1640
2106
|
] });
|
|
1641
2107
|
}
|
|
1642
|
-
var
|
|
2108
|
+
var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1643
2109
|
function PricingSection({
|
|
1644
2110
|
title = "Simple, transparent pricing",
|
|
1645
2111
|
description = "Start for free. Scale as you grow. No hidden fees.",
|
|
@@ -1647,13 +2113,13 @@ function PricingSection({
|
|
|
1647
2113
|
plans,
|
|
1648
2114
|
saveBadge = "Save 34%",
|
|
1649
2115
|
isAuthenticated,
|
|
1650
|
-
renderLink =
|
|
2116
|
+
renderLink = defaultRenderLink6,
|
|
1651
2117
|
footnote,
|
|
1652
2118
|
mode = "subscription",
|
|
1653
2119
|
foundingDeadline,
|
|
1654
2120
|
renderPlanFooter
|
|
1655
2121
|
}) {
|
|
1656
|
-
const [annual, setAnnual] =
|
|
2122
|
+
const [annual, setAnnual] = React31.useState(false);
|
|
1657
2123
|
const isOneTime = mode === "one-time";
|
|
1658
2124
|
const uiMode = useUIVariant();
|
|
1659
2125
|
const sectionClasses = cn$1(
|
|
@@ -1857,9 +2323,16 @@ function TeamSection({
|
|
|
1857
2323
|
title = "The people behind Olwiba",
|
|
1858
2324
|
description = "A small team with deep experience in design systems, open source, and developer tooling."
|
|
1859
2325
|
}) {
|
|
1860
|
-
|
|
2326
|
+
const mode = useUIVariant();
|
|
2327
|
+
const sectionClasses = cn$1(
|
|
2328
|
+
"overflow-hidden bg-card",
|
|
2329
|
+
mode === "smooth" && "rounded-3xl border",
|
|
2330
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
2331
|
+
!mode && "rounded-2xl border"
|
|
2332
|
+
);
|
|
2333
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, 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: [
|
|
1861
2334
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1862
|
-
/* @__PURE__ */ jsx(Badge
|
|
2335
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "mb-4", children: badge }),
|
|
1863
2336
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
1864
2337
|
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description })
|
|
1865
2338
|
] }),
|
|
@@ -1915,10 +2388,10 @@ function CountUp({
|
|
|
1915
2388
|
className,
|
|
1916
2389
|
...props
|
|
1917
2390
|
}) {
|
|
1918
|
-
const ref =
|
|
1919
|
-
const [value, setValue] =
|
|
1920
|
-
const hasStarted =
|
|
1921
|
-
|
|
2391
|
+
const ref = React31.useRef(null);
|
|
2392
|
+
const [value, setValue] = React31.useState(from);
|
|
2393
|
+
const hasStarted = React31.useRef(false);
|
|
2394
|
+
React31.useEffect(() => {
|
|
1922
2395
|
const el = ref.current;
|
|
1923
2396
|
if (!el) return;
|
|
1924
2397
|
const observer = new IntersectionObserver(
|
|
@@ -2011,20 +2484,27 @@ function NewsletterSection({
|
|
|
2011
2484
|
successDescription = defaults.successDescription,
|
|
2012
2485
|
onSubscribe
|
|
2013
2486
|
} = {}) {
|
|
2014
|
-
const [email, setEmail] =
|
|
2015
|
-
const [submitted, setSubmitted] =
|
|
2487
|
+
const [email, setEmail] = React31.useState("");
|
|
2488
|
+
const [submitted, setSubmitted] = React31.useState(false);
|
|
2489
|
+
const mode = useUIVariant();
|
|
2490
|
+
const sectionClasses = cn$1(
|
|
2491
|
+
"overflow-hidden bg-card",
|
|
2492
|
+
mode === "smooth" && "rounded-3xl border",
|
|
2493
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
2494
|
+
!mode && "rounded-2xl border"
|
|
2495
|
+
);
|
|
2016
2496
|
async function handleSubmit(e) {
|
|
2017
2497
|
e.preventDefault();
|
|
2018
2498
|
if (!email) return;
|
|
2019
2499
|
await onSubscribe?.(email);
|
|
2020
2500
|
setSubmitted(true);
|
|
2021
2501
|
}
|
|
2022
|
-
return /* @__PURE__ */ jsx("section", { className:
|
|
2502
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
2023
2503
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.1),transparent_60%)]" }),
|
|
2024
2504
|
/* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-xl text-center", children: [
|
|
2025
2505
|
/* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-center gap-2", children: [
|
|
2026
2506
|
/* @__PURE__ */ jsx("div", { className: "inline-flex h-8 w-8 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Mail, { className: "size-4" }) }),
|
|
2027
|
-
/* @__PURE__ */ jsx(Badge
|
|
2507
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge })
|
|
2028
2508
|
] }),
|
|
2029
2509
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
2030
2510
|
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-sm text-pretty text-muted-foreground", children: description }),
|
|
@@ -2045,7 +2525,7 @@ function NewsletterSection({
|
|
|
2045
2525
|
}
|
|
2046
2526
|
),
|
|
2047
2527
|
/* @__PURE__ */ jsx(
|
|
2048
|
-
Input
|
|
2528
|
+
Input,
|
|
2049
2529
|
{
|
|
2050
2530
|
type: "email",
|
|
2051
2531
|
placeholder,
|
|
@@ -2055,7 +2535,7 @@ function NewsletterSection({
|
|
|
2055
2535
|
className: "flex-1"
|
|
2056
2536
|
}
|
|
2057
2537
|
),
|
|
2058
|
-
/* @__PURE__ */ jsxs(Button
|
|
2538
|
+
/* @__PURE__ */ jsxs(Button, { type: "submit", children: [
|
|
2059
2539
|
submitLabel,
|
|
2060
2540
|
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
2061
2541
|
] })
|
|
@@ -2096,15 +2576,22 @@ function ContactSection({
|
|
|
2096
2576
|
sendAnotherLabel = defaults2.sendAnotherLabel,
|
|
2097
2577
|
onSubmit
|
|
2098
2578
|
} = {}) {
|
|
2099
|
-
const [submitted, setSubmitted] =
|
|
2579
|
+
const [submitted, setSubmitted] = React31.useState(false);
|
|
2580
|
+
const mode = useUIVariant();
|
|
2581
|
+
const sectionClasses = cn$1(
|
|
2582
|
+
"overflow-hidden bg-card",
|
|
2583
|
+
mode === "smooth" && "rounded-3xl border",
|
|
2584
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
2585
|
+
!mode && "rounded-2xl border"
|
|
2586
|
+
);
|
|
2100
2587
|
async function handleSubmit(e) {
|
|
2101
2588
|
e.preventDefault();
|
|
2102
2589
|
await onSubmit?.(e);
|
|
2103
2590
|
setSubmitted(true);
|
|
2104
2591
|
}
|
|
2105
|
-
return /* @__PURE__ */ jsx("section", { className:
|
|
2592
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
2106
2593
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-2xl text-center", children: [
|
|
2107
|
-
badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge
|
|
2594
|
+
badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
|
|
2108
2595
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl", children: title }),
|
|
2109
2596
|
/* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: description }),
|
|
2110
2597
|
contactInfo && contactInfo.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-8 flex flex-wrap justify-center gap-6", children: contactInfo.map(({ icon: Icon = Mail, label, value }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
@@ -2116,7 +2603,7 @@ function ContactSection({
|
|
|
2116
2603
|
/* @__PURE__ */ jsx("div", { className: "mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(MessageSquare, { className: "size-6" }) }),
|
|
2117
2604
|
/* @__PURE__ */ jsx("h3", { className: "mt-5 text-lg font-semibold", children: successTitle }),
|
|
2118
2605
|
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: successDescription }),
|
|
2119
|
-
/* @__PURE__ */ jsx(Button
|
|
2606
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
|
|
2120
2607
|
] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "mx-auto mt-16 max-w-xl sm:mt-20", children: [
|
|
2121
2608
|
/* @__PURE__ */ jsx(
|
|
2122
2609
|
"input",
|
|
@@ -2133,7 +2620,7 @@ function ContactSection({
|
|
|
2133
2620
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2134
2621
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-first", className: "block text-sm/6 font-semibold", children: "First name" }),
|
|
2135
2622
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2136
|
-
Input
|
|
2623
|
+
Input,
|
|
2137
2624
|
{
|
|
2138
2625
|
id: "contact-first",
|
|
2139
2626
|
name: "firstName",
|
|
@@ -2146,7 +2633,7 @@ function ContactSection({
|
|
|
2146
2633
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2147
2634
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-last", className: "block text-sm/6 font-semibold", children: "Last name" }),
|
|
2148
2635
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2149
|
-
Input
|
|
2636
|
+
Input,
|
|
2150
2637
|
{
|
|
2151
2638
|
id: "contact-last",
|
|
2152
2639
|
name: "lastName",
|
|
@@ -2163,7 +2650,7 @@ function ContactSection({
|
|
|
2163
2650
|
/* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
|
|
2164
2651
|
] }),
|
|
2165
2652
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2166
|
-
Input
|
|
2653
|
+
Input,
|
|
2167
2654
|
{
|
|
2168
2655
|
id: "contact-company",
|
|
2169
2656
|
name: "company",
|
|
@@ -2175,7 +2662,7 @@ function ContactSection({
|
|
|
2175
2662
|
/* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
|
|
2176
2663
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-email", className: "block text-sm/6 font-semibold", children: "Email" }),
|
|
2177
2664
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2178
|
-
Input
|
|
2665
|
+
Input,
|
|
2179
2666
|
{
|
|
2180
2667
|
id: "contact-email",
|
|
2181
2668
|
name: "email",
|
|
@@ -2221,7 +2708,7 @@ function ContactSection({
|
|
|
2221
2708
|
/* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
|
|
2222
2709
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-message", className: "block text-sm/6 font-semibold", children: "Message" }),
|
|
2223
2710
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2224
|
-
Textarea
|
|
2711
|
+
Textarea,
|
|
2225
2712
|
{
|
|
2226
2713
|
id: "contact-message",
|
|
2227
2714
|
name: "message",
|
|
@@ -2269,7 +2756,7 @@ function ContactSection({
|
|
|
2269
2756
|
)
|
|
2270
2757
|
] })
|
|
2271
2758
|
] }),
|
|
2272
|
-
/* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button
|
|
2759
|
+
/* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", children: [
|
|
2273
2760
|
"Let's talk",
|
|
2274
2761
|
/* @__PURE__ */ jsx(Send, { className: "ml-2 size-4" })
|
|
2275
2762
|
] }) })
|
|
@@ -2379,15 +2866,15 @@ function QualificationSection({
|
|
|
2379
2866
|
}) })
|
|
2380
2867
|
] }) }) });
|
|
2381
2868
|
}
|
|
2382
|
-
var
|
|
2869
|
+
var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
2383
2870
|
function Navbar({
|
|
2384
2871
|
brand,
|
|
2385
2872
|
navLinks,
|
|
2386
2873
|
cta,
|
|
2387
2874
|
controls,
|
|
2388
|
-
renderLink =
|
|
2875
|
+
renderLink = defaultRenderLink7
|
|
2389
2876
|
}) {
|
|
2390
|
-
const [open, setOpen] =
|
|
2877
|
+
const [open, setOpen] = React31.useState(false);
|
|
2391
2878
|
const brandHref = brand.href ?? "/";
|
|
2392
2879
|
const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
|
|
2393
2880
|
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: [
|
|
@@ -2404,7 +2891,7 @@ function Navbar({
|
|
|
2404
2891
|
children: link.label
|
|
2405
2892
|
}) }, link.label)) }),
|
|
2406
2893
|
hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
|
|
2407
|
-
controls?.map((control, i) => /* @__PURE__ */ jsx(
|
|
2894
|
+
controls?.map((control, i) => /* @__PURE__ */ jsx(React31.Fragment, { children: control }, i)),
|
|
2408
2895
|
(cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
|
|
2409
2896
|
cta?.secondary && renderLink({
|
|
2410
2897
|
href: cta.secondary.href,
|
|
@@ -2428,7 +2915,7 @@ function Navbar({
|
|
|
2428
2915
|
/* @__PURE__ */ jsx("span", { children: brand.name })
|
|
2429
2916
|
] })
|
|
2430
2917
|
}) }) }),
|
|
2431
|
-
controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(
|
|
2918
|
+
controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React31.Fragment, { children: control }, i)) }) : null,
|
|
2432
2919
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
2433
2920
|
/* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
|
|
2434
2921
|
href: link.href,
|
|
@@ -2449,14 +2936,14 @@ function Navbar({
|
|
|
2449
2936
|
] })
|
|
2450
2937
|
] }) });
|
|
2451
2938
|
}
|
|
2452
|
-
var
|
|
2939
|
+
var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
2453
2940
|
function Footer({
|
|
2454
2941
|
brand,
|
|
2455
2942
|
navLinks,
|
|
2456
2943
|
socialLinks,
|
|
2457
2944
|
legal,
|
|
2458
2945
|
status,
|
|
2459
|
-
renderLink =
|
|
2946
|
+
renderLink = defaultRenderLink8
|
|
2460
2947
|
}) {
|
|
2461
2948
|
const brandHref = brand.href ?? "/";
|
|
2462
2949
|
const copyrightText = legal ?? `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${brand.name}. All rights reserved.`;
|
|
@@ -2577,7 +3064,7 @@ function LogoStrip({
|
|
|
2577
3064
|
] });
|
|
2578
3065
|
}
|
|
2579
3066
|
function GridUnderlay({ variant, size = 24, className, ...props }) {
|
|
2580
|
-
const id =
|
|
3067
|
+
const id = React31.useId();
|
|
2581
3068
|
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: [
|
|
2582
3069
|
/* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
|
|
2583
3070
|
/* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
|
|
@@ -2605,9 +3092,9 @@ function GlowUnderlay({
|
|
|
2605
3092
|
style,
|
|
2606
3093
|
...props
|
|
2607
3094
|
}) {
|
|
2608
|
-
const [pos, setPos] =
|
|
2609
|
-
const containerRef =
|
|
2610
|
-
|
|
3095
|
+
const [pos, setPos] = React31.useState({ x: "50%", y: "50%" });
|
|
3096
|
+
const containerRef = React31.useRef(null);
|
|
3097
|
+
React31.useEffect(() => {
|
|
2611
3098
|
if (!followCursor) return;
|
|
2612
3099
|
const handleMouseMove = (e) => {
|
|
2613
3100
|
const el = containerRef.current;
|
|
@@ -2746,8 +3233,8 @@ function PageTransition({
|
|
|
2746
3233
|
style,
|
|
2747
3234
|
...props
|
|
2748
3235
|
}) {
|
|
2749
|
-
const [mounted, setMounted] =
|
|
2750
|
-
|
|
3236
|
+
const [mounted, setMounted] = React31.useState(false);
|
|
3237
|
+
React31.useEffect(() => {
|
|
2751
3238
|
const id = requestAnimationFrame(() => setMounted(true));
|
|
2752
3239
|
return () => cancelAnimationFrame(id);
|
|
2753
3240
|
}, []);
|
|
@@ -2774,8 +3261,8 @@ function Spotlight({
|
|
|
2774
3261
|
triggerClassName,
|
|
2775
3262
|
shortcut = "\u2318K"
|
|
2776
3263
|
}) {
|
|
2777
|
-
const [open, setOpen] =
|
|
2778
|
-
|
|
3264
|
+
const [open, setOpen] = React31.useState(false);
|
|
3265
|
+
React31.useEffect(() => {
|
|
2779
3266
|
const down = (e) => {
|
|
2780
3267
|
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
|
2781
3268
|
e.preventDefault();
|
|
@@ -2805,7 +3292,7 @@ function Spotlight({
|
|
|
2805
3292
|
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
2806
3293
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
2807
3294
|
/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
|
|
2808
|
-
groups.map((group, i) => /* @__PURE__ */ jsxs(
|
|
3295
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
|
|
2809
3296
|
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
2810
3297
|
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
2811
3298
|
CommandItem,
|
|
@@ -2830,8 +3317,8 @@ function Spotlight({
|
|
|
2830
3317
|
var BASE = 40;
|
|
2831
3318
|
var MAGNIFY_RADIUS = 80;
|
|
2832
3319
|
function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
|
|
2833
|
-
const [mouseX, setMouseX] =
|
|
2834
|
-
const containerRef =
|
|
3320
|
+
const [mouseX, setMouseX] = React31.useState(null);
|
|
3321
|
+
const containerRef = React31.useRef(null);
|
|
2835
3322
|
function getScale(index) {
|
|
2836
3323
|
if (mouseX === null || !containerRef.current) return 1;
|
|
2837
3324
|
const container = containerRef.current;
|
|
@@ -2929,6 +3416,391 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
|
|
|
2929
3416
|
] })
|
|
2930
3417
|
] }) });
|
|
2931
3418
|
}
|
|
3419
|
+
function RegisterHotkeys({ hotkeys }) {
|
|
3420
|
+
React31.useEffect(() => {
|
|
3421
|
+
const handleKeyDown = (event) => {
|
|
3422
|
+
for (const hotkey of hotkeys) {
|
|
3423
|
+
const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
|
|
3424
|
+
if (matches) {
|
|
3425
|
+
event.preventDefault();
|
|
3426
|
+
hotkey.handler();
|
|
3427
|
+
break;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3432
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
3433
|
+
}, [hotkeys]);
|
|
3434
|
+
return null;
|
|
3435
|
+
}
|
|
3436
|
+
function useControlledOpen(defaultOpen = false) {
|
|
3437
|
+
const [isOpen, setIsOpen] = React31.useState(defaultOpen);
|
|
3438
|
+
const open = React31.useCallback(() => setIsOpen(true), []);
|
|
3439
|
+
const close = React31.useCallback(() => setIsOpen(false), []);
|
|
3440
|
+
const toggle = React31.useCallback(() => setIsOpen((prev) => !prev), []);
|
|
3441
|
+
return { isOpen, open, close, toggle, setIsOpen };
|
|
3442
|
+
}
|
|
3443
|
+
function CommandMenu({
|
|
3444
|
+
groups,
|
|
3445
|
+
placeholder = "Type a command or search\u2026",
|
|
3446
|
+
emptyMessage = "No results found.",
|
|
3447
|
+
open: openProp,
|
|
3448
|
+
onOpenChange,
|
|
3449
|
+
hotkey = true
|
|
3450
|
+
}) {
|
|
3451
|
+
const internal = useControlledOpen(false);
|
|
3452
|
+
const isOpen = openProp ?? internal.isOpen;
|
|
3453
|
+
const setOpen = React31.useCallback(
|
|
3454
|
+
(next) => {
|
|
3455
|
+
if (openProp === void 0) internal.setIsOpen(next);
|
|
3456
|
+
onOpenChange?.(next);
|
|
3457
|
+
},
|
|
3458
|
+
[openProp, onOpenChange, internal.setIsOpen]
|
|
3459
|
+
);
|
|
3460
|
+
const runItem = (item) => {
|
|
3461
|
+
setOpen(false);
|
|
3462
|
+
item.onSelect();
|
|
3463
|
+
};
|
|
3464
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3465
|
+
hotkey && /* @__PURE__ */ jsx(
|
|
3466
|
+
RegisterHotkeys,
|
|
3467
|
+
{
|
|
3468
|
+
hotkeys: [
|
|
3469
|
+
{ key: "k", meta: true, handler: () => setOpen(!isOpen) },
|
|
3470
|
+
{ key: "k", ctrl: true, handler: () => setOpen(!isOpen) }
|
|
3471
|
+
]
|
|
3472
|
+
}
|
|
3473
|
+
),
|
|
3474
|
+
/* @__PURE__ */ jsxs(CommandDialog, { open: isOpen, onOpenChange: setOpen, children: [
|
|
3475
|
+
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
3476
|
+
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
3477
|
+
/* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
|
|
3478
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
|
|
3479
|
+
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
3480
|
+
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
3481
|
+
CommandItem,
|
|
3482
|
+
{
|
|
3483
|
+
value: [item.label, ...item.keywords ?? []].join(" "),
|
|
3484
|
+
onSelect: () => runItem(item),
|
|
3485
|
+
children: [
|
|
3486
|
+
item.icon && /* @__PURE__ */ jsx(item.icon, {}),
|
|
3487
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
3488
|
+
item.shortcut && /* @__PURE__ */ jsx(CommandShortcut, { children: item.shortcut })
|
|
3489
|
+
]
|
|
3490
|
+
},
|
|
3491
|
+
item.id
|
|
3492
|
+
)) })
|
|
3493
|
+
] }, group.heading))
|
|
3494
|
+
] })
|
|
3495
|
+
] })
|
|
3496
|
+
] });
|
|
3497
|
+
}
|
|
3498
|
+
function formatBytes(bytes) {
|
|
3499
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3500
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
3501
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3502
|
+
}
|
|
3503
|
+
function makeId() {
|
|
3504
|
+
return Math.random().toString(36).slice(2, 10);
|
|
3505
|
+
}
|
|
3506
|
+
function FileUpload({
|
|
3507
|
+
accept,
|
|
3508
|
+
multiple = false,
|
|
3509
|
+
maxSizeMb,
|
|
3510
|
+
maxFiles,
|
|
3511
|
+
files: filesProp,
|
|
3512
|
+
defaultFiles,
|
|
3513
|
+
onFilesChange,
|
|
3514
|
+
onFilesAdded,
|
|
3515
|
+
disabled,
|
|
3516
|
+
hint,
|
|
3517
|
+
className
|
|
3518
|
+
}) {
|
|
3519
|
+
const [internalFiles, setInternalFiles] = React31.useState(defaultFiles ?? []);
|
|
3520
|
+
const [isDragging, setIsDragging] = React31.useState(false);
|
|
3521
|
+
const [validationError, setValidationError] = React31.useState(null);
|
|
3522
|
+
const inputRef = React31.useRef(null);
|
|
3523
|
+
const dragDepth = React31.useRef(0);
|
|
3524
|
+
const files = filesProp ?? internalFiles;
|
|
3525
|
+
const setFiles = React31.useCallback(
|
|
3526
|
+
(next) => {
|
|
3527
|
+
if (!filesProp) setInternalFiles(next);
|
|
3528
|
+
onFilesChange?.(next);
|
|
3529
|
+
},
|
|
3530
|
+
[filesProp, onFilesChange]
|
|
3531
|
+
);
|
|
3532
|
+
const acceptList = React31.useMemo(
|
|
3533
|
+
() => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
|
|
3534
|
+
[accept]
|
|
3535
|
+
);
|
|
3536
|
+
const matchesAccept = (file) => {
|
|
3537
|
+
if (!acceptList.length) return true;
|
|
3538
|
+
const name = file.name.toLowerCase();
|
|
3539
|
+
return acceptList.some(
|
|
3540
|
+
(pattern) => pattern.startsWith(".") ? name.endsWith(pattern) : file.type === pattern || file.type.startsWith(pattern.replace("/*", "/"))
|
|
3541
|
+
);
|
|
3542
|
+
};
|
|
3543
|
+
const handleFiles = (fileList) => {
|
|
3544
|
+
if (!fileList || disabled) return;
|
|
3545
|
+
const incoming = Array.from(fileList);
|
|
3546
|
+
const room = maxFiles && multiple ? Math.max(0, maxFiles - files.length) : maxFiles || Infinity;
|
|
3547
|
+
if (maxFiles && room <= 0) {
|
|
3548
|
+
setValidationError(`You can only add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`);
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
const accepted = [];
|
|
3552
|
+
let rejected = false;
|
|
3553
|
+
for (const file of incoming.slice(0, room)) {
|
|
3554
|
+
if (!matchesAccept(file)) {
|
|
3555
|
+
rejected = true;
|
|
3556
|
+
continue;
|
|
3557
|
+
}
|
|
3558
|
+
if (maxSizeMb && file.size > maxSizeMb * 1024 * 1024) {
|
|
3559
|
+
rejected = true;
|
|
3560
|
+
continue;
|
|
3561
|
+
}
|
|
3562
|
+
accepted.push(file);
|
|
3563
|
+
}
|
|
3564
|
+
setValidationError(rejected ? `Some files were skipped \u2014 check the file type and size limit.` : null);
|
|
3565
|
+
if (!accepted.length) return;
|
|
3566
|
+
const entries = accepted.map((file) => ({ id: makeId(), file, status: "pending" }));
|
|
3567
|
+
setFiles(multiple ? [...files, ...entries] : entries);
|
|
3568
|
+
onFilesAdded?.(accepted);
|
|
3569
|
+
};
|
|
3570
|
+
const removeFile = (id) => {
|
|
3571
|
+
setFiles(files.filter((f) => f.id !== id));
|
|
3572
|
+
};
|
|
3573
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-3", className), children: [
|
|
3574
|
+
/* @__PURE__ */ jsxs(
|
|
3575
|
+
"div",
|
|
3576
|
+
{
|
|
3577
|
+
role: "button",
|
|
3578
|
+
tabIndex: disabled ? -1 : 0,
|
|
3579
|
+
onClick: () => !disabled && inputRef.current?.click(),
|
|
3580
|
+
onKeyDown: (e) => {
|
|
3581
|
+
if (disabled) return;
|
|
3582
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3583
|
+
e.preventDefault();
|
|
3584
|
+
inputRef.current?.click();
|
|
3585
|
+
}
|
|
3586
|
+
},
|
|
3587
|
+
onDragOver: (e) => e.preventDefault(),
|
|
3588
|
+
onDragEnter: (e) => {
|
|
3589
|
+
e.preventDefault();
|
|
3590
|
+
dragDepth.current += 1;
|
|
3591
|
+
if (!disabled) setIsDragging(true);
|
|
3592
|
+
},
|
|
3593
|
+
onDragLeave: () => {
|
|
3594
|
+
dragDepth.current = Math.max(0, dragDepth.current - 1);
|
|
3595
|
+
if (dragDepth.current === 0) setIsDragging(false);
|
|
3596
|
+
},
|
|
3597
|
+
onDrop: (e) => {
|
|
3598
|
+
e.preventDefault();
|
|
3599
|
+
dragDepth.current = 0;
|
|
3600
|
+
setIsDragging(false);
|
|
3601
|
+
handleFiles(e.dataTransfer.files);
|
|
3602
|
+
},
|
|
3603
|
+
className: cn$1(
|
|
3604
|
+
"flex cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed px-6 py-10 text-center transition-colors",
|
|
3605
|
+
isDragging ? "border-primary bg-primary/5" : "border-border hover:border-foreground/30",
|
|
3606
|
+
disabled && "pointer-events-none opacity-50"
|
|
3607
|
+
),
|
|
3608
|
+
children: [
|
|
3609
|
+
/* @__PURE__ */ jsx(UploadCloud, { className: "size-8 text-muted-foreground" }),
|
|
3610
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
|
|
3611
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary underline underline-offset-4", children: "Click to upload" }),
|
|
3612
|
+
" or drag and drop"
|
|
3613
|
+
] }),
|
|
3614
|
+
hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint }),
|
|
3615
|
+
/* @__PURE__ */ jsx(
|
|
3616
|
+
"input",
|
|
3617
|
+
{
|
|
3618
|
+
ref: inputRef,
|
|
3619
|
+
type: "file",
|
|
3620
|
+
accept,
|
|
3621
|
+
multiple,
|
|
3622
|
+
disabled,
|
|
3623
|
+
className: "sr-only",
|
|
3624
|
+
onChange: (e) => {
|
|
3625
|
+
handleFiles(e.target.files);
|
|
3626
|
+
e.target.value = "";
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
)
|
|
3630
|
+
]
|
|
3631
|
+
}
|
|
3632
|
+
),
|
|
3633
|
+
validationError && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: validationError }),
|
|
3634
|
+
files.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: files.map(({ id, file, progress, status = "pending", error }) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-lg border bg-card/60 px-3 py-2.5", children: [
|
|
3635
|
+
/* @__PURE__ */ jsx(File, { className: "size-5 shrink-0 text-muted-foreground" }),
|
|
3636
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3637
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3638
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: file.name }),
|
|
3639
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatBytes(file.size) })
|
|
3640
|
+
] }),
|
|
3641
|
+
status === "uploading" && /* @__PURE__ */ jsx("div", { className: "mt-1.5 h-1 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progress ?? 0}%` } }) }),
|
|
3642
|
+
status === "error" && error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: error })
|
|
3643
|
+
] }),
|
|
3644
|
+
status === "uploading" && /* @__PURE__ */ jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-muted-foreground" }),
|
|
3645
|
+
status === "done" && /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4 shrink-0 text-primary" }),
|
|
3646
|
+
status === "error" && /* @__PURE__ */ jsx(AlertCircle, { className: "size-4 shrink-0 text-destructive" }),
|
|
3647
|
+
/* @__PURE__ */ jsxs(
|
|
3648
|
+
Button,
|
|
3649
|
+
{
|
|
3650
|
+
type: "button",
|
|
3651
|
+
variant: "ghost",
|
|
3652
|
+
size: "icon",
|
|
3653
|
+
className: "size-7 shrink-0",
|
|
3654
|
+
onClick: () => removeFile(id),
|
|
3655
|
+
children: [
|
|
3656
|
+
/* @__PURE__ */ jsx(X, { className: "size-3.5" }),
|
|
3657
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
3658
|
+
"Remove ",
|
|
3659
|
+
file.name
|
|
3660
|
+
] })
|
|
3661
|
+
]
|
|
3662
|
+
}
|
|
3663
|
+
)
|
|
3664
|
+
] }, id)) })
|
|
3665
|
+
] });
|
|
3666
|
+
}
|
|
3667
|
+
var variantIcon = {
|
|
3668
|
+
success: /* @__PURE__ */ jsx(CheckCircle2, { className: "size-5 text-primary" }),
|
|
3669
|
+
info: /* @__PURE__ */ jsx(Info, { className: "size-5 text-muted-foreground" }),
|
|
3670
|
+
warning: /* @__PURE__ */ jsx(AlertTriangle, { className: "size-5 text-amber-500 dark:text-amber-400" }),
|
|
3671
|
+
error: /* @__PURE__ */ jsx(AlertCircle, { className: "size-5 text-destructive" }),
|
|
3672
|
+
message: /* @__PURE__ */ jsx(Inbox, { className: "size-5 text-muted-foreground" })
|
|
3673
|
+
};
|
|
3674
|
+
function NotificationToast({
|
|
3675
|
+
variant = "info",
|
|
3676
|
+
title,
|
|
3677
|
+
description,
|
|
3678
|
+
avatar,
|
|
3679
|
+
action,
|
|
3680
|
+
secondaryAction,
|
|
3681
|
+
onDismiss
|
|
3682
|
+
}) {
|
|
3683
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full max-w-sm rounded-lg border bg-card p-4 text-card-foreground shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
3684
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0 pt-0.5", children: avatar ? (
|
|
3685
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
3686
|
+
/* @__PURE__ */ jsx("img", { src: avatar, alt: "", className: "size-9 rounded-full border object-cover" })
|
|
3687
|
+
) : variantIcon[variant] }),
|
|
3688
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
3689
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: title }),
|
|
3690
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }),
|
|
3691
|
+
(action || secondaryAction) && /* @__PURE__ */ jsxs("div", { className: cn$1("mt-3 flex gap-4", avatar && "gap-3"), children: [
|
|
3692
|
+
action && /* @__PURE__ */ jsx("button", { type: "button", onClick: action.onClick, className: "text-sm font-medium text-primary hover:underline", children: action.label }),
|
|
3693
|
+
secondaryAction && /* @__PURE__ */ jsx("button", { type: "button", onClick: secondaryAction.onClick, className: "text-sm font-medium text-muted-foreground hover:text-foreground", children: secondaryAction.label })
|
|
3694
|
+
] })
|
|
3695
|
+
] }),
|
|
3696
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "size-6 shrink-0 -mt-1 -mr-1", onClick: onDismiss, children: [
|
|
3697
|
+
/* @__PURE__ */ jsx(X, { className: "size-4" }),
|
|
3698
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Dismiss" })
|
|
3699
|
+
] })
|
|
3700
|
+
] }) });
|
|
3701
|
+
}
|
|
3702
|
+
function notify(options) {
|
|
3703
|
+
const { duration, ...toastProps } = options;
|
|
3704
|
+
return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
|
|
3705
|
+
}
|
|
3706
|
+
function NotificationsPopover({
|
|
3707
|
+
notifications,
|
|
3708
|
+
onNotificationClick,
|
|
3709
|
+
onMarkAllRead,
|
|
3710
|
+
title = "Notifications",
|
|
3711
|
+
emptyMessage = "Nothing new \u2014 you\u2019re all caught up.",
|
|
3712
|
+
align = "end",
|
|
3713
|
+
open,
|
|
3714
|
+
onOpenChange,
|
|
3715
|
+
className
|
|
3716
|
+
}) {
|
|
3717
|
+
const unreadCount = notifications.filter((n) => !n.read).length;
|
|
3718
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
|
|
3719
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: cn$1("relative size-8", className), children: [
|
|
3720
|
+
/* @__PURE__ */ jsx(Bell, { className: "size-4" }),
|
|
3721
|
+
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -right-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold leading-4 text-primary-foreground", children: unreadCount > 9 ? "9+" : unreadCount }),
|
|
3722
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
3723
|
+
title,
|
|
3724
|
+
unreadCount > 0 ? ` (${unreadCount} unread)` : ""
|
|
3725
|
+
] })
|
|
3726
|
+
] }) }),
|
|
3727
|
+
/* @__PURE__ */ jsxs(PopoverContent, { align, className: "w-80 p-0", children: [
|
|
3728
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
|
|
3729
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: title }),
|
|
3730
|
+
onMarkAllRead && unreadCount > 0 && /* @__PURE__ */ jsx(
|
|
3731
|
+
"button",
|
|
3732
|
+
{
|
|
3733
|
+
type: "button",
|
|
3734
|
+
onClick: onMarkAllRead,
|
|
3735
|
+
className: "text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
3736
|
+
children: "Mark all read"
|
|
3737
|
+
}
|
|
3738
|
+
)
|
|
3739
|
+
] }),
|
|
3740
|
+
notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 px-4 py-10 text-center", children: [
|
|
3741
|
+
/* @__PURE__ */ jsx(Inbox, { className: "size-6 text-muted-foreground" }),
|
|
3742
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
|
|
3743
|
+
] }) : /* @__PURE__ */ jsx("ul", { className: "max-h-80 overflow-y-auto", children: notifications.map((notification) => /* @__PURE__ */ jsx("li", { className: "border-b last:border-b-0", children: /* @__PURE__ */ jsxs(
|
|
3744
|
+
"button",
|
|
3745
|
+
{
|
|
3746
|
+
type: "button",
|
|
3747
|
+
onClick: () => onNotificationClick?.(notification),
|
|
3748
|
+
className: cn$1(
|
|
3749
|
+
"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/60",
|
|
3750
|
+
!notification.read && "bg-muted/40"
|
|
3751
|
+
),
|
|
3752
|
+
children: [
|
|
3753
|
+
/* @__PURE__ */ jsx(
|
|
3754
|
+
"span",
|
|
3755
|
+
{
|
|
3756
|
+
"aria-hidden": true,
|
|
3757
|
+
className: cn$1(
|
|
3758
|
+
"mt-1.5 size-2 shrink-0 rounded-full",
|
|
3759
|
+
notification.read ? "bg-transparent" : "bg-primary"
|
|
3760
|
+
)
|
|
3761
|
+
}
|
|
3762
|
+
),
|
|
3763
|
+
notification.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8 shrink-0", children: [
|
|
3764
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: notification.avatar, alt: "" }),
|
|
3765
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: notification.title.slice(0, 2).toUpperCase() })
|
|
3766
|
+
] }) : notification.icon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: notification.icon }) : null,
|
|
3767
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
3768
|
+
/* @__PURE__ */ jsx("span", { className: cn$1("block truncate text-sm", !notification.read && "font-medium"), children: notification.title }),
|
|
3769
|
+
notification.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 line-clamp-2 block text-xs text-muted-foreground", children: notification.description }),
|
|
3770
|
+
notification.timestamp && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground/70", children: notification.timestamp })
|
|
3771
|
+
] })
|
|
3772
|
+
]
|
|
3773
|
+
}
|
|
3774
|
+
) }, notification.id)) })
|
|
3775
|
+
] })
|
|
3776
|
+
] });
|
|
3777
|
+
}
|
|
3778
|
+
function ActivityFeed({
|
|
3779
|
+
items,
|
|
3780
|
+
emptyMessage = "No activity yet.",
|
|
3781
|
+
className
|
|
3782
|
+
}) {
|
|
3783
|
+
if (items.length === 0) {
|
|
3784
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("flex flex-col items-center gap-2 py-10 text-center", className), children: [
|
|
3785
|
+
/* @__PURE__ */ jsx(Activity, { className: "size-6 text-muted-foreground" }),
|
|
3786
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
|
|
3787
|
+
] });
|
|
3788
|
+
}
|
|
3789
|
+
return /* @__PURE__ */ jsx("ol", { className, children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "relative flex gap-3 pb-6 last:pb-0", children: [
|
|
3790
|
+
i !== items.length - 1 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute left-4 top-9 bottom-0 w-px -translate-x-1/2 bg-border" }),
|
|
3791
|
+
/* @__PURE__ */ jsx("span", { className: "relative z-10 flex size-8 shrink-0 items-center justify-center", children: item.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
|
|
3792
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: item.avatar, alt: "" }),
|
|
3793
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: item.initials ?? "?" })
|
|
3794
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "flex size-8 items-center justify-center rounded-full border bg-card text-muted-foreground [&>svg]:size-4", children: item.icon ?? /* @__PURE__ */ jsx(Activity, {}) }) }),
|
|
3795
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-1", children: [
|
|
3796
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
3797
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: item.title }),
|
|
3798
|
+
item.timestamp && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.timestamp })
|
|
3799
|
+
] }),
|
|
3800
|
+
item.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: item.description })
|
|
3801
|
+
] })
|
|
3802
|
+
] }, item.id)) });
|
|
3803
|
+
}
|
|
2932
3804
|
function AppScreenRoot({ children, className }) {
|
|
2933
3805
|
return /* @__PURE__ */ jsx("div", { className: cn("flex h-full flex-col", className), children });
|
|
2934
3806
|
}
|
|
@@ -3311,20 +4183,20 @@ function FullPageSpinner({ message }) {
|
|
|
3311
4183
|
message && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
|
|
3312
4184
|
] });
|
|
3313
4185
|
}
|
|
3314
|
-
var
|
|
4186
|
+
var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
3315
4187
|
function PageHeader({
|
|
3316
4188
|
title,
|
|
3317
4189
|
description,
|
|
3318
4190
|
breadcrumbs,
|
|
3319
4191
|
backButton,
|
|
3320
|
-
renderLink =
|
|
4192
|
+
renderLink = defaultRenderLink9,
|
|
3321
4193
|
className,
|
|
3322
4194
|
children
|
|
3323
4195
|
}) {
|
|
3324
4196
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
|
|
3325
4197
|
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) => {
|
|
3326
4198
|
const isLast = i === breadcrumbs.length - 1;
|
|
3327
|
-
return /* @__PURE__ */ jsxs(
|
|
4199
|
+
return /* @__PURE__ */ jsxs(React31.Fragment, { children: [
|
|
3328
4200
|
i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
|
|
3329
4201
|
isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
|
|
3330
4202
|
href: crumb.href,
|
|
@@ -3360,7 +4232,7 @@ function PageHeader({
|
|
|
3360
4232
|
] });
|
|
3361
4233
|
}
|
|
3362
4234
|
function Suspensed({ children, fallback }) {
|
|
3363
|
-
return /* @__PURE__ */ jsx(
|
|
4235
|
+
return /* @__PURE__ */ jsx(React31.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
|
|
3364
4236
|
}
|
|
3365
4237
|
function getInitialTheme() {
|
|
3366
4238
|
if (typeof document === "undefined") return "dark";
|
|
@@ -3368,7 +4240,7 @@ function getInitialTheme() {
|
|
|
3368
4240
|
return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
|
|
3369
4241
|
}
|
|
3370
4242
|
function ThemeSwitchMinimal() {
|
|
3371
|
-
const [theme, setThemeState] =
|
|
4243
|
+
const [theme, setThemeState] = React31.useState(getInitialTheme);
|
|
3372
4244
|
const toggle = () => {
|
|
3373
4245
|
const next = theme === "dark" ? "light" : "dark";
|
|
3374
4246
|
setThemeState(next);
|
|
@@ -3494,9 +4366,9 @@ function getInitialColorName() {
|
|
|
3494
4366
|
return localStorage.getItem(STORAGE_KEY) ?? "emerald";
|
|
3495
4367
|
}
|
|
3496
4368
|
function BrandColorSwitchMinimal() {
|
|
3497
|
-
const [active, setActive] =
|
|
3498
|
-
const [open, setOpen] =
|
|
3499
|
-
|
|
4369
|
+
const [active, setActive] = React31.useState(getInitialColorName);
|
|
4370
|
+
const [open, setOpen] = React31.useState(false);
|
|
4371
|
+
React31.useEffect(() => {
|
|
3500
4372
|
const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
|
|
3501
4373
|
const color = BRAND_COLORS.find((c) => c.name === saved);
|
|
3502
4374
|
if (color) applyColor(color);
|
|
@@ -3557,7 +4429,7 @@ function BrandColorSwitchMinimal() {
|
|
|
3557
4429
|
] });
|
|
3558
4430
|
}
|
|
3559
4431
|
function ThemeColorUpdater({ colorTheme }) {
|
|
3560
|
-
|
|
4432
|
+
React31.useEffect(() => {
|
|
3561
4433
|
if (colorTheme) {
|
|
3562
4434
|
document.documentElement.setAttribute("data-theme", colorTheme);
|
|
3563
4435
|
} else {
|
|
@@ -3567,7 +4439,7 @@ function ThemeColorUpdater({ colorTheme }) {
|
|
|
3567
4439
|
return null;
|
|
3568
4440
|
}
|
|
3569
4441
|
function VersionBanner({ version, message, className, onDismiss }) {
|
|
3570
|
-
const [dismissed, setDismissed] =
|
|
4442
|
+
const [dismissed, setDismissed] = React31.useState(false);
|
|
3571
4443
|
if (dismissed) return null;
|
|
3572
4444
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
|
|
3573
4445
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -3606,23 +4478,6 @@ function DevBanner({ environment = "development", className }) {
|
|
|
3606
4478
|
}
|
|
3607
4479
|
);
|
|
3608
4480
|
}
|
|
3609
|
-
function RegisterHotkeys({ hotkeys }) {
|
|
3610
|
-
React29.useEffect(() => {
|
|
3611
|
-
const handleKeyDown = (event) => {
|
|
3612
|
-
for (const hotkey of hotkeys) {
|
|
3613
|
-
const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
|
|
3614
|
-
if (matches) {
|
|
3615
|
-
event.preventDefault();
|
|
3616
|
-
hotkey.handler();
|
|
3617
|
-
break;
|
|
3618
|
-
}
|
|
3619
|
-
}
|
|
3620
|
-
};
|
|
3621
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
3622
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
3623
|
-
}, [hotkeys]);
|
|
3624
|
-
return null;
|
|
3625
|
-
}
|
|
3626
4481
|
function RootErrorFallback({ error, reset }) {
|
|
3627
4482
|
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-svh flex-col items-center justify-center gap-6 p-8 text-center", children: [
|
|
3628
4483
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
@@ -3841,36 +4696,29 @@ function useMounted() {
|
|
|
3841
4696
|
return mounted;
|
|
3842
4697
|
}
|
|
3843
4698
|
function useConfirm() {
|
|
3844
|
-
const [isOpen, setIsOpen] =
|
|
3845
|
-
const [options, setOptions] =
|
|
3846
|
-
const resolveRef =
|
|
3847
|
-
const confirm =
|
|
4699
|
+
const [isOpen, setIsOpen] = React31.useState(false);
|
|
4700
|
+
const [options, setOptions] = React31.useState({});
|
|
4701
|
+
const resolveRef = React31.useRef(null);
|
|
4702
|
+
const confirm = React31.useCallback((opts = {}) => {
|
|
3848
4703
|
setOptions(opts);
|
|
3849
4704
|
setIsOpen(true);
|
|
3850
4705
|
return new Promise((resolve) => {
|
|
3851
4706
|
resolveRef.current = resolve;
|
|
3852
4707
|
});
|
|
3853
4708
|
}, []);
|
|
3854
|
-
const handleConfirm =
|
|
4709
|
+
const handleConfirm = React31.useCallback(() => {
|
|
3855
4710
|
resolveRef.current?.(true);
|
|
3856
4711
|
setIsOpen(false);
|
|
3857
4712
|
}, []);
|
|
3858
|
-
const handleCancel =
|
|
4713
|
+
const handleCancel = React31.useCallback(() => {
|
|
3859
4714
|
resolveRef.current?.(false);
|
|
3860
4715
|
setIsOpen(false);
|
|
3861
4716
|
}, []);
|
|
3862
4717
|
return { confirm, isOpen, options, handleConfirm, handleCancel };
|
|
3863
4718
|
}
|
|
3864
|
-
function useControlledOpen(defaultOpen = false) {
|
|
3865
|
-
const [isOpen, setIsOpen] = React29.useState(defaultOpen);
|
|
3866
|
-
const open = React29.useCallback(() => setIsOpen(true), []);
|
|
3867
|
-
const close = React29.useCallback(() => setIsOpen(false), []);
|
|
3868
|
-
const toggle = React29.useCallback(() => setIsOpen((prev) => !prev), []);
|
|
3869
|
-
return { isOpen, open, close, toggle, setIsOpen };
|
|
3870
|
-
}
|
|
3871
4719
|
function useScrolledPast(threshold) {
|
|
3872
|
-
const [scrolledPast, setScrolledPast] =
|
|
3873
|
-
|
|
4720
|
+
const [scrolledPast, setScrolledPast] = React31.useState(false);
|
|
4721
|
+
React31.useEffect(() => {
|
|
3874
4722
|
const handleScroll = () => {
|
|
3875
4723
|
setScrolledPast(window.scrollY > threshold);
|
|
3876
4724
|
};
|
|
@@ -3881,42 +4729,28 @@ function useScrolledPast(threshold) {
|
|
|
3881
4729
|
return scrolledPast;
|
|
3882
4730
|
}
|
|
3883
4731
|
function useCopyToClipboard() {
|
|
3884
|
-
const [copied, setCopied] =
|
|
3885
|
-
const timeoutRef =
|
|
3886
|
-
const copy =
|
|
4732
|
+
const [copied, setCopied] = React31.useState(false);
|
|
4733
|
+
const timeoutRef = React31.useRef(void 0);
|
|
4734
|
+
const copy = React31.useCallback((text) => {
|
|
3887
4735
|
navigator.clipboard.writeText(text).then(() => {
|
|
3888
4736
|
setCopied(true);
|
|
3889
4737
|
clearTimeout(timeoutRef.current);
|
|
3890
4738
|
timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
3891
4739
|
});
|
|
3892
4740
|
}, []);
|
|
3893
|
-
|
|
4741
|
+
React31.useEffect(() => () => clearTimeout(timeoutRef.current), []);
|
|
3894
4742
|
return [copied, copy];
|
|
3895
4743
|
}
|
|
3896
4744
|
function useDebounce(value, delay) {
|
|
3897
|
-
const [debounced, setDebounced] =
|
|
3898
|
-
|
|
4745
|
+
const [debounced, setDebounced] = React31.useState(value);
|
|
4746
|
+
React31.useEffect(() => {
|
|
3899
4747
|
const timer = setTimeout(() => setDebounced(value), delay);
|
|
3900
4748
|
return () => clearTimeout(timer);
|
|
3901
4749
|
}, [value, delay]);
|
|
3902
4750
|
return debounced;
|
|
3903
4751
|
}
|
|
3904
|
-
function useIntersectionObserver(options) {
|
|
3905
|
-
const ref = React29.useRef(null);
|
|
3906
|
-
const [isIntersecting, setIsIntersecting] = React29.useState(false);
|
|
3907
|
-
React29.useEffect(() => {
|
|
3908
|
-
const el = ref.current;
|
|
3909
|
-
if (!el) return;
|
|
3910
|
-
const observer = new IntersectionObserver(([entry]) => {
|
|
3911
|
-
setIsIntersecting(entry.isIntersecting);
|
|
3912
|
-
}, options);
|
|
3913
|
-
observer.observe(el);
|
|
3914
|
-
return () => observer.disconnect();
|
|
3915
|
-
}, []);
|
|
3916
|
-
return [ref, isIntersecting];
|
|
3917
|
-
}
|
|
3918
4752
|
function useLocalStorage(key, initialValue) {
|
|
3919
|
-
const [storedValue, setStoredValue] =
|
|
4753
|
+
const [storedValue, setStoredValue] = React31.useState(() => {
|
|
3920
4754
|
if (typeof window === "undefined") return initialValue;
|
|
3921
4755
|
try {
|
|
3922
4756
|
const item = window.localStorage.getItem(key);
|
|
@@ -3925,7 +4759,7 @@ function useLocalStorage(key, initialValue) {
|
|
|
3925
4759
|
return initialValue;
|
|
3926
4760
|
}
|
|
3927
4761
|
});
|
|
3928
|
-
const setValue =
|
|
4762
|
+
const setValue = React31.useCallback(
|
|
3929
4763
|
(value) => {
|
|
3930
4764
|
try {
|
|
3931
4765
|
setStoredValue(value);
|
|
@@ -3940,11 +4774,11 @@ function useLocalStorage(key, initialValue) {
|
|
|
3940
4774
|
return [storedValue, setValue];
|
|
3941
4775
|
}
|
|
3942
4776
|
function useMediaQuery(query) {
|
|
3943
|
-
const [matches, setMatches] =
|
|
4777
|
+
const [matches, setMatches] = React31.useState(() => {
|
|
3944
4778
|
if (typeof window === "undefined") return false;
|
|
3945
4779
|
return window.matchMedia(query).matches;
|
|
3946
4780
|
});
|
|
3947
|
-
|
|
4781
|
+
React31.useEffect(() => {
|
|
3948
4782
|
if (typeof window === "undefined") return;
|
|
3949
4783
|
const mql = window.matchMedia(query);
|
|
3950
4784
|
setMatches(mql.matches);
|
|
@@ -3955,18 +4789,18 @@ function useMediaQuery(query) {
|
|
|
3955
4789
|
return matches;
|
|
3956
4790
|
}
|
|
3957
4791
|
function usePagination(total, pageSize) {
|
|
3958
|
-
const [page, setPage] =
|
|
4792
|
+
const [page, setPage] = React31.useState(1);
|
|
3959
4793
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
3960
4794
|
const offset = (page - 1) * pageSize;
|
|
3961
|
-
const goTo =
|
|
4795
|
+
const goTo = React31.useCallback(
|
|
3962
4796
|
(p) => setPage(Math.min(Math.max(1, p), totalPages)),
|
|
3963
4797
|
[totalPages]
|
|
3964
4798
|
);
|
|
3965
|
-
const next =
|
|
3966
|
-
const prev =
|
|
4799
|
+
const next = React31.useCallback(() => goTo(page + 1), [page, goTo]);
|
|
4800
|
+
const prev = React31.useCallback(() => goTo(page - 1), [page, goTo]);
|
|
3967
4801
|
return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
|
|
3968
4802
|
}
|
|
3969
4803
|
|
|
3970
|
-
export { AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BrandColorSwitchMinimal, Button, Card,
|
|
4804
|
+
export { ActivityFeed, AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, Carousel, ChangelogCard, ChangelogList, Checkbox, CommandMenu, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, CtaSection, DataTable, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, NotificationToast, NotificationsPopover, OlwibaUIProvider, OnboardingWizard, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, QualificationSection, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, SettingsSection, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, StepsSection, Suspensed, Switch, TeamMembersPanel, TeamSection, TechStackSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
|
|
3971
4805
|
//# sourceMappingURL=index.js.map
|
|
3972
4806
|
//# sourceMappingURL=index.js.map
|