@olwiba/ui 0.1.15 → 0.2.1
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 +149 -35
- package/dist/index.js +790 -408
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/app/AuthSection.tsx +5 -4
- package/src/app/BillingPanel.tsx +18 -12
- package/src/app/OnboardingWizard.tsx +19 -6
- package/src/app/SettingsSection.tsx +1 -1
- package/src/app/TeamMembersPanel.tsx +47 -37
- package/src/components/ActivityFeed.tsx +82 -0
- package/src/components/Chart.tsx +245 -0
- package/src/components/CommandMenu.tsx +7 -1
- package/src/components/DataTable.tsx +23 -13
- package/src/components/FileUpload.tsx +24 -5
- package/src/components/NotificationsPopover.tsx +146 -0
- package/src/components/Notify.tsx +4 -2
- package/src/index.ts +7 -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/mechanics/Sortable.tsx +128 -0
- package/src/marketing/CarouselSection.tsx +0 -126
- package/src/marketing/CtaCardSection.tsx +0 -95
package/dist/index.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { cn } from './chunk-AMWLSHD6.js';
|
|
2
2
|
export { MdxContent, cn } from './chunk-AMWLSHD6.js';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React32 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, 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,
|
|
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 { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, ArrowLeft, ArrowUpRight, X, ArrowRight, Sparkles,
|
|
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
9
|
import { useReactTable, getPaginationRowModel, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
|
|
10
|
+
import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core';
|
|
11
|
+
import { sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, horizontalListSortingStrategy, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
|
|
12
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
13
|
+
import { PieChart, Pie, Cell, BarChart, XAxis, YAxis, Bar, AreaChart, Area, LineChart, Line, ResponsiveContainer, Tooltip as Tooltip$1, CartesianGrid } from 'recharts';
|
|
10
14
|
import { toast } from 'sonner';
|
|
11
15
|
|
|
12
|
-
var OlwibaUIContext =
|
|
16
|
+
var OlwibaUIContext = React32.createContext({
|
|
13
17
|
isMobile: false,
|
|
14
18
|
mode: "default",
|
|
15
19
|
setMode: () => {
|
|
@@ -18,17 +22,17 @@ var OlwibaUIContext = React30.createContext({
|
|
|
18
22
|
function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
|
|
19
23
|
const detectedMobile = useIsMobile();
|
|
20
24
|
const isMobile = isMobileProp ?? detectedMobile;
|
|
21
|
-
const [mode, setMode] =
|
|
25
|
+
const [mode, setMode] = React32.useState(initialMode);
|
|
22
26
|
const variant = mode !== "default" ? mode : void 0;
|
|
23
27
|
return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
|
|
24
28
|
}
|
|
25
29
|
function useOlwibaUI() {
|
|
26
|
-
return
|
|
30
|
+
return React32.useContext(OlwibaUIContext);
|
|
27
31
|
}
|
|
28
32
|
function useUIMode() {
|
|
29
|
-
return
|
|
33
|
+
return React32.useContext(OlwibaUIContext).mode;
|
|
30
34
|
}
|
|
31
|
-
var Button =
|
|
35
|
+
var Button = React32.forwardRef(
|
|
32
36
|
({ mode, ...props }, ref) => {
|
|
33
37
|
const ctxMode = useUIMode();
|
|
34
38
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -36,7 +40,7 @@ var Button = React30.forwardRef(
|
|
|
36
40
|
}
|
|
37
41
|
);
|
|
38
42
|
Button.displayName = "Button";
|
|
39
|
-
var Card =
|
|
43
|
+
var Card = React32.forwardRef(
|
|
40
44
|
({ mode, ...props }, ref) => {
|
|
41
45
|
const ctxMode = useUIMode();
|
|
42
46
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -47,7 +51,7 @@ Card.displayName = "Card";
|
|
|
47
51
|
function Badge(props) {
|
|
48
52
|
return /* @__PURE__ */ jsx(Badge$1, { ...props });
|
|
49
53
|
}
|
|
50
|
-
var Input =
|
|
54
|
+
var Input = React32.forwardRef(
|
|
51
55
|
({ mode, ...props }, ref) => {
|
|
52
56
|
const ctxMode = useUIMode();
|
|
53
57
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -55,7 +59,7 @@ var Input = React30.forwardRef(
|
|
|
55
59
|
}
|
|
56
60
|
);
|
|
57
61
|
Input.displayName = "Input";
|
|
58
|
-
var Textarea =
|
|
62
|
+
var Textarea = React32.forwardRef(
|
|
59
63
|
({ mode, ...props }, ref) => {
|
|
60
64
|
const ctxMode = useUIMode();
|
|
61
65
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -63,13 +67,13 @@ var Textarea = React30.forwardRef(
|
|
|
63
67
|
}
|
|
64
68
|
);
|
|
65
69
|
Textarea.displayName = "Textarea";
|
|
66
|
-
var Checkbox =
|
|
70
|
+
var Checkbox = React32.forwardRef(({ mode, ...props }, ref) => {
|
|
67
71
|
const ctxMode = useUIMode();
|
|
68
72
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
69
73
|
return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
|
|
70
74
|
});
|
|
71
75
|
Checkbox.displayName = "Checkbox";
|
|
72
|
-
var Switch =
|
|
76
|
+
var Switch = React32.forwardRef(({ mode: _mode, ...props }, ref) => {
|
|
73
77
|
return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
|
|
74
78
|
});
|
|
75
79
|
Switch.displayName = "Switch";
|
|
@@ -513,7 +517,7 @@ function DefaultForm({
|
|
|
513
517
|
const isForgotPassword = mode === "forgot-password";
|
|
514
518
|
const isResetPassword = mode === "reset-password";
|
|
515
519
|
const isVerify = mode === "verify";
|
|
516
|
-
const [code, setCode] =
|
|
520
|
+
const [code, setCode] = React32.useState("");
|
|
517
521
|
const hasPrefill = !!(defaultEmail || defaultPassword);
|
|
518
522
|
const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
|
|
519
523
|
const copy = authCopy[mode];
|
|
@@ -586,11 +590,14 @@ function DefaultForm({
|
|
|
586
590
|
/* @__PURE__ */ jsx("input", { type: "hidden", name: "code", value: code }),
|
|
587
591
|
/* @__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
592
|
] }),
|
|
589
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error }),
|
|
590
|
-
success && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-primary", children: success }),
|
|
593
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error }),
|
|
594
|
+
success && /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm font-medium text-primary", children: success }),
|
|
591
595
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
592
|
-
/* @__PURE__ */
|
|
593
|
-
|
|
596
|
+
/* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: [
|
|
597
|
+
loading && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
|
|
598
|
+
loading ? "Please wait\u2026" : copy.submit
|
|
599
|
+
] }),
|
|
600
|
+
onSso && (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
|
|
594
601
|
] })
|
|
595
602
|
] }),
|
|
596
603
|
isVerify && onResend && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
|
|
@@ -670,10 +677,10 @@ function DataTable({
|
|
|
670
677
|
emptyMessage = "No results.",
|
|
671
678
|
className
|
|
672
679
|
}) {
|
|
673
|
-
const [sorting, setSorting] =
|
|
674
|
-
const [rowSelection, setRowSelection] =
|
|
675
|
-
const [globalFilter, setGlobalFilter] =
|
|
676
|
-
const allColumns =
|
|
680
|
+
const [sorting, setSorting] = React32.useState([]);
|
|
681
|
+
const [rowSelection, setRowSelection] = React32.useState({});
|
|
682
|
+
const [globalFilter, setGlobalFilter] = React32.useState("");
|
|
683
|
+
const allColumns = React32.useMemo(
|
|
677
684
|
() => selectable ? [selectColumn, ...columns] : columns,
|
|
678
685
|
[selectable, columns]
|
|
679
686
|
);
|
|
@@ -695,7 +702,7 @@ function DataTable({
|
|
|
695
702
|
initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
|
|
696
703
|
enableRowSelection: selectable
|
|
697
704
|
});
|
|
698
|
-
|
|
705
|
+
React32.useEffect(() => {
|
|
699
706
|
if (!onSelectionChange) return;
|
|
700
707
|
onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
|
|
701
708
|
}, [rowSelection]);
|
|
@@ -720,39 +727,48 @@ function DataTable({
|
|
|
720
727
|
/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
|
|
721
728
|
const canSort = header.column.getCanSort();
|
|
722
729
|
const sortDir = header.column.getIsSorted();
|
|
723
|
-
return /* @__PURE__ */ jsx(
|
|
724
|
-
|
|
730
|
+
return /* @__PURE__ */ jsx(
|
|
731
|
+
TableHead,
|
|
725
732
|
{
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
733
|
+
"aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : canSort ? "none" : void 0,
|
|
734
|
+
children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
|
|
735
|
+
"button",
|
|
736
|
+
{
|
|
737
|
+
type: "button",
|
|
738
|
+
className: "flex items-center gap-1.5 font-medium hover:text-foreground",
|
|
739
|
+
onClick: header.column.getToggleSortingHandler(),
|
|
740
|
+
children: [
|
|
741
|
+
flexRender(header.column.columnDef.header, header.getContext()),
|
|
742
|
+
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" })
|
|
743
|
+
]
|
|
744
|
+
}
|
|
745
|
+
) : flexRender(header.column.columnDef.header, header.getContext())
|
|
746
|
+
},
|
|
747
|
+
header.id
|
|
748
|
+
);
|
|
735
749
|
}) }, headerGroup.id)) }),
|
|
736
750
|
/* @__PURE__ */ jsx(TableBody, { children: rows.length ? rows.map((row) => /* @__PURE__ */ jsx(
|
|
737
751
|
TableRow,
|
|
738
752
|
{
|
|
739
753
|
"data-state": row.getIsSelected() ? "selected" : void 0,
|
|
740
754
|
onClick: () => onRowClick?.(row.original),
|
|
741
|
-
|
|
755
|
+
tabIndex: onRowClick ? 0 : void 0,
|
|
756
|
+
onKeyDown: onRowClick ? (e) => {
|
|
757
|
+
if (e.key === "Enter" && e.target === e.currentTarget) onRowClick(row.original);
|
|
758
|
+
} : void 0,
|
|
759
|
+
className: cn$1(onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
|
|
742
760
|
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
|
|
743
761
|
},
|
|
744
762
|
row.id
|
|
745
763
|
)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
|
|
746
764
|
] }) }),
|
|
747
|
-
pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
765
|
+
(selectable || pageSize > 0 && table.getPageCount() > 1) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
748
766
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
749
|
-
selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected
|
|
750
|
-
"
|
|
751
|
-
table.getState().pagination.pageIndex + 1
|
|
752
|
-
" of ",
|
|
753
|
-
table.getPageCount()
|
|
767
|
+
selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`,
|
|
768
|
+
selectable && pageSize > 0 && table.getPageCount() > 1 && " \xB7 ",
|
|
769
|
+
pageSize > 0 && table.getPageCount() > 1 && `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`
|
|
754
770
|
] }),
|
|
755
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
771
|
+
pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
756
772
|
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
|
|
757
773
|
/* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
|
|
758
774
|
" Previous"
|
|
@@ -765,32 +781,42 @@ function DataTable({
|
|
|
765
781
|
] })
|
|
766
782
|
] });
|
|
767
783
|
}
|
|
768
|
-
function InviteDialog({ roles, onInvite }) {
|
|
769
|
-
const [email, setEmail] =
|
|
770
|
-
const [role, setRole] =
|
|
771
|
-
return /* @__PURE__ */
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
/* @__PURE__ */
|
|
784
|
+
function InviteDialog({ roles, onInvite, onClose }) {
|
|
785
|
+
const [email, setEmail] = React32.useState("");
|
|
786
|
+
const [role, setRole] = React32.useState(roles[roles.length - 1] ?? roles[0]);
|
|
787
|
+
return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
|
|
788
|
+
"form",
|
|
789
|
+
{
|
|
790
|
+
onSubmit: (e) => {
|
|
791
|
+
e.preventDefault();
|
|
792
|
+
onInvite?.(email, role);
|
|
793
|
+
onClose();
|
|
794
|
+
},
|
|
795
|
+
children: [
|
|
796
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
797
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
|
|
798
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
|
|
799
|
+
] }),
|
|
800
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
|
|
801
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
802
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
|
|
803
|
+
/* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", required: true, placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
|
|
804
|
+
] }),
|
|
805
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
806
|
+
/* @__PURE__ */ jsx(Label, { children: "Role" }),
|
|
807
|
+
/* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
|
|
808
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
809
|
+
/* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
|
|
810
|
+
] })
|
|
811
|
+
] })
|
|
812
|
+
] }),
|
|
813
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
814
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", children: "Cancel" }) }),
|
|
815
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", children: "Send invite" })
|
|
786
816
|
] })
|
|
787
|
-
]
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Cancel" }) }),
|
|
791
|
-
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { disabled: !email, onClick: () => onInvite?.(email, role), children: "Send invite" }) })
|
|
792
|
-
] })
|
|
793
|
-
] });
|
|
817
|
+
]
|
|
818
|
+
}
|
|
819
|
+
) });
|
|
794
820
|
}
|
|
795
821
|
function TeamMembersPanel({
|
|
796
822
|
members,
|
|
@@ -801,7 +827,8 @@ function TeamMembersPanel({
|
|
|
801
827
|
title = "Team members",
|
|
802
828
|
description = "Manage who has access to this workspace."
|
|
803
829
|
}) {
|
|
804
|
-
const
|
|
830
|
+
const [inviteOpen, setInviteOpen] = React32.useState(false);
|
|
831
|
+
const columns = React32.useMemo(() => [
|
|
805
832
|
{
|
|
806
833
|
accessorKey: "name",
|
|
807
834
|
header: "Name",
|
|
@@ -824,7 +851,7 @@ function TeamMembersPanel({
|
|
|
824
851
|
/* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
|
|
825
852
|
] }) : /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: row.original.role })
|
|
826
853
|
},
|
|
827
|
-
{
|
|
854
|
+
...onRemove ? [{
|
|
828
855
|
id: "actions",
|
|
829
856
|
header: "",
|
|
830
857
|
cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
@@ -832,23 +859,23 @@ function TeamMembersPanel({
|
|
|
832
859
|
/* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }),
|
|
833
860
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Row actions" })
|
|
834
861
|
] }) }),
|
|
835
|
-
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove
|
|
862
|
+
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove(row.original.id), children: "Remove member" }) })
|
|
836
863
|
] }),
|
|
837
864
|
enableSorting: false
|
|
838
|
-
}
|
|
865
|
+
}] : []
|
|
839
866
|
], [roles, onRoleChange, onRemove]);
|
|
840
867
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
841
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
868
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
|
|
842
869
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
843
870
|
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
|
|
844
871
|
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
|
|
845
872
|
] }),
|
|
846
|
-
/* @__PURE__ */ jsxs(Dialog, { children: [
|
|
873
|
+
onInvite && /* @__PURE__ */ jsxs(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: [
|
|
847
874
|
/* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { children: [
|
|
848
875
|
/* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
|
|
849
876
|
" Invite member"
|
|
850
877
|
] }) }),
|
|
851
|
-
/* @__PURE__ */ jsx(InviteDialog, { roles, onInvite })
|
|
878
|
+
/* @__PURE__ */ jsx(InviteDialog, { roles, onInvite, onClose: () => setInviteOpen(false) }, String(inviteOpen))
|
|
852
879
|
] })
|
|
853
880
|
] }),
|
|
854
881
|
/* @__PURE__ */ jsx(DataTable, { columns, data: members, searchKey: "name", searchPlaceholder: "Search members\u2026", pageSize: 0 })
|
|
@@ -870,12 +897,13 @@ function BillingPanel({
|
|
|
870
897
|
onUpdatePaymentMethod,
|
|
871
898
|
className
|
|
872
899
|
}) {
|
|
873
|
-
const invoiceColumns =
|
|
874
|
-
{ accessorKey: "date", header: "Date" },
|
|
875
|
-
{ accessorKey: "amount", header: "Amount" },
|
|
900
|
+
const invoiceColumns = React32.useMemo(() => [
|
|
901
|
+
{ accessorKey: "date", header: "Date", enableSorting: false },
|
|
902
|
+
{ accessorKey: "amount", header: "Amount", enableSorting: false },
|
|
876
903
|
{
|
|
877
904
|
accessorKey: "status",
|
|
878
905
|
header: "Status",
|
|
906
|
+
enableSorting: false,
|
|
879
907
|
cell: ({ row }) => /* @__PURE__ */ jsx(Badge$1, { variant: invoiceStatusVariant[row.original.status], className: "capitalize", children: row.original.status })
|
|
880
908
|
},
|
|
881
909
|
{
|
|
@@ -903,19 +931,23 @@ function BillingPanel({
|
|
|
903
931
|
] }),
|
|
904
932
|
onManagePlan && /* @__PURE__ */ jsx(Button, { onClick: onManagePlan, children: "Manage plan" })
|
|
905
933
|
] }),
|
|
906
|
-
usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) =>
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
metric.
|
|
912
|
-
"
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
934
|
+
usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => {
|
|
935
|
+
const overLimit = metric.limit > 0 && metric.used > metric.limit;
|
|
936
|
+
const percent = metric.limit > 0 ? Math.min(100, metric.used / metric.limit * 100) : 0;
|
|
937
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
938
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
|
|
939
|
+
/* @__PURE__ */ jsx("span", { children: metric.label }),
|
|
940
|
+
/* @__PURE__ */ jsxs("span", { className: overLimit ? "font-medium text-destructive" : "text-muted-foreground", children: [
|
|
941
|
+
metric.used,
|
|
942
|
+
metric.unit,
|
|
943
|
+
" / ",
|
|
944
|
+
metric.limit,
|
|
945
|
+
metric.unit
|
|
946
|
+
] })
|
|
947
|
+
] }),
|
|
948
|
+
/* @__PURE__ */ jsx(Progress, { value: percent, className: overLimit ? "[&>div]:bg-destructive" : void 0 })
|
|
949
|
+
] }, metric.label);
|
|
950
|
+
}) })
|
|
919
951
|
] }),
|
|
920
952
|
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: [
|
|
921
953
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
@@ -944,9 +976,9 @@ function OnboardingWizard({
|
|
|
944
976
|
completeLabel = "Finish",
|
|
945
977
|
className
|
|
946
978
|
}) {
|
|
947
|
-
const [internalStep, setInternalStep] =
|
|
948
|
-
const [error, setError] =
|
|
949
|
-
const [pending, setPending] =
|
|
979
|
+
const [internalStep, setInternalStep] = React32.useState(0);
|
|
980
|
+
const [error, setError] = React32.useState(null);
|
|
981
|
+
const [pending, setPending] = React32.useState(false);
|
|
950
982
|
const index = stepProp ?? internalStep;
|
|
951
983
|
const current = steps[index];
|
|
952
984
|
const isLast = index === steps.length - 1;
|
|
@@ -959,8 +991,15 @@ function OnboardingWizard({
|
|
|
959
991
|
const handleNext = async () => {
|
|
960
992
|
if (current.onNext) {
|
|
961
993
|
setPending(true);
|
|
962
|
-
|
|
963
|
-
|
|
994
|
+
let result;
|
|
995
|
+
try {
|
|
996
|
+
result = await current.onNext();
|
|
997
|
+
} catch (err) {
|
|
998
|
+
setError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
|
|
999
|
+
return;
|
|
1000
|
+
} finally {
|
|
1001
|
+
setPending(false);
|
|
1002
|
+
}
|
|
964
1003
|
if (result === false) {
|
|
965
1004
|
setError("Please complete this step before continuing.");
|
|
966
1005
|
return;
|
|
@@ -980,30 +1019,47 @@ function OnboardingWizard({
|
|
|
980
1019
|
if (index > 0) goTo(index - 1);
|
|
981
1020
|
};
|
|
982
1021
|
return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full max-w-lg space-y-8", className), children: [
|
|
983
|
-
/* @__PURE__ */ jsx("ol", { className: "flex items-center", children: steps.map((step, i) => /* @__PURE__ */ jsxs(
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1022
|
+
/* @__PURE__ */ jsx("ol", { className: "flex items-center", "aria-label": "Progress", children: steps.map((step, i) => /* @__PURE__ */ jsxs(
|
|
1023
|
+
"li",
|
|
1024
|
+
{
|
|
1025
|
+
"aria-current": i === index ? "step" : void 0,
|
|
1026
|
+
className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"),
|
|
1027
|
+
children: [
|
|
1028
|
+
/* @__PURE__ */ jsxs(
|
|
1029
|
+
"div",
|
|
1030
|
+
{
|
|
1031
|
+
className: cn$1(
|
|
1032
|
+
"flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
|
|
1033
|
+
i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
|
|
1034
|
+
),
|
|
1035
|
+
children: [
|
|
1036
|
+
i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1,
|
|
1037
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
1038
|
+
step.title,
|
|
1039
|
+
i < index ? " (completed)" : ""
|
|
1040
|
+
] })
|
|
1041
|
+
]
|
|
1042
|
+
}
|
|
990
1043
|
),
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1044
|
+
i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
|
|
1045
|
+
]
|
|
1046
|
+
},
|
|
1047
|
+
step.id
|
|
1048
|
+
)) }),
|
|
996
1049
|
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
997
1050
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
998
1051
|
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: current.title }),
|
|
999
1052
|
current.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: current.description })
|
|
1000
1053
|
] }),
|
|
1001
1054
|
/* @__PURE__ */ jsx("div", { children: current.content }),
|
|
1002
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error })
|
|
1055
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error })
|
|
1003
1056
|
] }),
|
|
1004
1057
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1005
1058
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: handleBack, disabled: index === 0 || pending, children: "Back" }),
|
|
1006
|
-
/* @__PURE__ */
|
|
1059
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleNext, disabled: pending, children: [
|
|
1060
|
+
pending && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
|
|
1061
|
+
pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue"
|
|
1062
|
+
] })
|
|
1007
1063
|
] })
|
|
1008
1064
|
] });
|
|
1009
1065
|
}
|
|
@@ -1256,9 +1312,9 @@ function FadeIn({
|
|
|
1256
1312
|
style,
|
|
1257
1313
|
...props
|
|
1258
1314
|
}) {
|
|
1259
|
-
const ref =
|
|
1260
|
-
const [visible, setVisible] =
|
|
1261
|
-
|
|
1315
|
+
const ref = React32.useRef(null);
|
|
1316
|
+
const [visible, setVisible] = React32.useState(false);
|
|
1317
|
+
React32.useEffect(() => {
|
|
1262
1318
|
const el = ref.current;
|
|
1263
1319
|
if (!el) return;
|
|
1264
1320
|
const observer = new IntersectionObserver(
|
|
@@ -1365,27 +1421,28 @@ function HeroSection({
|
|
|
1365
1421
|
socialProofText,
|
|
1366
1422
|
renderLink = defaultRenderLink4
|
|
1367
1423
|
}) {
|
|
1424
|
+
const mode = useUIVariant();
|
|
1368
1425
|
return /* @__PURE__ */ jsx("section", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: marketingSectionSpacing.hero, children: [
|
|
1369
1426
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-2 lg:gap-16", children: [
|
|
1370
1427
|
/* @__PURE__ */ jsx(FadeIn, { direction: "left", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
1371
|
-
badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge
|
|
1428
|
+
badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
|
|
1372
1429
|
/* @__PURE__ */ jsx("h1", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl", children: heading }),
|
|
1373
1430
|
/* @__PURE__ */ jsx("p", { className: "max-w-lg text-pretty text-lg text-muted-foreground", children: description }),
|
|
1374
1431
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
1375
1432
|
renderLink({
|
|
1376
1433
|
href: primaryCta.href,
|
|
1377
|
-
children: /* @__PURE__ */ jsxs(Button
|
|
1434
|
+
children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
|
|
1378
1435
|
primaryCta.label,
|
|
1379
1436
|
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1380
1437
|
] })
|
|
1381
1438
|
}),
|
|
1382
1439
|
secondaryCta && renderLink({
|
|
1383
1440
|
href: secondaryCta.href,
|
|
1384
|
-
children: /* @__PURE__ */ jsx(Button
|
|
1441
|
+
children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
1385
1442
|
})
|
|
1386
1443
|
] })
|
|
1387
1444
|
] }) }),
|
|
1388
|
-
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 }) })
|
|
1445
|
+
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 }) })
|
|
1389
1446
|
] }),
|
|
1390
1447
|
(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: [
|
|
1391
1448
|
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: [
|
|
@@ -1444,6 +1501,88 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
|
|
|
1444
1501
|
}
|
|
1445
1502
|
return content;
|
|
1446
1503
|
}
|
|
1504
|
+
function Carousel({
|
|
1505
|
+
children,
|
|
1506
|
+
itemClassName = "w-[280px] sm:w-[320px]",
|
|
1507
|
+
controls = "top-right",
|
|
1508
|
+
ariaLabel,
|
|
1509
|
+
className
|
|
1510
|
+
}) {
|
|
1511
|
+
const mode = useUIVariant();
|
|
1512
|
+
const trackRef = React32.useRef(null);
|
|
1513
|
+
const [canPrev, setCanPrev] = React32.useState(false);
|
|
1514
|
+
const [canNext, setCanNext] = React32.useState(true);
|
|
1515
|
+
const updateScrollState = React32.useCallback(() => {
|
|
1516
|
+
const track = trackRef.current;
|
|
1517
|
+
if (!track) return;
|
|
1518
|
+
setCanPrev(track.scrollLeft > 8);
|
|
1519
|
+
setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
|
|
1520
|
+
}, []);
|
|
1521
|
+
React32.useEffect(() => {
|
|
1522
|
+
const track = trackRef.current;
|
|
1523
|
+
if (!track) return;
|
|
1524
|
+
updateScrollState();
|
|
1525
|
+
track.addEventListener("scroll", updateScrollState, { passive: true });
|
|
1526
|
+
const observer = new ResizeObserver(updateScrollState);
|
|
1527
|
+
observer.observe(track);
|
|
1528
|
+
return () => {
|
|
1529
|
+
track.removeEventListener("scroll", updateScrollState);
|
|
1530
|
+
observer.disconnect();
|
|
1531
|
+
};
|
|
1532
|
+
}, [updateScrollState]);
|
|
1533
|
+
const scrollByItem = (direction) => {
|
|
1534
|
+
const track = trackRef.current;
|
|
1535
|
+
if (!track) return;
|
|
1536
|
+
const item = track.querySelector("[data-carousel-item]");
|
|
1537
|
+
const gap = parseFloat(getComputedStyle(track).columnGap) || 24;
|
|
1538
|
+
const step = item ? item.offsetWidth + gap : track.clientWidth * 0.8;
|
|
1539
|
+
track.scrollBy({ left: direction * step, behavior: "smooth" });
|
|
1540
|
+
};
|
|
1541
|
+
const controlClasses = cn$1(
|
|
1542
|
+
"flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
|
|
1543
|
+
"hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
|
|
1544
|
+
mode === "smooth" ? "rounded-2xl" : "rounded-xl"
|
|
1545
|
+
);
|
|
1546
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1547
|
+
controls === "top-right" && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-end gap-2", children: [
|
|
1548
|
+
/* @__PURE__ */ jsx(
|
|
1549
|
+
"button",
|
|
1550
|
+
{
|
|
1551
|
+
type: "button",
|
|
1552
|
+
"aria-label": "Previous",
|
|
1553
|
+
className: controlClasses,
|
|
1554
|
+
onClick: () => scrollByItem(-1),
|
|
1555
|
+
disabled: !canPrev,
|
|
1556
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
1557
|
+
}
|
|
1558
|
+
),
|
|
1559
|
+
/* @__PURE__ */ jsx(
|
|
1560
|
+
"button",
|
|
1561
|
+
{
|
|
1562
|
+
type: "button",
|
|
1563
|
+
"aria-label": "Next",
|
|
1564
|
+
className: controlClasses,
|
|
1565
|
+
onClick: () => scrollByItem(1),
|
|
1566
|
+
disabled: !canNext,
|
|
1567
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
1568
|
+
}
|
|
1569
|
+
)
|
|
1570
|
+
] }),
|
|
1571
|
+
/* @__PURE__ */ jsx(
|
|
1572
|
+
"div",
|
|
1573
|
+
{
|
|
1574
|
+
ref: trackRef,
|
|
1575
|
+
role: "region",
|
|
1576
|
+
"aria-label": ariaLabel,
|
|
1577
|
+
className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1578
|
+
children: React32.Children.map(
|
|
1579
|
+
children,
|
|
1580
|
+
(child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
|
|
1581
|
+
)
|
|
1582
|
+
}
|
|
1583
|
+
)
|
|
1584
|
+
] });
|
|
1585
|
+
}
|
|
1447
1586
|
var translateMap2 = {
|
|
1448
1587
|
up: [0, 20],
|
|
1449
1588
|
down: [0, -20],
|
|
@@ -1461,9 +1600,9 @@ function StaggerChildren({
|
|
|
1461
1600
|
className,
|
|
1462
1601
|
...props
|
|
1463
1602
|
}) {
|
|
1464
|
-
const ref =
|
|
1465
|
-
const [visible, setVisible] =
|
|
1466
|
-
|
|
1603
|
+
const ref = React32.useRef(null);
|
|
1604
|
+
const [visible, setVisible] = React32.useState(false);
|
|
1605
|
+
React32.useEffect(() => {
|
|
1467
1606
|
const el = ref.current;
|
|
1468
1607
|
if (!el) return;
|
|
1469
1608
|
const observer = new IntersectionObserver(
|
|
@@ -1481,7 +1620,7 @@ function StaggerChildren({
|
|
|
1481
1620
|
return () => observer.disconnect();
|
|
1482
1621
|
}, [once]);
|
|
1483
1622
|
const [tx, ty] = translateMap2[direction];
|
|
1484
|
-
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children:
|
|
1623
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React32.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
|
|
1485
1624
|
"div",
|
|
1486
1625
|
{
|
|
1487
1626
|
className: "h-full",
|
|
@@ -1499,7 +1638,8 @@ function FeaturesSection({
|
|
|
1499
1638
|
title = "Everything you need to ship fast",
|
|
1500
1639
|
description = "A complete system of components, blocks, and hooks designed to work together - and get out of your way.",
|
|
1501
1640
|
badge = "Features",
|
|
1502
|
-
features
|
|
1641
|
+
features,
|
|
1642
|
+
layout = "grid"
|
|
1503
1643
|
}) {
|
|
1504
1644
|
const mode = useUIVariant();
|
|
1505
1645
|
const sectionClasses = cn$1(
|
|
@@ -1508,18 +1648,19 @@ function FeaturesSection({
|
|
|
1508
1648
|
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1509
1649
|
!mode && "rounded-2xl border"
|
|
1510
1650
|
);
|
|
1511
|
-
|
|
1651
|
+
const cards = features.map((feature) => /* @__PURE__ */ jsx(
|
|
1652
|
+
FeatureCard,
|
|
1653
|
+
{
|
|
1654
|
+
icon: feature.icon,
|
|
1655
|
+
title: feature.title,
|
|
1656
|
+
description: feature.description,
|
|
1657
|
+
href: feature.href
|
|
1658
|
+
},
|
|
1659
|
+
feature.title
|
|
1660
|
+
));
|
|
1661
|
+
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: [
|
|
1512
1662
|
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1513
|
-
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children:
|
|
1514
|
-
FeatureCard,
|
|
1515
|
-
{
|
|
1516
|
-
icon: feature.icon,
|
|
1517
|
-
title: feature.title,
|
|
1518
|
-
description: feature.description,
|
|
1519
|
-
href: feature.href
|
|
1520
|
-
},
|
|
1521
|
-
feature.title
|
|
1522
|
-
)) })
|
|
1663
|
+
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 })
|
|
1523
1664
|
] }) }) });
|
|
1524
1665
|
}
|
|
1525
1666
|
function GroupedFeaturesSection({
|
|
@@ -1649,106 +1790,6 @@ function TechStackSection({
|
|
|
1649
1790
|
}) })
|
|
1650
1791
|
] }) }) });
|
|
1651
1792
|
}
|
|
1652
|
-
function CarouselSection({
|
|
1653
|
-
title = "Explore the platform",
|
|
1654
|
-
description,
|
|
1655
|
-
badge = "Features",
|
|
1656
|
-
features
|
|
1657
|
-
}) {
|
|
1658
|
-
const mode = useUIVariant();
|
|
1659
|
-
const trackRef = React30.useRef(null);
|
|
1660
|
-
const [canPrev, setCanPrev] = React30.useState(false);
|
|
1661
|
-
const [canNext, setCanNext] = React30.useState(true);
|
|
1662
|
-
const updateScrollState = React30.useCallback(() => {
|
|
1663
|
-
const track = trackRef.current;
|
|
1664
|
-
if (!track) return;
|
|
1665
|
-
setCanPrev(track.scrollLeft > 8);
|
|
1666
|
-
setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
|
|
1667
|
-
}, []);
|
|
1668
|
-
React30.useEffect(() => {
|
|
1669
|
-
const track = trackRef.current;
|
|
1670
|
-
if (!track) return;
|
|
1671
|
-
updateScrollState();
|
|
1672
|
-
track.addEventListener("scroll", updateScrollState, { passive: true });
|
|
1673
|
-
const observer = new ResizeObserver(updateScrollState);
|
|
1674
|
-
observer.observe(track);
|
|
1675
|
-
return () => {
|
|
1676
|
-
track.removeEventListener("scroll", updateScrollState);
|
|
1677
|
-
observer.disconnect();
|
|
1678
|
-
};
|
|
1679
|
-
}, [updateScrollState]);
|
|
1680
|
-
const scrollByCard = (direction) => {
|
|
1681
|
-
const track = trackRef.current;
|
|
1682
|
-
if (!track) return;
|
|
1683
|
-
const card = track.querySelector("[data-carousel-item]");
|
|
1684
|
-
const step = card ? card.offsetWidth + 24 : track.clientWidth * 0.8;
|
|
1685
|
-
track.scrollBy({ left: direction * step, behavior: "smooth" });
|
|
1686
|
-
};
|
|
1687
|
-
const sectionClasses = cn$1(
|
|
1688
|
-
"overflow-hidden bg-card",
|
|
1689
|
-
mode === "smooth" && "rounded-3xl border",
|
|
1690
|
-
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1691
|
-
!mode && "rounded-2xl border"
|
|
1692
|
-
);
|
|
1693
|
-
const controlClasses = cn$1(
|
|
1694
|
-
"flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
|
|
1695
|
-
"hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
|
|
1696
|
-
mode === "smooth" ? "rounded-2xl" : "rounded-xl"
|
|
1697
|
-
);
|
|
1698
|
-
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: [
|
|
1699
|
-
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1700
|
-
/* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
|
|
1701
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-end gap-2", children: [
|
|
1702
|
-
/* @__PURE__ */ jsx(
|
|
1703
|
-
"button",
|
|
1704
|
-
{
|
|
1705
|
-
type: "button",
|
|
1706
|
-
"aria-label": "Previous",
|
|
1707
|
-
className: controlClasses,
|
|
1708
|
-
onClick: () => scrollByCard(-1),
|
|
1709
|
-
disabled: !canPrev,
|
|
1710
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
1711
|
-
}
|
|
1712
|
-
),
|
|
1713
|
-
/* @__PURE__ */ jsx(
|
|
1714
|
-
"button",
|
|
1715
|
-
{
|
|
1716
|
-
type: "button",
|
|
1717
|
-
"aria-label": "Next",
|
|
1718
|
-
className: controlClasses,
|
|
1719
|
-
onClick: () => scrollByCard(1),
|
|
1720
|
-
disabled: !canNext,
|
|
1721
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
1722
|
-
}
|
|
1723
|
-
)
|
|
1724
|
-
] }),
|
|
1725
|
-
/* @__PURE__ */ jsx(
|
|
1726
|
-
"div",
|
|
1727
|
-
{
|
|
1728
|
-
ref: trackRef,
|
|
1729
|
-
className: "mt-4 flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1730
|
-
children: features.map((feature) => /* @__PURE__ */ jsx(
|
|
1731
|
-
"div",
|
|
1732
|
-
{
|
|
1733
|
-
"data-carousel-item": true,
|
|
1734
|
-
className: "w-[280px] shrink-0 snap-start sm:w-[320px]",
|
|
1735
|
-
children: /* @__PURE__ */ jsx(
|
|
1736
|
-
FeatureCard,
|
|
1737
|
-
{
|
|
1738
|
-
icon: feature.icon,
|
|
1739
|
-
title: feature.title,
|
|
1740
|
-
description: feature.description,
|
|
1741
|
-
href: feature.href
|
|
1742
|
-
}
|
|
1743
|
-
)
|
|
1744
|
-
},
|
|
1745
|
-
feature.title
|
|
1746
|
-
))
|
|
1747
|
-
}
|
|
1748
|
-
)
|
|
1749
|
-
] })
|
|
1750
|
-
] }) }) });
|
|
1751
|
-
}
|
|
1752
1793
|
var speedDuration = {
|
|
1753
1794
|
slow: "280s",
|
|
1754
1795
|
normal: "180s",
|
|
@@ -1829,79 +1870,42 @@ function FeatureMarqueeSection({
|
|
|
1829
1870
|
` })
|
|
1830
1871
|
] });
|
|
1831
1872
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
})
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1846
|
-
!mode && "rounded-2xl border"
|
|
1847
|
-
);
|
|
1848
|
-
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
|
|
1849
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
|
|
1850
|
-
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
|
|
1851
|
-
/* @__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" }) }),
|
|
1852
|
-
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
|
|
1853
|
-
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1854
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
|
|
1855
|
-
renderLink({
|
|
1856
|
-
href: primaryCta.href,
|
|
1857
|
-
children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
|
|
1858
|
-
primaryCta.label,
|
|
1859
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1860
|
-
] })
|
|
1861
|
-
}),
|
|
1862
|
-
secondaryCta && renderLink({
|
|
1863
|
-
href: secondaryCta.href,
|
|
1864
|
-
children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
1865
|
-
})
|
|
1866
|
-
] }),
|
|
1867
|
-
footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
|
|
1868
|
-
] }) })
|
|
1869
|
-
] }) });
|
|
1873
|
+
function useIntersectionObserver(options) {
|
|
1874
|
+
const ref = React32.useRef(null);
|
|
1875
|
+
const [isIntersecting, setIsIntersecting] = React32.useState(false);
|
|
1876
|
+
React32.useEffect(() => {
|
|
1877
|
+
const el = ref.current;
|
|
1878
|
+
if (!el) return;
|
|
1879
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
1880
|
+
setIsIntersecting(entry.isIntersecting);
|
|
1881
|
+
}, options);
|
|
1882
|
+
observer.observe(el);
|
|
1883
|
+
return () => observer.disconnect();
|
|
1884
|
+
}, []);
|
|
1885
|
+
return [ref, isIntersecting];
|
|
1870
1886
|
}
|
|
1871
|
-
var
|
|
1872
|
-
function
|
|
1887
|
+
var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1888
|
+
function ShowcaseCta({
|
|
1873
1889
|
heading,
|
|
1874
1890
|
description,
|
|
1875
1891
|
primaryCta,
|
|
1876
1892
|
footnote,
|
|
1877
1893
|
icon,
|
|
1878
|
-
renderLink =
|
|
1894
|
+
renderLink = defaultRenderLink5,
|
|
1895
|
+
sectionClasses
|
|
1879
1896
|
}) {
|
|
1880
|
-
const ref =
|
|
1881
|
-
const [visible, setVisible] =
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
const observer = new IntersectionObserver(
|
|
1886
|
-
([entry]) => {
|
|
1887
|
-
if (entry.isIntersecting) {
|
|
1888
|
-
setVisible(true);
|
|
1889
|
-
observer.disconnect();
|
|
1890
|
-
}
|
|
1891
|
-
},
|
|
1892
|
-
{ threshold: 0.15 }
|
|
1893
|
-
);
|
|
1894
|
-
observer.observe(el);
|
|
1895
|
-
return () => observer.disconnect();
|
|
1896
|
-
}, []);
|
|
1897
|
+
const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
|
|
1898
|
+
const [visible, setVisible] = React32.useState(false);
|
|
1899
|
+
React32.useEffect(() => {
|
|
1900
|
+
if (intersecting) setVisible(true);
|
|
1901
|
+
}, [intersecting]);
|
|
1897
1902
|
return /* @__PURE__ */ jsxs(
|
|
1898
1903
|
"section",
|
|
1899
1904
|
{
|
|
1900
1905
|
ref,
|
|
1901
1906
|
className: cn$1(
|
|
1902
|
-
|
|
1903
|
-
"
|
|
1904
|
-
"transition-[opacity,transform] duration-700 ease-out",
|
|
1907
|
+
sectionClasses,
|
|
1908
|
+
"relative transition-[opacity,transform] duration-700 ease-out",
|
|
1905
1909
|
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6"
|
|
1906
1910
|
),
|
|
1907
1911
|
children: [
|
|
@@ -1910,11 +1914,11 @@ function CtaCardSection({
|
|
|
1910
1914
|
{
|
|
1911
1915
|
className: "pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
1912
1916
|
style: {
|
|
1913
|
-
opacity: visible ? 0.
|
|
1917
|
+
opacity: visible ? 0.1 : 0,
|
|
1914
1918
|
transform: visible ? "scale(1) rotate(0deg)" : "scale(0.9) rotate(-5deg)",
|
|
1915
1919
|
transition: "opacity 700ms ease 200ms, transform 700ms ease 200ms"
|
|
1916
1920
|
},
|
|
1917
|
-
children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground
|
|
1921
|
+
children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground", strokeWidth: 0.75 })
|
|
1918
1922
|
}
|
|
1919
1923
|
),
|
|
1920
1924
|
/* @__PURE__ */ jsxs("div", { className: "relative z-10 px-6 py-20 text-center sm:px-10 sm:py-28", children: [
|
|
@@ -1922,9 +1926,9 @@ function CtaCardSection({
|
|
|
1922
1926
|
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1923
1927
|
/* @__PURE__ */ jsx("div", { className: "mt-8", children: renderLink({
|
|
1924
1928
|
href: primaryCta.href,
|
|
1925
|
-
children: /* @__PURE__ */ jsxs(
|
|
1929
|
+
children: /* @__PURE__ */ jsxs(Button, { size: "lg", className: "rounded-full px-6", children: [
|
|
1926
1930
|
primaryCta.label,
|
|
1927
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
|
|
1931
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1928
1932
|
] })
|
|
1929
1933
|
}) }),
|
|
1930
1934
|
footnote && /* @__PURE__ */ jsx("p", { className: "mt-5 text-sm text-muted-foreground", children: footnote })
|
|
@@ -1933,6 +1937,49 @@ function CtaCardSection({
|
|
|
1933
1937
|
}
|
|
1934
1938
|
);
|
|
1935
1939
|
}
|
|
1940
|
+
function CtaSection(props) {
|
|
1941
|
+
const {
|
|
1942
|
+
heading,
|
|
1943
|
+
description,
|
|
1944
|
+
primaryCta,
|
|
1945
|
+
secondaryCta,
|
|
1946
|
+
footnote,
|
|
1947
|
+
variant = "default",
|
|
1948
|
+
renderLink = defaultRenderLink5
|
|
1949
|
+
} = props;
|
|
1950
|
+
const mode = useUIVariant();
|
|
1951
|
+
const sectionClasses = cn$1(
|
|
1952
|
+
"overflow-hidden bg-card",
|
|
1953
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1954
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1955
|
+
!mode && "rounded-2xl border"
|
|
1956
|
+
);
|
|
1957
|
+
if (variant === "showcase") {
|
|
1958
|
+
return /* @__PURE__ */ jsx(ShowcaseCta, { ...props, sectionClasses });
|
|
1959
|
+
}
|
|
1960
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
|
|
1961
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
|
|
1962
|
+
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
|
|
1963
|
+
/* @__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" }) }),
|
|
1964
|
+
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
|
|
1965
|
+
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1966
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
|
|
1967
|
+
renderLink({
|
|
1968
|
+
href: primaryCta.href,
|
|
1969
|
+
children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
|
|
1970
|
+
primaryCta.label,
|
|
1971
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
1972
|
+
] })
|
|
1973
|
+
}),
|
|
1974
|
+
secondaryCta && renderLink({
|
|
1975
|
+
href: secondaryCta.href,
|
|
1976
|
+
children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
|
|
1977
|
+
})
|
|
1978
|
+
] }),
|
|
1979
|
+
footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
|
|
1980
|
+
] }) })
|
|
1981
|
+
] }) });
|
|
1982
|
+
}
|
|
1936
1983
|
function PricingCard({
|
|
1937
1984
|
name,
|
|
1938
1985
|
price,
|
|
@@ -2026,8 +2073,8 @@ function CountdownTimer({
|
|
|
2026
2073
|
className,
|
|
2027
2074
|
compact = false
|
|
2028
2075
|
}) {
|
|
2029
|
-
const [timeLeft, setTimeLeft] =
|
|
2030
|
-
|
|
2076
|
+
const [timeLeft, setTimeLeft] = React32.useState(() => getTimeLeft(deadline));
|
|
2077
|
+
React32.useEffect(() => {
|
|
2031
2078
|
setTimeLeft(getTimeLeft(deadline));
|
|
2032
2079
|
const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
|
|
2033
2080
|
return () => clearInterval(id);
|
|
@@ -2053,7 +2100,7 @@ function CountdownTimer({
|
|
|
2053
2100
|
];
|
|
2054
2101
|
return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
|
|
2055
2102
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
2056
|
-
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(
|
|
2103
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React32.Fragment, { children: [
|
|
2057
2104
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
2058
2105
|
String(value).padStart(2, "0"),
|
|
2059
2106
|
/* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
|
|
@@ -2062,7 +2109,7 @@ function CountdownTimer({
|
|
|
2062
2109
|
] }, unit)) })
|
|
2063
2110
|
] });
|
|
2064
2111
|
}
|
|
2065
|
-
var
|
|
2112
|
+
var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
2066
2113
|
function PricingSection({
|
|
2067
2114
|
title = "Simple, transparent pricing",
|
|
2068
2115
|
description = "Start for free. Scale as you grow. No hidden fees.",
|
|
@@ -2070,13 +2117,13 @@ function PricingSection({
|
|
|
2070
2117
|
plans,
|
|
2071
2118
|
saveBadge = "Save 34%",
|
|
2072
2119
|
isAuthenticated,
|
|
2073
|
-
renderLink =
|
|
2120
|
+
renderLink = defaultRenderLink6,
|
|
2074
2121
|
footnote,
|
|
2075
2122
|
mode = "subscription",
|
|
2076
2123
|
foundingDeadline,
|
|
2077
2124
|
renderPlanFooter
|
|
2078
2125
|
}) {
|
|
2079
|
-
const [annual, setAnnual] =
|
|
2126
|
+
const [annual, setAnnual] = React32.useState(false);
|
|
2080
2127
|
const isOneTime = mode === "one-time";
|
|
2081
2128
|
const uiMode = useUIVariant();
|
|
2082
2129
|
const sectionClasses = cn$1(
|
|
@@ -2280,9 +2327,16 @@ function TeamSection({
|
|
|
2280
2327
|
title = "The people behind Olwiba",
|
|
2281
2328
|
description = "A small team with deep experience in design systems, open source, and developer tooling."
|
|
2282
2329
|
}) {
|
|
2283
|
-
|
|
2330
|
+
const mode = useUIVariant();
|
|
2331
|
+
const sectionClasses = cn$1(
|
|
2332
|
+
"overflow-hidden bg-card",
|
|
2333
|
+
mode === "smooth" && "rounded-3xl border",
|
|
2334
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
2335
|
+
!mode && "rounded-2xl border"
|
|
2336
|
+
);
|
|
2337
|
+
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: [
|
|
2284
2338
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2285
|
-
/* @__PURE__ */ jsx(Badge
|
|
2339
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "mb-4", children: badge }),
|
|
2286
2340
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
2287
2341
|
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description })
|
|
2288
2342
|
] }),
|
|
@@ -2338,10 +2392,10 @@ function CountUp({
|
|
|
2338
2392
|
className,
|
|
2339
2393
|
...props
|
|
2340
2394
|
}) {
|
|
2341
|
-
const ref =
|
|
2342
|
-
const [value, setValue] =
|
|
2343
|
-
const hasStarted =
|
|
2344
|
-
|
|
2395
|
+
const ref = React32.useRef(null);
|
|
2396
|
+
const [value, setValue] = React32.useState(from);
|
|
2397
|
+
const hasStarted = React32.useRef(false);
|
|
2398
|
+
React32.useEffect(() => {
|
|
2345
2399
|
const el = ref.current;
|
|
2346
2400
|
if (!el) return;
|
|
2347
2401
|
const observer = new IntersectionObserver(
|
|
@@ -2434,20 +2488,27 @@ function NewsletterSection({
|
|
|
2434
2488
|
successDescription = defaults.successDescription,
|
|
2435
2489
|
onSubscribe
|
|
2436
2490
|
} = {}) {
|
|
2437
|
-
const [email, setEmail] =
|
|
2438
|
-
const [submitted, setSubmitted] =
|
|
2491
|
+
const [email, setEmail] = React32.useState("");
|
|
2492
|
+
const [submitted, setSubmitted] = React32.useState(false);
|
|
2493
|
+
const mode = useUIVariant();
|
|
2494
|
+
const sectionClasses = cn$1(
|
|
2495
|
+
"overflow-hidden bg-card",
|
|
2496
|
+
mode === "smooth" && "rounded-3xl border",
|
|
2497
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
2498
|
+
!mode && "rounded-2xl border"
|
|
2499
|
+
);
|
|
2439
2500
|
async function handleSubmit(e) {
|
|
2440
2501
|
e.preventDefault();
|
|
2441
2502
|
if (!email) return;
|
|
2442
2503
|
await onSubscribe?.(email);
|
|
2443
2504
|
setSubmitted(true);
|
|
2444
2505
|
}
|
|
2445
|
-
return /* @__PURE__ */ jsx("section", { className:
|
|
2506
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
2446
2507
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.1),transparent_60%)]" }),
|
|
2447
2508
|
/* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-xl text-center", children: [
|
|
2448
2509
|
/* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-center gap-2", children: [
|
|
2449
2510
|
/* @__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" }) }),
|
|
2450
|
-
/* @__PURE__ */ jsx(Badge
|
|
2511
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge })
|
|
2451
2512
|
] }),
|
|
2452
2513
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
2453
2514
|
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-sm text-pretty text-muted-foreground", children: description }),
|
|
@@ -2468,7 +2529,7 @@ function NewsletterSection({
|
|
|
2468
2529
|
}
|
|
2469
2530
|
),
|
|
2470
2531
|
/* @__PURE__ */ jsx(
|
|
2471
|
-
Input
|
|
2532
|
+
Input,
|
|
2472
2533
|
{
|
|
2473
2534
|
type: "email",
|
|
2474
2535
|
placeholder,
|
|
@@ -2478,7 +2539,7 @@ function NewsletterSection({
|
|
|
2478
2539
|
className: "flex-1"
|
|
2479
2540
|
}
|
|
2480
2541
|
),
|
|
2481
|
-
/* @__PURE__ */ jsxs(Button
|
|
2542
|
+
/* @__PURE__ */ jsxs(Button, { type: "submit", children: [
|
|
2482
2543
|
submitLabel,
|
|
2483
2544
|
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
|
|
2484
2545
|
] })
|
|
@@ -2519,15 +2580,22 @@ function ContactSection({
|
|
|
2519
2580
|
sendAnotherLabel = defaults2.sendAnotherLabel,
|
|
2520
2581
|
onSubmit
|
|
2521
2582
|
} = {}) {
|
|
2522
|
-
const [submitted, setSubmitted] =
|
|
2583
|
+
const [submitted, setSubmitted] = React32.useState(false);
|
|
2584
|
+
const mode = useUIVariant();
|
|
2585
|
+
const sectionClasses = cn$1(
|
|
2586
|
+
"overflow-hidden bg-card",
|
|
2587
|
+
mode === "smooth" && "rounded-3xl border",
|
|
2588
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
2589
|
+
!mode && "rounded-2xl border"
|
|
2590
|
+
);
|
|
2523
2591
|
async function handleSubmit(e) {
|
|
2524
2592
|
e.preventDefault();
|
|
2525
2593
|
await onSubmit?.(e);
|
|
2526
2594
|
setSubmitted(true);
|
|
2527
2595
|
}
|
|
2528
|
-
return /* @__PURE__ */ jsx("section", { className:
|
|
2596
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
2529
2597
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-2xl text-center", children: [
|
|
2530
|
-
badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge
|
|
2598
|
+
badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
|
|
2531
2599
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl", children: title }),
|
|
2532
2600
|
/* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: description }),
|
|
2533
2601
|
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: [
|
|
@@ -2539,7 +2607,7 @@ function ContactSection({
|
|
|
2539
2607
|
/* @__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" }) }),
|
|
2540
2608
|
/* @__PURE__ */ jsx("h3", { className: "mt-5 text-lg font-semibold", children: successTitle }),
|
|
2541
2609
|
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: successDescription }),
|
|
2542
|
-
/* @__PURE__ */ jsx(Button
|
|
2610
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
|
|
2543
2611
|
] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "mx-auto mt-16 max-w-xl sm:mt-20", children: [
|
|
2544
2612
|
/* @__PURE__ */ jsx(
|
|
2545
2613
|
"input",
|
|
@@ -2556,7 +2624,7 @@ function ContactSection({
|
|
|
2556
2624
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2557
2625
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-first", className: "block text-sm/6 font-semibold", children: "First name" }),
|
|
2558
2626
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2559
|
-
Input
|
|
2627
|
+
Input,
|
|
2560
2628
|
{
|
|
2561
2629
|
id: "contact-first",
|
|
2562
2630
|
name: "firstName",
|
|
@@ -2569,7 +2637,7 @@ function ContactSection({
|
|
|
2569
2637
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2570
2638
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-last", className: "block text-sm/6 font-semibold", children: "Last name" }),
|
|
2571
2639
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2572
|
-
Input
|
|
2640
|
+
Input,
|
|
2573
2641
|
{
|
|
2574
2642
|
id: "contact-last",
|
|
2575
2643
|
name: "lastName",
|
|
@@ -2586,7 +2654,7 @@ function ContactSection({
|
|
|
2586
2654
|
/* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
|
|
2587
2655
|
] }),
|
|
2588
2656
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2589
|
-
Input
|
|
2657
|
+
Input,
|
|
2590
2658
|
{
|
|
2591
2659
|
id: "contact-company",
|
|
2592
2660
|
name: "company",
|
|
@@ -2598,7 +2666,7 @@ function ContactSection({
|
|
|
2598
2666
|
/* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
|
|
2599
2667
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-email", className: "block text-sm/6 font-semibold", children: "Email" }),
|
|
2600
2668
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2601
|
-
Input
|
|
2669
|
+
Input,
|
|
2602
2670
|
{
|
|
2603
2671
|
id: "contact-email",
|
|
2604
2672
|
name: "email",
|
|
@@ -2644,7 +2712,7 @@ function ContactSection({
|
|
|
2644
2712
|
/* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
|
|
2645
2713
|
/* @__PURE__ */ jsx(Label, { htmlFor: "contact-message", className: "block text-sm/6 font-semibold", children: "Message" }),
|
|
2646
2714
|
/* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
|
|
2647
|
-
Textarea
|
|
2715
|
+
Textarea,
|
|
2648
2716
|
{
|
|
2649
2717
|
id: "contact-message",
|
|
2650
2718
|
name: "message",
|
|
@@ -2692,7 +2760,7 @@ function ContactSection({
|
|
|
2692
2760
|
)
|
|
2693
2761
|
] })
|
|
2694
2762
|
] }),
|
|
2695
|
-
/* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button
|
|
2763
|
+
/* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", children: [
|
|
2696
2764
|
"Let's talk",
|
|
2697
2765
|
/* @__PURE__ */ jsx(Send, { className: "ml-2 size-4" })
|
|
2698
2766
|
] }) })
|
|
@@ -2802,15 +2870,15 @@ function QualificationSection({
|
|
|
2802
2870
|
}) })
|
|
2803
2871
|
] }) }) });
|
|
2804
2872
|
}
|
|
2805
|
-
var
|
|
2873
|
+
var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
2806
2874
|
function Navbar({
|
|
2807
2875
|
brand,
|
|
2808
2876
|
navLinks,
|
|
2809
2877
|
cta,
|
|
2810
2878
|
controls,
|
|
2811
|
-
renderLink =
|
|
2879
|
+
renderLink = defaultRenderLink7
|
|
2812
2880
|
}) {
|
|
2813
|
-
const [open, setOpen] =
|
|
2881
|
+
const [open, setOpen] = React32.useState(false);
|
|
2814
2882
|
const brandHref = brand.href ?? "/";
|
|
2815
2883
|
const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
|
|
2816
2884
|
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: [
|
|
@@ -2827,7 +2895,7 @@ function Navbar({
|
|
|
2827
2895
|
children: link.label
|
|
2828
2896
|
}) }, link.label)) }),
|
|
2829
2897
|
hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
|
|
2830
|
-
controls?.map((control, i) => /* @__PURE__ */ jsx(
|
|
2898
|
+
controls?.map((control, i) => /* @__PURE__ */ jsx(React32.Fragment, { children: control }, i)),
|
|
2831
2899
|
(cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
|
|
2832
2900
|
cta?.secondary && renderLink({
|
|
2833
2901
|
href: cta.secondary.href,
|
|
@@ -2851,7 +2919,7 @@ function Navbar({
|
|
|
2851
2919
|
/* @__PURE__ */ jsx("span", { children: brand.name })
|
|
2852
2920
|
] })
|
|
2853
2921
|
}) }) }),
|
|
2854
|
-
controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(
|
|
2922
|
+
controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React32.Fragment, { children: control }, i)) }) : null,
|
|
2855
2923
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
2856
2924
|
/* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
|
|
2857
2925
|
href: link.href,
|
|
@@ -2872,14 +2940,14 @@ function Navbar({
|
|
|
2872
2940
|
] })
|
|
2873
2941
|
] }) });
|
|
2874
2942
|
}
|
|
2875
|
-
var
|
|
2943
|
+
var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
2876
2944
|
function Footer({
|
|
2877
2945
|
brand,
|
|
2878
2946
|
navLinks,
|
|
2879
2947
|
socialLinks,
|
|
2880
2948
|
legal,
|
|
2881
2949
|
status,
|
|
2882
|
-
renderLink =
|
|
2950
|
+
renderLink = defaultRenderLink8
|
|
2883
2951
|
}) {
|
|
2884
2952
|
const brandHref = brand.href ?? "/";
|
|
2885
2953
|
const copyrightText = legal ?? `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${brand.name}. All rights reserved.`;
|
|
@@ -3000,7 +3068,7 @@ function LogoStrip({
|
|
|
3000
3068
|
] });
|
|
3001
3069
|
}
|
|
3002
3070
|
function GridUnderlay({ variant, size = 24, className, ...props }) {
|
|
3003
|
-
const id =
|
|
3071
|
+
const id = React32.useId();
|
|
3004
3072
|
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: [
|
|
3005
3073
|
/* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
|
|
3006
3074
|
/* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
|
|
@@ -3028,9 +3096,9 @@ function GlowUnderlay({
|
|
|
3028
3096
|
style,
|
|
3029
3097
|
...props
|
|
3030
3098
|
}) {
|
|
3031
|
-
const [pos, setPos] =
|
|
3032
|
-
const containerRef =
|
|
3033
|
-
|
|
3099
|
+
const [pos, setPos] = React32.useState({ x: "50%", y: "50%" });
|
|
3100
|
+
const containerRef = React32.useRef(null);
|
|
3101
|
+
React32.useEffect(() => {
|
|
3034
3102
|
if (!followCursor) return;
|
|
3035
3103
|
const handleMouseMove = (e) => {
|
|
3036
3104
|
const el = containerRef.current;
|
|
@@ -3169,8 +3237,8 @@ function PageTransition({
|
|
|
3169
3237
|
style,
|
|
3170
3238
|
...props
|
|
3171
3239
|
}) {
|
|
3172
|
-
const [mounted, setMounted] =
|
|
3173
|
-
|
|
3240
|
+
const [mounted, setMounted] = React32.useState(false);
|
|
3241
|
+
React32.useEffect(() => {
|
|
3174
3242
|
const id = requestAnimationFrame(() => setMounted(true));
|
|
3175
3243
|
return () => cancelAnimationFrame(id);
|
|
3176
3244
|
}, []);
|
|
@@ -3190,6 +3258,66 @@ function PageTransition({
|
|
|
3190
3258
|
}
|
|
3191
3259
|
);
|
|
3192
3260
|
}
|
|
3261
|
+
var strategies = {
|
|
3262
|
+
vertical: verticalListSortingStrategy,
|
|
3263
|
+
horizontal: horizontalListSortingStrategy,
|
|
3264
|
+
grid: rectSortingStrategy
|
|
3265
|
+
};
|
|
3266
|
+
var containerClasses = {
|
|
3267
|
+
vertical: "flex flex-col gap-2",
|
|
3268
|
+
horizontal: "flex gap-2",
|
|
3269
|
+
grid: "grid gap-2"
|
|
3270
|
+
};
|
|
3271
|
+
function SortableItem({
|
|
3272
|
+
id,
|
|
3273
|
+
disabled,
|
|
3274
|
+
className,
|
|
3275
|
+
children
|
|
3276
|
+
}) {
|
|
3277
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id, disabled });
|
|
3278
|
+
return /* @__PURE__ */ jsx(
|
|
3279
|
+
"div",
|
|
3280
|
+
{
|
|
3281
|
+
ref: setNodeRef,
|
|
3282
|
+
style: { transform: CSS.Transform.toString(transform), transition },
|
|
3283
|
+
className: cn$1(
|
|
3284
|
+
"touch-none",
|
|
3285
|
+
!disabled && "cursor-grab active:cursor-grabbing",
|
|
3286
|
+
isDragging && "relative z-10 opacity-80",
|
|
3287
|
+
className
|
|
3288
|
+
),
|
|
3289
|
+
...attributes,
|
|
3290
|
+
...listeners,
|
|
3291
|
+
children
|
|
3292
|
+
}
|
|
3293
|
+
);
|
|
3294
|
+
}
|
|
3295
|
+
function Sortable({
|
|
3296
|
+
items,
|
|
3297
|
+
onReorder,
|
|
3298
|
+
children,
|
|
3299
|
+
direction = "vertical",
|
|
3300
|
+
disabled,
|
|
3301
|
+
className,
|
|
3302
|
+
itemClassName
|
|
3303
|
+
}) {
|
|
3304
|
+
const sensors = useSensors(
|
|
3305
|
+
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
3306
|
+
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
3307
|
+
);
|
|
3308
|
+
const handleDragEnd = (event) => {
|
|
3309
|
+
const { active, over } = event;
|
|
3310
|
+
if (!over || active.id === over.id) return;
|
|
3311
|
+
const oldIndex = items.indexOf(String(active.id));
|
|
3312
|
+
const newIndex = items.indexOf(String(over.id));
|
|
3313
|
+
if (oldIndex === -1 || newIndex === -1) return;
|
|
3314
|
+
onReorder(arrayMove(items, oldIndex, newIndex));
|
|
3315
|
+
};
|
|
3316
|
+
const childArray = React32.Children.toArray(children);
|
|
3317
|
+
return /* @__PURE__ */ jsx(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(SortableContext, { items, strategy: strategies[direction], children: /* @__PURE__ */ jsx("div", { className: cn$1(containerClasses[direction], className), children: childArray.map(
|
|
3318
|
+
(child, i) => items[i] === void 0 ? null : /* @__PURE__ */ jsx(SortableItem, { id: items[i], disabled, className: itemClassName, children: child }, items[i])
|
|
3319
|
+
) }) }) });
|
|
3320
|
+
}
|
|
3193
3321
|
function Spotlight({
|
|
3194
3322
|
groups,
|
|
3195
3323
|
placeholder = "Search anything...",
|
|
@@ -3197,8 +3325,8 @@ function Spotlight({
|
|
|
3197
3325
|
triggerClassName,
|
|
3198
3326
|
shortcut = "\u2318K"
|
|
3199
3327
|
}) {
|
|
3200
|
-
const [open, setOpen] =
|
|
3201
|
-
|
|
3328
|
+
const [open, setOpen] = React32.useState(false);
|
|
3329
|
+
React32.useEffect(() => {
|
|
3202
3330
|
const down = (e) => {
|
|
3203
3331
|
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
|
3204
3332
|
e.preventDefault();
|
|
@@ -3228,7 +3356,7 @@ function Spotlight({
|
|
|
3228
3356
|
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
3229
3357
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
3230
3358
|
/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
|
|
3231
|
-
groups.map((group, i) => /* @__PURE__ */ jsxs(
|
|
3359
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React32.Fragment, { children: [
|
|
3232
3360
|
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
3233
3361
|
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
3234
3362
|
CommandItem,
|
|
@@ -3253,8 +3381,8 @@ function Spotlight({
|
|
|
3253
3381
|
var BASE = 40;
|
|
3254
3382
|
var MAGNIFY_RADIUS = 80;
|
|
3255
3383
|
function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
|
|
3256
|
-
const [mouseX, setMouseX] =
|
|
3257
|
-
const containerRef =
|
|
3384
|
+
const [mouseX, setMouseX] = React32.useState(null);
|
|
3385
|
+
const containerRef = React32.useRef(null);
|
|
3258
3386
|
function getScale(index) {
|
|
3259
3387
|
if (mouseX === null || !containerRef.current) return 1;
|
|
3260
3388
|
const container = containerRef.current;
|
|
@@ -3353,7 +3481,7 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
|
|
|
3353
3481
|
] }) });
|
|
3354
3482
|
}
|
|
3355
3483
|
function RegisterHotkeys({ hotkeys }) {
|
|
3356
|
-
|
|
3484
|
+
React32.useEffect(() => {
|
|
3357
3485
|
const handleKeyDown = (event) => {
|
|
3358
3486
|
for (const hotkey of hotkeys) {
|
|
3359
3487
|
const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
|
|
@@ -3370,10 +3498,10 @@ function RegisterHotkeys({ hotkeys }) {
|
|
|
3370
3498
|
return null;
|
|
3371
3499
|
}
|
|
3372
3500
|
function useControlledOpen(defaultOpen = false) {
|
|
3373
|
-
const [isOpen, setIsOpen] =
|
|
3374
|
-
const open =
|
|
3375
|
-
const close =
|
|
3376
|
-
const toggle =
|
|
3501
|
+
const [isOpen, setIsOpen] = React32.useState(defaultOpen);
|
|
3502
|
+
const open = React32.useCallback(() => setIsOpen(true), []);
|
|
3503
|
+
const close = React32.useCallback(() => setIsOpen(false), []);
|
|
3504
|
+
const toggle = React32.useCallback(() => setIsOpen((prev) => !prev), []);
|
|
3377
3505
|
return { isOpen, open, close, toggle, setIsOpen };
|
|
3378
3506
|
}
|
|
3379
3507
|
function CommandMenu({
|
|
@@ -3386,7 +3514,13 @@ function CommandMenu({
|
|
|
3386
3514
|
}) {
|
|
3387
3515
|
const internal = useControlledOpen(false);
|
|
3388
3516
|
const isOpen = openProp ?? internal.isOpen;
|
|
3389
|
-
const setOpen =
|
|
3517
|
+
const setOpen = React32.useCallback(
|
|
3518
|
+
(next) => {
|
|
3519
|
+
if (openProp === void 0) internal.setIsOpen(next);
|
|
3520
|
+
onOpenChange?.(next);
|
|
3521
|
+
},
|
|
3522
|
+
[openProp, onOpenChange, internal.setIsOpen]
|
|
3523
|
+
);
|
|
3390
3524
|
const runItem = (item) => {
|
|
3391
3525
|
setOpen(false);
|
|
3392
3526
|
item.onSelect();
|
|
@@ -3405,7 +3539,7 @@ function CommandMenu({
|
|
|
3405
3539
|
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
3406
3540
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
3407
3541
|
/* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
|
|
3408
|
-
groups.map((group, i) => /* @__PURE__ */ jsxs(
|
|
3542
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React32.Fragment, { children: [
|
|
3409
3543
|
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
3410
3544
|
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
3411
3545
|
CommandItem,
|
|
@@ -3425,6 +3559,157 @@ function CommandMenu({
|
|
|
3425
3559
|
] })
|
|
3426
3560
|
] });
|
|
3427
3561
|
}
|
|
3562
|
+
var TOKEN_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
|
|
3563
|
+
var seriesColor = (s, i) => s.color ?? TOKEN_COLORS[i % TOKEN_COLORS.length];
|
|
3564
|
+
function ChartTooltip({
|
|
3565
|
+
active,
|
|
3566
|
+
payload,
|
|
3567
|
+
label,
|
|
3568
|
+
valueFormatter
|
|
3569
|
+
}) {
|
|
3570
|
+
if (!active || !payload?.length) return null;
|
|
3571
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-popover px-3 py-2 text-xs text-popover-foreground shadow-md", children: [
|
|
3572
|
+
label !== void 0 && /* @__PURE__ */ jsx("p", { className: "mb-1 font-medium", children: label }),
|
|
3573
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: payload.map((entry, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3574
|
+
/* @__PURE__ */ jsx(
|
|
3575
|
+
"span",
|
|
3576
|
+
{
|
|
3577
|
+
"aria-hidden": true,
|
|
3578
|
+
className: "size-2 shrink-0 rounded-full",
|
|
3579
|
+
style: { background: entry.color ?? entry.payload?.fill }
|
|
3580
|
+
}
|
|
3581
|
+
),
|
|
3582
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: entry.name }),
|
|
3583
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto pl-3 font-medium tabular-nums", children: typeof entry.value === "number" ? valueFormatter(entry.value) : entry.value })
|
|
3584
|
+
] }, i)) })
|
|
3585
|
+
] });
|
|
3586
|
+
}
|
|
3587
|
+
function ChartLegend({ entries }) {
|
|
3588
|
+
return /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap items-center justify-center gap-x-4 gap-y-1", children: entries.map((entry) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
3589
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-2 rounded-full", style: { background: entry.color } }),
|
|
3590
|
+
entry.label
|
|
3591
|
+
] }, entry.label)) });
|
|
3592
|
+
}
|
|
3593
|
+
var axisProps = {
|
|
3594
|
+
tickLine: false,
|
|
3595
|
+
axisLine: false,
|
|
3596
|
+
tick: { fill: "var(--muted-foreground)", fontSize: 12 },
|
|
3597
|
+
tickMargin: 8
|
|
3598
|
+
};
|
|
3599
|
+
function Chart({
|
|
3600
|
+
type = "line",
|
|
3601
|
+
data,
|
|
3602
|
+
xKey,
|
|
3603
|
+
series,
|
|
3604
|
+
height = 300,
|
|
3605
|
+
grid = true,
|
|
3606
|
+
legend,
|
|
3607
|
+
valueFormatter = (v) => String(v),
|
|
3608
|
+
className
|
|
3609
|
+
}) {
|
|
3610
|
+
const showLegend = legend ?? (type === "donut" || series.length > 1);
|
|
3611
|
+
const tooltip = /* @__PURE__ */ jsx(
|
|
3612
|
+
Tooltip$1,
|
|
3613
|
+
{
|
|
3614
|
+
cursor: type === "bar" ? { fill: "var(--muted)", opacity: 0.4 } : { stroke: "var(--border)" },
|
|
3615
|
+
content: /* @__PURE__ */ jsx(ChartTooltip, { valueFormatter })
|
|
3616
|
+
}
|
|
3617
|
+
);
|
|
3618
|
+
const gridEl = grid ? /* @__PURE__ */ jsx(CartesianGrid, { vertical: false, stroke: "var(--border)", strokeOpacity: 0.6 }) : null;
|
|
3619
|
+
let chart;
|
|
3620
|
+
let legendEntries;
|
|
3621
|
+
if (type === "donut") {
|
|
3622
|
+
const valueKey = series[0]?.key;
|
|
3623
|
+
legendEntries = data.map((row, i) => ({
|
|
3624
|
+
label: String(row[xKey]),
|
|
3625
|
+
color: TOKEN_COLORS[i % TOKEN_COLORS.length]
|
|
3626
|
+
}));
|
|
3627
|
+
chart = /* @__PURE__ */ jsxs(PieChart, { children: [
|
|
3628
|
+
/* @__PURE__ */ jsx(
|
|
3629
|
+
Pie,
|
|
3630
|
+
{
|
|
3631
|
+
data,
|
|
3632
|
+
dataKey: valueKey,
|
|
3633
|
+
nameKey: xKey,
|
|
3634
|
+
innerRadius: "60%",
|
|
3635
|
+
outerRadius: "85%",
|
|
3636
|
+
paddingAngle: 2,
|
|
3637
|
+
stroke: "var(--card)",
|
|
3638
|
+
strokeWidth: 2,
|
|
3639
|
+
children: data.map((row, i) => /* @__PURE__ */ jsx(Cell, { fill: TOKEN_COLORS[i % TOKEN_COLORS.length] }, String(row[xKey])))
|
|
3640
|
+
}
|
|
3641
|
+
),
|
|
3642
|
+
tooltip
|
|
3643
|
+
] });
|
|
3644
|
+
} else {
|
|
3645
|
+
legendEntries = series.map((s, i) => ({ label: s.label ?? s.key, color: seriesColor(s, i) }));
|
|
3646
|
+
if (type === "bar") {
|
|
3647
|
+
chart = /* @__PURE__ */ jsxs(BarChart, { data, barCategoryGap: "25%", children: [
|
|
3648
|
+
gridEl,
|
|
3649
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
|
|
3650
|
+
/* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
|
|
3651
|
+
tooltip,
|
|
3652
|
+
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
3653
|
+
Bar,
|
|
3654
|
+
{
|
|
3655
|
+
dataKey: s.key,
|
|
3656
|
+
name: s.label ?? s.key,
|
|
3657
|
+
fill: seriesColor(s, i),
|
|
3658
|
+
radius: [4, 4, 0, 0],
|
|
3659
|
+
maxBarSize: 40
|
|
3660
|
+
},
|
|
3661
|
+
s.key
|
|
3662
|
+
))
|
|
3663
|
+
] });
|
|
3664
|
+
} else if (type === "area") {
|
|
3665
|
+
chart = /* @__PURE__ */ jsxs(AreaChart, { data, children: [
|
|
3666
|
+
gridEl,
|
|
3667
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
|
|
3668
|
+
/* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
|
|
3669
|
+
tooltip,
|
|
3670
|
+
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
3671
|
+
Area,
|
|
3672
|
+
{
|
|
3673
|
+
type: "monotone",
|
|
3674
|
+
dataKey: s.key,
|
|
3675
|
+
name: s.label ?? s.key,
|
|
3676
|
+
stroke: seriesColor(s, i),
|
|
3677
|
+
strokeWidth: 2,
|
|
3678
|
+
fill: seriesColor(s, i),
|
|
3679
|
+
fillOpacity: 0.12,
|
|
3680
|
+
dot: false,
|
|
3681
|
+
activeDot: { r: 4 }
|
|
3682
|
+
},
|
|
3683
|
+
s.key
|
|
3684
|
+
))
|
|
3685
|
+
] });
|
|
3686
|
+
} else {
|
|
3687
|
+
chart = /* @__PURE__ */ jsxs(LineChart, { data, children: [
|
|
3688
|
+
gridEl,
|
|
3689
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: xKey, ...axisProps }),
|
|
3690
|
+
/* @__PURE__ */ jsx(YAxis, { ...axisProps, width: 48, tickFormatter: valueFormatter }),
|
|
3691
|
+
tooltip,
|
|
3692
|
+
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
3693
|
+
Line,
|
|
3694
|
+
{
|
|
3695
|
+
type: "monotone",
|
|
3696
|
+
dataKey: s.key,
|
|
3697
|
+
name: s.label ?? s.key,
|
|
3698
|
+
stroke: seriesColor(s, i),
|
|
3699
|
+
strokeWidth: 2,
|
|
3700
|
+
dot: false,
|
|
3701
|
+
activeDot: { r: 4 }
|
|
3702
|
+
},
|
|
3703
|
+
s.key
|
|
3704
|
+
))
|
|
3705
|
+
] });
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full", className), children: [
|
|
3709
|
+
/* @__PURE__ */ jsx("div", { style: { height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: chart }) }),
|
|
3710
|
+
showLegend && /* @__PURE__ */ jsx(ChartLegend, { entries: legendEntries })
|
|
3711
|
+
] });
|
|
3712
|
+
}
|
|
3428
3713
|
function formatBytes(bytes) {
|
|
3429
3714
|
if (bytes < 1024) return `${bytes} B`;
|
|
3430
3715
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
@@ -3446,19 +3731,20 @@ function FileUpload({
|
|
|
3446
3731
|
hint,
|
|
3447
3732
|
className
|
|
3448
3733
|
}) {
|
|
3449
|
-
const [internalFiles, setInternalFiles] =
|
|
3450
|
-
const [isDragging, setIsDragging] =
|
|
3451
|
-
const [validationError, setValidationError] =
|
|
3452
|
-
const inputRef =
|
|
3734
|
+
const [internalFiles, setInternalFiles] = React32.useState(defaultFiles ?? []);
|
|
3735
|
+
const [isDragging, setIsDragging] = React32.useState(false);
|
|
3736
|
+
const [validationError, setValidationError] = React32.useState(null);
|
|
3737
|
+
const inputRef = React32.useRef(null);
|
|
3738
|
+
const dragDepth = React32.useRef(0);
|
|
3453
3739
|
const files = filesProp ?? internalFiles;
|
|
3454
|
-
const setFiles =
|
|
3740
|
+
const setFiles = React32.useCallback(
|
|
3455
3741
|
(next) => {
|
|
3456
3742
|
if (!filesProp) setInternalFiles(next);
|
|
3457
3743
|
onFilesChange?.(next);
|
|
3458
3744
|
},
|
|
3459
3745
|
[filesProp, onFilesChange]
|
|
3460
3746
|
);
|
|
3461
|
-
const acceptList =
|
|
3747
|
+
const acceptList = React32.useMemo(
|
|
3462
3748
|
() => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
|
|
3463
3749
|
[accept]
|
|
3464
3750
|
);
|
|
@@ -3472,7 +3758,7 @@ function FileUpload({
|
|
|
3472
3758
|
const handleFiles = (fileList) => {
|
|
3473
3759
|
if (!fileList || disabled) return;
|
|
3474
3760
|
const incoming = Array.from(fileList);
|
|
3475
|
-
const room = maxFiles ? Math.max(0, maxFiles - files.length) : Infinity;
|
|
3761
|
+
const room = maxFiles && multiple ? Math.max(0, maxFiles - files.length) : maxFiles || Infinity;
|
|
3476
3762
|
if (maxFiles && room <= 0) {
|
|
3477
3763
|
setValidationError(`You can only add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`);
|
|
3478
3764
|
return;
|
|
@@ -3507,15 +3793,25 @@ function FileUpload({
|
|
|
3507
3793
|
tabIndex: disabled ? -1 : 0,
|
|
3508
3794
|
onClick: () => !disabled && inputRef.current?.click(),
|
|
3509
3795
|
onKeyDown: (e) => {
|
|
3510
|
-
if (
|
|
3796
|
+
if (disabled) return;
|
|
3797
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3798
|
+
e.preventDefault();
|
|
3799
|
+
inputRef.current?.click();
|
|
3800
|
+
}
|
|
3511
3801
|
},
|
|
3512
|
-
onDragOver: (e) =>
|
|
3802
|
+
onDragOver: (e) => e.preventDefault(),
|
|
3803
|
+
onDragEnter: (e) => {
|
|
3513
3804
|
e.preventDefault();
|
|
3805
|
+
dragDepth.current += 1;
|
|
3514
3806
|
if (!disabled) setIsDragging(true);
|
|
3515
3807
|
},
|
|
3516
|
-
onDragLeave: () =>
|
|
3808
|
+
onDragLeave: () => {
|
|
3809
|
+
dragDepth.current = Math.max(0, dragDepth.current - 1);
|
|
3810
|
+
if (dragDepth.current === 0) setIsDragging(false);
|
|
3811
|
+
},
|
|
3517
3812
|
onDrop: (e) => {
|
|
3518
3813
|
e.preventDefault();
|
|
3814
|
+
dragDepth.current = 0;
|
|
3519
3815
|
setIsDragging(false);
|
|
3520
3816
|
handleFiles(e.dataTransfer.files);
|
|
3521
3817
|
},
|
|
@@ -3549,7 +3845,7 @@ function FileUpload({
|
|
|
3549
3845
|
]
|
|
3550
3846
|
}
|
|
3551
3847
|
),
|
|
3552
|
-
validationError && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: validationError }),
|
|
3848
|
+
validationError && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: validationError }),
|
|
3553
3849
|
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: [
|
|
3554
3850
|
/* @__PURE__ */ jsx(File, { className: "size-5 shrink-0 text-muted-foreground" }),
|
|
3555
3851
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -3586,6 +3882,8 @@ function FileUpload({
|
|
|
3586
3882
|
var variantIcon = {
|
|
3587
3883
|
success: /* @__PURE__ */ jsx(CheckCircle2, { className: "size-5 text-primary" }),
|
|
3588
3884
|
info: /* @__PURE__ */ jsx(Info, { className: "size-5 text-muted-foreground" }),
|
|
3885
|
+
warning: /* @__PURE__ */ jsx(AlertTriangle, { className: "size-5 text-amber-500 dark:text-amber-400" }),
|
|
3886
|
+
error: /* @__PURE__ */ jsx(AlertCircle, { className: "size-5 text-destructive" }),
|
|
3589
3887
|
message: /* @__PURE__ */ jsx(Inbox, { className: "size-5 text-muted-foreground" })
|
|
3590
3888
|
};
|
|
3591
3889
|
function NotificationToast({
|
|
@@ -3620,6 +3918,104 @@ function notify(options) {
|
|
|
3620
3918
|
const { duration, ...toastProps } = options;
|
|
3621
3919
|
return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
|
|
3622
3920
|
}
|
|
3921
|
+
function NotificationsPopover({
|
|
3922
|
+
notifications,
|
|
3923
|
+
onNotificationClick,
|
|
3924
|
+
onMarkAllRead,
|
|
3925
|
+
title = "Notifications",
|
|
3926
|
+
emptyMessage = "Nothing new \u2014 you\u2019re all caught up.",
|
|
3927
|
+
align = "end",
|
|
3928
|
+
open,
|
|
3929
|
+
onOpenChange,
|
|
3930
|
+
className
|
|
3931
|
+
}) {
|
|
3932
|
+
const unreadCount = notifications.filter((n) => !n.read).length;
|
|
3933
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
|
|
3934
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: cn$1("relative size-8", className), children: [
|
|
3935
|
+
/* @__PURE__ */ jsx(Bell, { className: "size-4" }),
|
|
3936
|
+
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 }),
|
|
3937
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
3938
|
+
title,
|
|
3939
|
+
unreadCount > 0 ? ` (${unreadCount} unread)` : ""
|
|
3940
|
+
] })
|
|
3941
|
+
] }) }),
|
|
3942
|
+
/* @__PURE__ */ jsxs(PopoverContent, { align, className: "w-80 p-0", children: [
|
|
3943
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
|
|
3944
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: title }),
|
|
3945
|
+
onMarkAllRead && unreadCount > 0 && /* @__PURE__ */ jsx(
|
|
3946
|
+
"button",
|
|
3947
|
+
{
|
|
3948
|
+
type: "button",
|
|
3949
|
+
onClick: onMarkAllRead,
|
|
3950
|
+
className: "text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
3951
|
+
children: "Mark all read"
|
|
3952
|
+
}
|
|
3953
|
+
)
|
|
3954
|
+
] }),
|
|
3955
|
+
notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 px-4 py-10 text-center", children: [
|
|
3956
|
+
/* @__PURE__ */ jsx(Inbox, { className: "size-6 text-muted-foreground" }),
|
|
3957
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
|
|
3958
|
+
] }) : /* @__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(
|
|
3959
|
+
"button",
|
|
3960
|
+
{
|
|
3961
|
+
type: "button",
|
|
3962
|
+
onClick: () => onNotificationClick?.(notification),
|
|
3963
|
+
className: cn$1(
|
|
3964
|
+
"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/60",
|
|
3965
|
+
!notification.read && "bg-muted/40"
|
|
3966
|
+
),
|
|
3967
|
+
children: [
|
|
3968
|
+
/* @__PURE__ */ jsx(
|
|
3969
|
+
"span",
|
|
3970
|
+
{
|
|
3971
|
+
"aria-hidden": true,
|
|
3972
|
+
className: cn$1(
|
|
3973
|
+
"mt-1.5 size-2 shrink-0 rounded-full",
|
|
3974
|
+
notification.read ? "bg-transparent" : "bg-primary"
|
|
3975
|
+
)
|
|
3976
|
+
}
|
|
3977
|
+
),
|
|
3978
|
+
notification.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8 shrink-0", children: [
|
|
3979
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: notification.avatar, alt: "" }),
|
|
3980
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: notification.title.slice(0, 2).toUpperCase() })
|
|
3981
|
+
] }) : notification.icon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: notification.icon }) : null,
|
|
3982
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
3983
|
+
/* @__PURE__ */ jsx("span", { className: cn$1("block truncate text-sm", !notification.read && "font-medium"), children: notification.title }),
|
|
3984
|
+
notification.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 line-clamp-2 block text-xs text-muted-foreground", children: notification.description }),
|
|
3985
|
+
notification.timestamp && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground/70", children: notification.timestamp })
|
|
3986
|
+
] })
|
|
3987
|
+
]
|
|
3988
|
+
}
|
|
3989
|
+
) }, notification.id)) })
|
|
3990
|
+
] })
|
|
3991
|
+
] });
|
|
3992
|
+
}
|
|
3993
|
+
function ActivityFeed({
|
|
3994
|
+
items,
|
|
3995
|
+
emptyMessage = "No activity yet.",
|
|
3996
|
+
className
|
|
3997
|
+
}) {
|
|
3998
|
+
if (items.length === 0) {
|
|
3999
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("flex flex-col items-center gap-2 py-10 text-center", className), children: [
|
|
4000
|
+
/* @__PURE__ */ jsx(Activity, { className: "size-6 text-muted-foreground" }),
|
|
4001
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
|
|
4002
|
+
] });
|
|
4003
|
+
}
|
|
4004
|
+
return /* @__PURE__ */ jsx("ol", { className, children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "relative flex gap-3 pb-6 last:pb-0", children: [
|
|
4005
|
+
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" }),
|
|
4006
|
+
/* @__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: [
|
|
4007
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: item.avatar, alt: "" }),
|
|
4008
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: item.initials ?? "?" })
|
|
4009
|
+
] }) : /* @__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, {}) }) }),
|
|
4010
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-1", children: [
|
|
4011
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
4012
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: item.title }),
|
|
4013
|
+
item.timestamp && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.timestamp })
|
|
4014
|
+
] }),
|
|
4015
|
+
item.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: item.description })
|
|
4016
|
+
] })
|
|
4017
|
+
] }, item.id)) });
|
|
4018
|
+
}
|
|
3623
4019
|
function AppScreenRoot({ children, className }) {
|
|
3624
4020
|
return /* @__PURE__ */ jsx("div", { className: cn("flex h-full flex-col", className), children });
|
|
3625
4021
|
}
|
|
@@ -4002,20 +4398,20 @@ function FullPageSpinner({ message }) {
|
|
|
4002
4398
|
message && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
|
|
4003
4399
|
] });
|
|
4004
4400
|
}
|
|
4005
|
-
var
|
|
4401
|
+
var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
4006
4402
|
function PageHeader({
|
|
4007
4403
|
title,
|
|
4008
4404
|
description,
|
|
4009
4405
|
breadcrumbs,
|
|
4010
4406
|
backButton,
|
|
4011
|
-
renderLink =
|
|
4407
|
+
renderLink = defaultRenderLink9,
|
|
4012
4408
|
className,
|
|
4013
4409
|
children
|
|
4014
4410
|
}) {
|
|
4015
4411
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
|
|
4016
4412
|
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) => {
|
|
4017
4413
|
const isLast = i === breadcrumbs.length - 1;
|
|
4018
|
-
return /* @__PURE__ */ jsxs(
|
|
4414
|
+
return /* @__PURE__ */ jsxs(React32.Fragment, { children: [
|
|
4019
4415
|
i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
|
|
4020
4416
|
isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
|
|
4021
4417
|
href: crumb.href,
|
|
@@ -4051,7 +4447,7 @@ function PageHeader({
|
|
|
4051
4447
|
] });
|
|
4052
4448
|
}
|
|
4053
4449
|
function Suspensed({ children, fallback }) {
|
|
4054
|
-
return /* @__PURE__ */ jsx(
|
|
4450
|
+
return /* @__PURE__ */ jsx(React32.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
|
|
4055
4451
|
}
|
|
4056
4452
|
function getInitialTheme() {
|
|
4057
4453
|
if (typeof document === "undefined") return "dark";
|
|
@@ -4059,7 +4455,7 @@ function getInitialTheme() {
|
|
|
4059
4455
|
return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
|
|
4060
4456
|
}
|
|
4061
4457
|
function ThemeSwitchMinimal() {
|
|
4062
|
-
const [theme, setThemeState] =
|
|
4458
|
+
const [theme, setThemeState] = React32.useState(getInitialTheme);
|
|
4063
4459
|
const toggle = () => {
|
|
4064
4460
|
const next = theme === "dark" ? "light" : "dark";
|
|
4065
4461
|
setThemeState(next);
|
|
@@ -4185,9 +4581,9 @@ function getInitialColorName() {
|
|
|
4185
4581
|
return localStorage.getItem(STORAGE_KEY) ?? "emerald";
|
|
4186
4582
|
}
|
|
4187
4583
|
function BrandColorSwitchMinimal() {
|
|
4188
|
-
const [active, setActive] =
|
|
4189
|
-
const [open, setOpen] =
|
|
4190
|
-
|
|
4584
|
+
const [active, setActive] = React32.useState(getInitialColorName);
|
|
4585
|
+
const [open, setOpen] = React32.useState(false);
|
|
4586
|
+
React32.useEffect(() => {
|
|
4191
4587
|
const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
|
|
4192
4588
|
const color = BRAND_COLORS.find((c) => c.name === saved);
|
|
4193
4589
|
if (color) applyColor(color);
|
|
@@ -4248,7 +4644,7 @@ function BrandColorSwitchMinimal() {
|
|
|
4248
4644
|
] });
|
|
4249
4645
|
}
|
|
4250
4646
|
function ThemeColorUpdater({ colorTheme }) {
|
|
4251
|
-
|
|
4647
|
+
React32.useEffect(() => {
|
|
4252
4648
|
if (colorTheme) {
|
|
4253
4649
|
document.documentElement.setAttribute("data-theme", colorTheme);
|
|
4254
4650
|
} else {
|
|
@@ -4258,7 +4654,7 @@ function ThemeColorUpdater({ colorTheme }) {
|
|
|
4258
4654
|
return null;
|
|
4259
4655
|
}
|
|
4260
4656
|
function VersionBanner({ version, message, className, onDismiss }) {
|
|
4261
|
-
const [dismissed, setDismissed] =
|
|
4657
|
+
const [dismissed, setDismissed] = React32.useState(false);
|
|
4262
4658
|
if (dismissed) return null;
|
|
4263
4659
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
|
|
4264
4660
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -4515,29 +4911,29 @@ function useMounted() {
|
|
|
4515
4911
|
return mounted;
|
|
4516
4912
|
}
|
|
4517
4913
|
function useConfirm() {
|
|
4518
|
-
const [isOpen, setIsOpen] =
|
|
4519
|
-
const [options, setOptions] =
|
|
4520
|
-
const resolveRef =
|
|
4521
|
-
const confirm =
|
|
4914
|
+
const [isOpen, setIsOpen] = React32.useState(false);
|
|
4915
|
+
const [options, setOptions] = React32.useState({});
|
|
4916
|
+
const resolveRef = React32.useRef(null);
|
|
4917
|
+
const confirm = React32.useCallback((opts = {}) => {
|
|
4522
4918
|
setOptions(opts);
|
|
4523
4919
|
setIsOpen(true);
|
|
4524
4920
|
return new Promise((resolve) => {
|
|
4525
4921
|
resolveRef.current = resolve;
|
|
4526
4922
|
});
|
|
4527
4923
|
}, []);
|
|
4528
|
-
const handleConfirm =
|
|
4924
|
+
const handleConfirm = React32.useCallback(() => {
|
|
4529
4925
|
resolveRef.current?.(true);
|
|
4530
4926
|
setIsOpen(false);
|
|
4531
4927
|
}, []);
|
|
4532
|
-
const handleCancel =
|
|
4928
|
+
const handleCancel = React32.useCallback(() => {
|
|
4533
4929
|
resolveRef.current?.(false);
|
|
4534
4930
|
setIsOpen(false);
|
|
4535
4931
|
}, []);
|
|
4536
4932
|
return { confirm, isOpen, options, handleConfirm, handleCancel };
|
|
4537
4933
|
}
|
|
4538
4934
|
function useScrolledPast(threshold) {
|
|
4539
|
-
const [scrolledPast, setScrolledPast] =
|
|
4540
|
-
|
|
4935
|
+
const [scrolledPast, setScrolledPast] = React32.useState(false);
|
|
4936
|
+
React32.useEffect(() => {
|
|
4541
4937
|
const handleScroll = () => {
|
|
4542
4938
|
setScrolledPast(window.scrollY > threshold);
|
|
4543
4939
|
};
|
|
@@ -4548,42 +4944,28 @@ function useScrolledPast(threshold) {
|
|
|
4548
4944
|
return scrolledPast;
|
|
4549
4945
|
}
|
|
4550
4946
|
function useCopyToClipboard() {
|
|
4551
|
-
const [copied, setCopied] =
|
|
4552
|
-
const timeoutRef =
|
|
4553
|
-
const copy =
|
|
4947
|
+
const [copied, setCopied] = React32.useState(false);
|
|
4948
|
+
const timeoutRef = React32.useRef(void 0);
|
|
4949
|
+
const copy = React32.useCallback((text) => {
|
|
4554
4950
|
navigator.clipboard.writeText(text).then(() => {
|
|
4555
4951
|
setCopied(true);
|
|
4556
4952
|
clearTimeout(timeoutRef.current);
|
|
4557
4953
|
timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
4558
4954
|
});
|
|
4559
4955
|
}, []);
|
|
4560
|
-
|
|
4956
|
+
React32.useEffect(() => () => clearTimeout(timeoutRef.current), []);
|
|
4561
4957
|
return [copied, copy];
|
|
4562
4958
|
}
|
|
4563
4959
|
function useDebounce(value, delay) {
|
|
4564
|
-
const [debounced, setDebounced] =
|
|
4565
|
-
|
|
4960
|
+
const [debounced, setDebounced] = React32.useState(value);
|
|
4961
|
+
React32.useEffect(() => {
|
|
4566
4962
|
const timer = setTimeout(() => setDebounced(value), delay);
|
|
4567
4963
|
return () => clearTimeout(timer);
|
|
4568
4964
|
}, [value, delay]);
|
|
4569
4965
|
return debounced;
|
|
4570
4966
|
}
|
|
4571
|
-
function useIntersectionObserver(options) {
|
|
4572
|
-
const ref = React30.useRef(null);
|
|
4573
|
-
const [isIntersecting, setIsIntersecting] = React30.useState(false);
|
|
4574
|
-
React30.useEffect(() => {
|
|
4575
|
-
const el = ref.current;
|
|
4576
|
-
if (!el) return;
|
|
4577
|
-
const observer = new IntersectionObserver(([entry]) => {
|
|
4578
|
-
setIsIntersecting(entry.isIntersecting);
|
|
4579
|
-
}, options);
|
|
4580
|
-
observer.observe(el);
|
|
4581
|
-
return () => observer.disconnect();
|
|
4582
|
-
}, []);
|
|
4583
|
-
return [ref, isIntersecting];
|
|
4584
|
-
}
|
|
4585
4967
|
function useLocalStorage(key, initialValue) {
|
|
4586
|
-
const [storedValue, setStoredValue] =
|
|
4968
|
+
const [storedValue, setStoredValue] = React32.useState(() => {
|
|
4587
4969
|
if (typeof window === "undefined") return initialValue;
|
|
4588
4970
|
try {
|
|
4589
4971
|
const item = window.localStorage.getItem(key);
|
|
@@ -4592,7 +4974,7 @@ function useLocalStorage(key, initialValue) {
|
|
|
4592
4974
|
return initialValue;
|
|
4593
4975
|
}
|
|
4594
4976
|
});
|
|
4595
|
-
const setValue =
|
|
4977
|
+
const setValue = React32.useCallback(
|
|
4596
4978
|
(value) => {
|
|
4597
4979
|
try {
|
|
4598
4980
|
setStoredValue(value);
|
|
@@ -4607,11 +4989,11 @@ function useLocalStorage(key, initialValue) {
|
|
|
4607
4989
|
return [storedValue, setValue];
|
|
4608
4990
|
}
|
|
4609
4991
|
function useMediaQuery(query) {
|
|
4610
|
-
const [matches, setMatches] =
|
|
4992
|
+
const [matches, setMatches] = React32.useState(() => {
|
|
4611
4993
|
if (typeof window === "undefined") return false;
|
|
4612
4994
|
return window.matchMedia(query).matches;
|
|
4613
4995
|
});
|
|
4614
|
-
|
|
4996
|
+
React32.useEffect(() => {
|
|
4615
4997
|
if (typeof window === "undefined") return;
|
|
4616
4998
|
const mql = window.matchMedia(query);
|
|
4617
4999
|
setMatches(mql.matches);
|
|
@@ -4622,18 +5004,18 @@ function useMediaQuery(query) {
|
|
|
4622
5004
|
return matches;
|
|
4623
5005
|
}
|
|
4624
5006
|
function usePagination(total, pageSize) {
|
|
4625
|
-
const [page, setPage] =
|
|
5007
|
+
const [page, setPage] = React32.useState(1);
|
|
4626
5008
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
4627
5009
|
const offset = (page - 1) * pageSize;
|
|
4628
|
-
const goTo =
|
|
5010
|
+
const goTo = React32.useCallback(
|
|
4629
5011
|
(p) => setPage(Math.min(Math.max(1, p), totalPages)),
|
|
4630
5012
|
[totalPages]
|
|
4631
5013
|
);
|
|
4632
|
-
const next =
|
|
4633
|
-
const prev =
|
|
5014
|
+
const next = React32.useCallback(() => goTo(page + 1), [page, goTo]);
|
|
5015
|
+
const prev = React32.useCallback(() => goTo(page - 1), [page, goTo]);
|
|
4634
5016
|
return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
|
|
4635
5017
|
}
|
|
4636
5018
|
|
|
4637
|
-
export { AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card,
|
|
5019
|
+
export { ActivityFeed, AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, Carousel, ChangelogCard, ChangelogList, Chart, 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, Sortable, 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 };
|
|
4638
5020
|
//# sourceMappingURL=index.js.map
|
|
4639
5021
|
//# sourceMappingURL=index.js.map
|