@olwiba/ui 0.2.9 → 0.2.11
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 +53 -1
- package/dist/index.js +255 -130
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/app/AppShell.tsx +31 -3
- package/src/components/PricingCard.tsx +7 -1
- package/src/index.ts +6 -0
- package/src/marketing/PricingSection.tsx +7 -2
- package/src/motion/AnimatedSwap.tsx +200 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from './chunk-AMWLSHD6.js';
|
|
2
2
|
export { MdxContent, cn } from './chunk-AMWLSHD6.js';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React20 from 'react';
|
|
4
4
|
import { useRef, useEffect, useState } from 'react';
|
|
5
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';
|
|
@@ -13,7 +13,7 @@ import { CSS } from '@dnd-kit/utilities';
|
|
|
13
13
|
import { PieChart, Pie, Cell, BarChart, XAxis, YAxis, Bar, AreaChart, Area, LineChart, Line, ResponsiveContainer, Tooltip as Tooltip$1, CartesianGrid } from 'recharts';
|
|
14
14
|
import { toast } from 'sonner';
|
|
15
15
|
|
|
16
|
-
var OlwibaUIContext =
|
|
16
|
+
var OlwibaUIContext = React20.createContext({
|
|
17
17
|
isMobile: false,
|
|
18
18
|
mode: "default",
|
|
19
19
|
setMode: () => {
|
|
@@ -22,17 +22,17 @@ var OlwibaUIContext = React35.createContext({
|
|
|
22
22
|
function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
|
|
23
23
|
const detectedMobile = useIsMobile();
|
|
24
24
|
const isMobile = isMobileProp ?? detectedMobile;
|
|
25
|
-
const [mode, setMode] =
|
|
25
|
+
const [mode, setMode] = React20.useState(initialMode);
|
|
26
26
|
const variant = mode !== "default" ? mode : void 0;
|
|
27
27
|
return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
|
|
28
28
|
}
|
|
29
29
|
function useOlwibaUI() {
|
|
30
|
-
return
|
|
30
|
+
return React20.useContext(OlwibaUIContext);
|
|
31
31
|
}
|
|
32
32
|
function useUIMode() {
|
|
33
|
-
return
|
|
33
|
+
return React20.useContext(OlwibaUIContext).mode;
|
|
34
34
|
}
|
|
35
|
-
var Button =
|
|
35
|
+
var Button = React20.forwardRef(
|
|
36
36
|
({ mode, ...props }, ref) => {
|
|
37
37
|
const ctxMode = useUIMode();
|
|
38
38
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -40,7 +40,7 @@ var Button = React35.forwardRef(
|
|
|
40
40
|
}
|
|
41
41
|
);
|
|
42
42
|
Button.displayName = "Button";
|
|
43
|
-
var Card =
|
|
43
|
+
var Card = React20.forwardRef(
|
|
44
44
|
({ mode, ...props }, ref) => {
|
|
45
45
|
const ctxMode = useUIMode();
|
|
46
46
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -51,7 +51,7 @@ Card.displayName = "Card";
|
|
|
51
51
|
function Badge(props) {
|
|
52
52
|
return /* @__PURE__ */ jsx(Badge$1, { ...props });
|
|
53
53
|
}
|
|
54
|
-
var Input =
|
|
54
|
+
var Input = React20.forwardRef(
|
|
55
55
|
({ mode, ...props }, ref) => {
|
|
56
56
|
const ctxMode = useUIMode();
|
|
57
57
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -59,7 +59,7 @@ var Input = React35.forwardRef(
|
|
|
59
59
|
}
|
|
60
60
|
);
|
|
61
61
|
Input.displayName = "Input";
|
|
62
|
-
var Textarea =
|
|
62
|
+
var Textarea = React20.forwardRef(
|
|
63
63
|
({ mode, ...props }, ref) => {
|
|
64
64
|
const ctxMode = useUIMode();
|
|
65
65
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -67,13 +67,13 @@ var Textarea = React35.forwardRef(
|
|
|
67
67
|
}
|
|
68
68
|
);
|
|
69
69
|
Textarea.displayName = "Textarea";
|
|
70
|
-
var Checkbox =
|
|
70
|
+
var Checkbox = React20.forwardRef(({ mode, ...props }, ref) => {
|
|
71
71
|
const ctxMode = useUIMode();
|
|
72
72
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
73
73
|
return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
|
|
74
74
|
});
|
|
75
75
|
Checkbox.displayName = "Checkbox";
|
|
76
|
-
var Switch =
|
|
76
|
+
var Switch = React20.forwardRef(({ mode: _mode, ...props }, ref) => {
|
|
77
77
|
return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
|
|
78
78
|
});
|
|
79
79
|
Switch.displayName = "Switch";
|
|
@@ -261,6 +261,7 @@ function PublicPageFrame({
|
|
|
261
261
|
}) {
|
|
262
262
|
return /* @__PURE__ */ jsx("div", { className: cn("min-h-screen", surfaceMap[surface], className), ...props, children: /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", spacingMap2[spacing], maxWidthMap2[maxWidth]), children }) });
|
|
263
263
|
}
|
|
264
|
+
var RAIL_SQUARE = "group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!min-w-8 group-data-[collapsible=icon]:!max-w-8 group-data-[collapsible=icon]:!p-0 group-data-[collapsible=icon]:transition-[width,height]";
|
|
264
265
|
function NavUser({ user }) {
|
|
265
266
|
const { isMobile } = useSidebar();
|
|
266
267
|
const uiMode = useUIMode();
|
|
@@ -271,7 +272,11 @@ function NavUser({ user }) {
|
|
|
271
272
|
SidebarMenuButton,
|
|
272
273
|
{
|
|
273
274
|
size: "lg",
|
|
274
|
-
|
|
275
|
+
tooltip: user.name ?? user.email,
|
|
276
|
+
className: cn$1(
|
|
277
|
+
"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
|
|
278
|
+
RAIL_SQUARE
|
|
279
|
+
),
|
|
275
280
|
children: [
|
|
276
281
|
/* @__PURE__ */ jsxs(Avatar, { mode: avatarMode, size: "sm", className: "grayscale", children: [
|
|
277
282
|
user.avatar && /* @__PURE__ */ jsx(AvatarImage, { src: user.avatar, alt: user.name }),
|
|
@@ -345,8 +350,7 @@ function ShellSidebar({
|
|
|
345
350
|
{
|
|
346
351
|
asChild: true,
|
|
347
352
|
size: "lg",
|
|
348
|
-
|
|
349
|
-
className: "group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!min-w-8 group-data-[collapsible=icon]:!max-w-8 group-data-[collapsible=icon]:!p-0",
|
|
353
|
+
className: RAIL_SQUARE,
|
|
350
354
|
children: renderLink({
|
|
351
355
|
href: brand.href ?? "#",
|
|
352
356
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -534,7 +538,7 @@ function DefaultForm({
|
|
|
534
538
|
const isForgotPassword = mode === "forgot-password";
|
|
535
539
|
const isResetPassword = mode === "reset-password";
|
|
536
540
|
const isVerify = mode === "verify";
|
|
537
|
-
const [code, setCode] =
|
|
541
|
+
const [code, setCode] = React20.useState("");
|
|
538
542
|
const hasPrefill = !!(defaultEmail || defaultPassword);
|
|
539
543
|
const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
|
|
540
544
|
const copy = authCopy[mode];
|
|
@@ -694,10 +698,10 @@ function DataTable({
|
|
|
694
698
|
emptyMessage = "No results.",
|
|
695
699
|
className
|
|
696
700
|
}) {
|
|
697
|
-
const [sorting, setSorting] =
|
|
698
|
-
const [rowSelection, setRowSelection] =
|
|
699
|
-
const [globalFilter, setGlobalFilter] =
|
|
700
|
-
const allColumns =
|
|
701
|
+
const [sorting, setSorting] = React20.useState([]);
|
|
702
|
+
const [rowSelection, setRowSelection] = React20.useState({});
|
|
703
|
+
const [globalFilter, setGlobalFilter] = React20.useState("");
|
|
704
|
+
const allColumns = React20.useMemo(
|
|
701
705
|
() => selectable ? [selectColumn, ...columns] : columns,
|
|
702
706
|
[selectable, columns]
|
|
703
707
|
);
|
|
@@ -719,7 +723,7 @@ function DataTable({
|
|
|
719
723
|
initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
|
|
720
724
|
enableRowSelection: selectable
|
|
721
725
|
});
|
|
722
|
-
|
|
726
|
+
React20.useEffect(() => {
|
|
723
727
|
if (!onSelectionChange) return;
|
|
724
728
|
onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
|
|
725
729
|
}, [rowSelection]);
|
|
@@ -799,8 +803,8 @@ function DataTable({
|
|
|
799
803
|
] });
|
|
800
804
|
}
|
|
801
805
|
function InviteDialog({ roles, onInvite, onClose }) {
|
|
802
|
-
const [email, setEmail] =
|
|
803
|
-
const [role, setRole] =
|
|
806
|
+
const [email, setEmail] = React20.useState("");
|
|
807
|
+
const [role, setRole] = React20.useState(roles[roles.length - 1] ?? roles[0]);
|
|
804
808
|
return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
|
|
805
809
|
"form",
|
|
806
810
|
{
|
|
@@ -844,8 +848,8 @@ function TeamMembersPanel({
|
|
|
844
848
|
title = "Team members",
|
|
845
849
|
description = "Manage who has access to this workspace."
|
|
846
850
|
}) {
|
|
847
|
-
const [inviteOpen, setInviteOpen] =
|
|
848
|
-
const columns =
|
|
851
|
+
const [inviteOpen, setInviteOpen] = React20.useState(false);
|
|
852
|
+
const columns = React20.useMemo(() => [
|
|
849
853
|
{
|
|
850
854
|
accessorKey: "name",
|
|
851
855
|
header: "Name",
|
|
@@ -914,7 +918,7 @@ function BillingPanel({
|
|
|
914
918
|
onUpdatePaymentMethod,
|
|
915
919
|
className
|
|
916
920
|
}) {
|
|
917
|
-
const invoiceColumns =
|
|
921
|
+
const invoiceColumns = React20.useMemo(() => [
|
|
918
922
|
{ accessorKey: "date", header: "Date", enableSorting: false },
|
|
919
923
|
{ accessorKey: "amount", header: "Amount", enableSorting: false },
|
|
920
924
|
{
|
|
@@ -993,9 +997,9 @@ function OnboardingWizard({
|
|
|
993
997
|
completeLabel = "Finish",
|
|
994
998
|
className
|
|
995
999
|
}) {
|
|
996
|
-
const [internalStep, setInternalStep] =
|
|
997
|
-
const [error, setError] =
|
|
998
|
-
const [pending, setPending] =
|
|
1000
|
+
const [internalStep, setInternalStep] = React20.useState(0);
|
|
1001
|
+
const [error, setError] = React20.useState(null);
|
|
1002
|
+
const [pending, setPending] = React20.useState(false);
|
|
999
1003
|
const index = stepProp ?? internalStep;
|
|
1000
1004
|
const current = steps[index];
|
|
1001
1005
|
const isLast = index === steps.length - 1;
|
|
@@ -1325,10 +1329,10 @@ function UpdateBanner({
|
|
|
1325
1329
|
buttonLabel = "Refresh now",
|
|
1326
1330
|
className
|
|
1327
1331
|
}) {
|
|
1328
|
-
const [show, setShow] =
|
|
1329
|
-
const [entered, setEntered] =
|
|
1330
|
-
const [refreshing, setRefreshing] =
|
|
1331
|
-
|
|
1332
|
+
const [show, setShow] = React20.useState(forceShow);
|
|
1333
|
+
const [entered, setEntered] = React20.useState(false);
|
|
1334
|
+
const [refreshing, setRefreshing] = React20.useState(false);
|
|
1335
|
+
React20.useEffect(() => {
|
|
1332
1336
|
if (forceShow) {
|
|
1333
1337
|
setShow(true);
|
|
1334
1338
|
return;
|
|
@@ -1351,7 +1355,7 @@ function UpdateBanner({
|
|
|
1351
1355
|
clearInterval(id);
|
|
1352
1356
|
};
|
|
1353
1357
|
}, [currentVersion, fetchVersion, forceShow, intervalMs]);
|
|
1354
|
-
|
|
1358
|
+
React20.useEffect(() => {
|
|
1355
1359
|
if (!show) {
|
|
1356
1360
|
setEntered(false);
|
|
1357
1361
|
return;
|
|
@@ -1424,9 +1428,9 @@ function FadeIn({
|
|
|
1424
1428
|
style,
|
|
1425
1429
|
...props
|
|
1426
1430
|
}) {
|
|
1427
|
-
const ref =
|
|
1428
|
-
const [visible, setVisible] =
|
|
1429
|
-
|
|
1431
|
+
const ref = React20.useRef(null);
|
|
1432
|
+
const [visible, setVisible] = React20.useState(false);
|
|
1433
|
+
React20.useEffect(() => {
|
|
1430
1434
|
const el = ref.current;
|
|
1431
1435
|
if (!el) return;
|
|
1432
1436
|
const observer = new IntersectionObserver(
|
|
@@ -1621,16 +1625,16 @@ function Carousel({
|
|
|
1621
1625
|
className
|
|
1622
1626
|
}) {
|
|
1623
1627
|
const mode = useUIVariant();
|
|
1624
|
-
const trackRef =
|
|
1625
|
-
const [canPrev, setCanPrev] =
|
|
1626
|
-
const [canNext, setCanNext] =
|
|
1627
|
-
const updateScrollState =
|
|
1628
|
+
const trackRef = React20.useRef(null);
|
|
1629
|
+
const [canPrev, setCanPrev] = React20.useState(false);
|
|
1630
|
+
const [canNext, setCanNext] = React20.useState(true);
|
|
1631
|
+
const updateScrollState = React20.useCallback(() => {
|
|
1628
1632
|
const track = trackRef.current;
|
|
1629
1633
|
if (!track) return;
|
|
1630
1634
|
setCanPrev(track.scrollLeft > 8);
|
|
1631
1635
|
setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
|
|
1632
1636
|
}, []);
|
|
1633
|
-
|
|
1637
|
+
React20.useEffect(() => {
|
|
1634
1638
|
const track = trackRef.current;
|
|
1635
1639
|
if (!track) return;
|
|
1636
1640
|
updateScrollState();
|
|
@@ -1687,7 +1691,7 @@ function Carousel({
|
|
|
1687
1691
|
role: "region",
|
|
1688
1692
|
"aria-label": ariaLabel,
|
|
1689
1693
|
className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1690
|
-
children:
|
|
1694
|
+
children: React20.Children.map(
|
|
1691
1695
|
children,
|
|
1692
1696
|
(child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
|
|
1693
1697
|
)
|
|
@@ -1712,9 +1716,9 @@ function StaggerChildren({
|
|
|
1712
1716
|
className,
|
|
1713
1717
|
...props
|
|
1714
1718
|
}) {
|
|
1715
|
-
const ref =
|
|
1716
|
-
const [visible, setVisible] =
|
|
1717
|
-
|
|
1719
|
+
const ref = React20.useRef(null);
|
|
1720
|
+
const [visible, setVisible] = React20.useState(false);
|
|
1721
|
+
React20.useEffect(() => {
|
|
1718
1722
|
const el = ref.current;
|
|
1719
1723
|
if (!el) return;
|
|
1720
1724
|
const observer = new IntersectionObserver(
|
|
@@ -1732,7 +1736,7 @@ function StaggerChildren({
|
|
|
1732
1736
|
return () => observer.disconnect();
|
|
1733
1737
|
}, [once]);
|
|
1734
1738
|
const [tx, ty] = translateMap2[direction];
|
|
1735
|
-
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children:
|
|
1739
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React20.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
|
|
1736
1740
|
"div",
|
|
1737
1741
|
{
|
|
1738
1742
|
className: "h-full",
|
|
@@ -1874,7 +1878,7 @@ function StepsSection({
|
|
|
1874
1878
|
/* @__PURE__ */ jsx("div", { className: "mt-12", children: resolvedGroups.map((group, groupIndex) => {
|
|
1875
1879
|
const startNumber = nextNumber;
|
|
1876
1880
|
nextNumber += group.steps.length;
|
|
1877
|
-
return /* @__PURE__ */ jsxs(
|
|
1881
|
+
return /* @__PURE__ */ jsxs(React20.Fragment, { children: [
|
|
1878
1882
|
/* @__PURE__ */ jsx("div", { className: cn$1(groupIndex > 0 && "mt-10"), children: /* @__PURE__ */ jsx(TimelineGroup, { steps: group.steps, startNumber, fadeIn: groupIndex > 0 }) }),
|
|
1879
1883
|
group.after && /* @__PURE__ */ jsx("div", { className: "mt-16", children: group.after })
|
|
1880
1884
|
] }, groupIndex);
|
|
@@ -2044,9 +2048,9 @@ function FeatureMarqueeSection({
|
|
|
2044
2048
|
] });
|
|
2045
2049
|
}
|
|
2046
2050
|
function useIntersectionObserver(options) {
|
|
2047
|
-
const ref =
|
|
2048
|
-
const [isIntersecting, setIsIntersecting] =
|
|
2049
|
-
|
|
2051
|
+
const ref = React20.useRef(null);
|
|
2052
|
+
const [isIntersecting, setIsIntersecting] = React20.useState(false);
|
|
2053
|
+
React20.useEffect(() => {
|
|
2050
2054
|
const el = ref.current;
|
|
2051
2055
|
if (!el) return;
|
|
2052
2056
|
const observer = new IntersectionObserver(([entry]) => {
|
|
@@ -2068,8 +2072,8 @@ function ShowcaseCta({
|
|
|
2068
2072
|
sectionClasses
|
|
2069
2073
|
}) {
|
|
2070
2074
|
const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
|
|
2071
|
-
const [visible, setVisible] =
|
|
2072
|
-
|
|
2075
|
+
const [visible, setVisible] = React20.useState(false);
|
|
2076
|
+
React20.useEffect(() => {
|
|
2073
2077
|
if (intersecting) setVisible(true);
|
|
2074
2078
|
}, [intersecting]);
|
|
2075
2079
|
return /* @__PURE__ */ jsxs(
|
|
@@ -2153,6 +2157,122 @@ function CtaSection(props) {
|
|
|
2153
2157
|
] }) })
|
|
2154
2158
|
] }) });
|
|
2155
2159
|
}
|
|
2160
|
+
var EFFECTS = {
|
|
2161
|
+
// Odometer. No opacity at all — the two values are solid and one physically
|
|
2162
|
+
// displaces the other, which is what makes it read as a mechanism rather
|
|
2163
|
+
// than a crossfade. Needs `clip`, or the departing value is legible above
|
|
2164
|
+
// the container the whole way out.
|
|
2165
|
+
roll: {
|
|
2166
|
+
clip: true,
|
|
2167
|
+
duration: 320,
|
|
2168
|
+
easing: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
2169
|
+
enter: { from: { transform: "translateY(100%)" }, to: { transform: "translateY(0)" } },
|
|
2170
|
+
exit: { from: { transform: "translateY(0)" }, to: { transform: "translateY(-100%)" } }
|
|
2171
|
+
},
|
|
2172
|
+
// The neutral one. No movement, so it never fights a layout that's also
|
|
2173
|
+
// reflowing around it.
|
|
2174
|
+
fade: {
|
|
2175
|
+
clip: false,
|
|
2176
|
+
duration: 220,
|
|
2177
|
+
easing: "ease",
|
|
2178
|
+
enter: { from: { opacity: 0 }, to: { opacity: 1 } },
|
|
2179
|
+
exit: { from: { opacity: 1 }, to: { opacity: 0 } }
|
|
2180
|
+
},
|
|
2181
|
+
// Horizontal, and short: 0.75rem of travel carried by opacity, so it needs
|
|
2182
|
+
// no clipping and won't crop a ring or drop shadow on whatever it wraps.
|
|
2183
|
+
slide: {
|
|
2184
|
+
clip: false,
|
|
2185
|
+
duration: 280,
|
|
2186
|
+
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
2187
|
+
enter: {
|
|
2188
|
+
from: { opacity: 0, transform: "translateX(0.75rem)" },
|
|
2189
|
+
to: { opacity: 1, transform: "translateX(0)" }
|
|
2190
|
+
},
|
|
2191
|
+
exit: {
|
|
2192
|
+
from: { opacity: 1, transform: "translateX(0)" },
|
|
2193
|
+
to: { opacity: 0, transform: "translateX(-0.75rem)" }
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
var MODE_EFFECT = {
|
|
2198
|
+
default: "fade",
|
|
2199
|
+
playful: "roll",
|
|
2200
|
+
smooth: "slide"
|
|
2201
|
+
};
|
|
2202
|
+
function AnimatedSwap({
|
|
2203
|
+
swapKey,
|
|
2204
|
+
effect,
|
|
2205
|
+
duration,
|
|
2206
|
+
children,
|
|
2207
|
+
className,
|
|
2208
|
+
style,
|
|
2209
|
+
...props
|
|
2210
|
+
}) {
|
|
2211
|
+
const mode = useUIMode();
|
|
2212
|
+
const resolved = effect ?? MODE_EFFECT[mode];
|
|
2213
|
+
const spec = typeof resolved !== "string" ? resolved : resolved === "none" ? null : EFFECTS[resolved];
|
|
2214
|
+
const ms = duration ?? spec?.duration ?? 0;
|
|
2215
|
+
const reduced = usePrefersReducedMotion();
|
|
2216
|
+
const animated = spec !== null && !reduced;
|
|
2217
|
+
const previous = React20.useRef(children);
|
|
2218
|
+
const seenKey = React20.useRef(swapKey);
|
|
2219
|
+
const [swap, setSwap] = React20.useState(null);
|
|
2220
|
+
if (seenKey.current !== swapKey) {
|
|
2221
|
+
const out = previous.current;
|
|
2222
|
+
seenKey.current = swapKey;
|
|
2223
|
+
if (animated) setSwap({ key: swapKey, out, running: false });
|
|
2224
|
+
}
|
|
2225
|
+
React20.useEffect(() => {
|
|
2226
|
+
previous.current = children;
|
|
2227
|
+
});
|
|
2228
|
+
React20.useEffect(() => {
|
|
2229
|
+
if (!swap || swap.running) return;
|
|
2230
|
+
const raf = requestAnimationFrame(() => {
|
|
2231
|
+
setSwap((s) => s && s.key === swap.key ? { ...s, running: true } : s);
|
|
2232
|
+
});
|
|
2233
|
+
return () => cancelAnimationFrame(raf);
|
|
2234
|
+
}, [swap]);
|
|
2235
|
+
React20.useEffect(() => {
|
|
2236
|
+
if (!swap?.running) return;
|
|
2237
|
+
const done = setTimeout(() => {
|
|
2238
|
+
setSwap((s) => s && s.key === swap.key ? null : s);
|
|
2239
|
+
}, ms);
|
|
2240
|
+
return () => clearTimeout(done);
|
|
2241
|
+
}, [swap, ms]);
|
|
2242
|
+
const phase = (layer) => {
|
|
2243
|
+
if (!swap || !spec) return void 0;
|
|
2244
|
+
return {
|
|
2245
|
+
...spec[layer][swap.running ? "to" : "from"],
|
|
2246
|
+
transition: `transform ${ms}ms ${spec.easing}, opacity ${ms}ms ${spec.easing}`
|
|
2247
|
+
};
|
|
2248
|
+
};
|
|
2249
|
+
return /* @__PURE__ */ jsxs(
|
|
2250
|
+
"span",
|
|
2251
|
+
{
|
|
2252
|
+
className: cn$1("relative inline-block", swap && spec?.clip && "overflow-hidden", className),
|
|
2253
|
+
style,
|
|
2254
|
+
...props,
|
|
2255
|
+
children: [
|
|
2256
|
+
/* @__PURE__ */ jsx("span", { className: "block", style: phase("enter"), children }),
|
|
2257
|
+
swap && // Pinned rather than inset-0: stretching the outgoing copy to the
|
|
2258
|
+
// incoming one's box would resize it mid-exit.
|
|
2259
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-0 top-0 block", "aria-hidden": "true", style: phase("exit"), children: swap.out })
|
|
2260
|
+
]
|
|
2261
|
+
}
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
function usePrefersReducedMotion() {
|
|
2265
|
+
const [reduced, setReduced] = React20.useState(false);
|
|
2266
|
+
React20.useEffect(() => {
|
|
2267
|
+
const query = window.matchMedia?.("(prefers-reduced-motion: reduce)");
|
|
2268
|
+
if (!query) return;
|
|
2269
|
+
setReduced(query.matches);
|
|
2270
|
+
const onChange = (e) => setReduced(e.matches);
|
|
2271
|
+
query.addEventListener("change", onChange);
|
|
2272
|
+
return () => query.removeEventListener("change", onChange);
|
|
2273
|
+
}, []);
|
|
2274
|
+
return reduced;
|
|
2275
|
+
}
|
|
2156
2276
|
function PricingCard({
|
|
2157
2277
|
name,
|
|
2158
2278
|
price,
|
|
@@ -2186,7 +2306,7 @@ function PricingCard({
|
|
|
2186
2306
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2187
2307
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: name }),
|
|
2188
2308
|
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1", children: [
|
|
2189
|
-
/* @__PURE__ */ jsx(
|
|
2309
|
+
/* @__PURE__ */ jsx(AnimatedSwap, { swapKey: price, className: "text-4xl font-bold tracking-tight", children: price }),
|
|
2190
2310
|
/* @__PURE__ */ jsx("span", { className: "mb-1 text-sm text-muted-foreground", children: period })
|
|
2191
2311
|
] }),
|
|
2192
2312
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
|
|
@@ -2246,8 +2366,8 @@ function CountdownTimer({
|
|
|
2246
2366
|
className,
|
|
2247
2367
|
compact = false
|
|
2248
2368
|
}) {
|
|
2249
|
-
const [timeLeft, setTimeLeft] =
|
|
2250
|
-
|
|
2369
|
+
const [timeLeft, setTimeLeft] = React20.useState(() => getTimeLeft(deadline));
|
|
2370
|
+
React20.useEffect(() => {
|
|
2251
2371
|
setTimeLeft(getTimeLeft(deadline));
|
|
2252
2372
|
const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
|
|
2253
2373
|
return () => clearInterval(id);
|
|
@@ -2273,7 +2393,7 @@ function CountdownTimer({
|
|
|
2273
2393
|
];
|
|
2274
2394
|
return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
|
|
2275
2395
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
2276
|
-
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(
|
|
2396
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React20.Fragment, { children: [
|
|
2277
2397
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
2278
2398
|
String(value).padStart(2, "0"),
|
|
2279
2399
|
/* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
|
|
@@ -2328,13 +2448,13 @@ function PricingSection({
|
|
|
2328
2448
|
onSelectPlan,
|
|
2329
2449
|
pendingPlanName
|
|
2330
2450
|
}) {
|
|
2331
|
-
const [annual, setAnnual] =
|
|
2451
|
+
const [annual, setAnnual] = React20.useState(false);
|
|
2332
2452
|
const isOneTime = mode === "one-time";
|
|
2333
2453
|
const useCadences = !!cadences?.length;
|
|
2334
2454
|
const initialCadence = (defaultCadence && cadences?.some((c) => c.key === defaultCadence) ? defaultCadence : null) ?? cadences?.[0]?.key ?? "";
|
|
2335
|
-
const [activeCadence, setActiveCadence] =
|
|
2455
|
+
const [activeCadence, setActiveCadence] = React20.useState(initialCadence);
|
|
2336
2456
|
const cadence = cadences?.find((c) => c.key === activeCadence);
|
|
2337
|
-
const saveBadges =
|
|
2457
|
+
const saveBadges = React20.useMemo(
|
|
2338
2458
|
() => useCadences ? computeSaveBadges(plans, cadences, initialCadence) : {},
|
|
2339
2459
|
[useCadences, plans, cadences, initialCadence]
|
|
2340
2460
|
);
|
|
@@ -2363,7 +2483,12 @@ function PricingSection({
|
|
|
2363
2483
|
),
|
|
2364
2484
|
children: [
|
|
2365
2485
|
entry.label,
|
|
2366
|
-
saveBadges[entry.key] &&
|
|
2486
|
+
saveBadges[entry.key] && // `default` (primary), not `secondary`: the toggle is
|
|
2487
|
+
// `bg-muted` with a `bg-background` active tab, and a
|
|
2488
|
+
// secondary badge is the same grey as both — the
|
|
2489
|
+
// saving is the incentive, so it has to carry brand
|
|
2490
|
+
// colour to read at all.
|
|
2491
|
+
/* @__PURE__ */ jsx(Badge$1, { variant: "default", className: "text-xs", children: saveBadges[entry.key] })
|
|
2367
2492
|
]
|
|
2368
2493
|
},
|
|
2369
2494
|
entry.key
|
|
@@ -2393,7 +2518,7 @@ function PricingSection({
|
|
|
2393
2518
|
),
|
|
2394
2519
|
children: [
|
|
2395
2520
|
"Annual",
|
|
2396
|
-
saveBadge && /* @__PURE__ */ jsx(Badge$1, { variant: "
|
|
2521
|
+
saveBadge && /* @__PURE__ */ jsx(Badge$1, { variant: "default", className: "text-xs", children: saveBadge })
|
|
2397
2522
|
]
|
|
2398
2523
|
}
|
|
2399
2524
|
)
|
|
@@ -2623,10 +2748,10 @@ function CountUp({
|
|
|
2623
2748
|
className,
|
|
2624
2749
|
...props
|
|
2625
2750
|
}) {
|
|
2626
|
-
const ref =
|
|
2627
|
-
const [value, setValue] =
|
|
2628
|
-
const hasStarted =
|
|
2629
|
-
|
|
2751
|
+
const ref = React20.useRef(null);
|
|
2752
|
+
const [value, setValue] = React20.useState(from);
|
|
2753
|
+
const hasStarted = React20.useRef(false);
|
|
2754
|
+
React20.useEffect(() => {
|
|
2630
2755
|
const el = ref.current;
|
|
2631
2756
|
if (!el) return;
|
|
2632
2757
|
const observer = new IntersectionObserver(
|
|
@@ -2719,8 +2844,8 @@ function NewsletterSection({
|
|
|
2719
2844
|
successDescription = defaults.successDescription,
|
|
2720
2845
|
onSubscribe
|
|
2721
2846
|
} = {}) {
|
|
2722
|
-
const [email, setEmail] =
|
|
2723
|
-
const [submitted, setSubmitted] =
|
|
2847
|
+
const [email, setEmail] = React20.useState("");
|
|
2848
|
+
const [submitted, setSubmitted] = React20.useState(false);
|
|
2724
2849
|
const mode = useUIVariant();
|
|
2725
2850
|
const sectionClasses = cn$1(
|
|
2726
2851
|
"overflow-hidden bg-card",
|
|
@@ -2811,7 +2936,7 @@ function ContactSection({
|
|
|
2811
2936
|
sendAnotherLabel = defaults2.sendAnotherLabel,
|
|
2812
2937
|
onSubmit
|
|
2813
2938
|
} = {}) {
|
|
2814
|
-
const [submitted, setSubmitted] =
|
|
2939
|
+
const [submitted, setSubmitted] = React20.useState(false);
|
|
2815
2940
|
const mode = useUIVariant();
|
|
2816
2941
|
const sectionClasses = cn$1(
|
|
2817
2942
|
"overflow-hidden bg-card",
|
|
@@ -3109,10 +3234,10 @@ function Navbar({
|
|
|
3109
3234
|
controls,
|
|
3110
3235
|
renderLink = defaultRenderLink7
|
|
3111
3236
|
}) {
|
|
3112
|
-
const [open, setOpen] =
|
|
3237
|
+
const [open, setOpen] = React20.useState(false);
|
|
3113
3238
|
const brandHref = brand.href ?? "/";
|
|
3114
3239
|
const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
|
|
3115
|
-
const closeMobileMenu =
|
|
3240
|
+
const closeMobileMenu = React20.useCallback(() => setOpen(false), []);
|
|
3116
3241
|
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: [
|
|
3117
3242
|
renderLink({
|
|
3118
3243
|
href: brandHref,
|
|
@@ -3127,7 +3252,7 @@ function Navbar({
|
|
|
3127
3252
|
children: link.label
|
|
3128
3253
|
}) }, link.label)) }),
|
|
3129
3254
|
hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
|
|
3130
|
-
controls?.map((control, i) => /* @__PURE__ */ jsx(
|
|
3255
|
+
controls?.map((control, i) => /* @__PURE__ */ jsx(React20.Fragment, { children: control }, i)),
|
|
3131
3256
|
(cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
|
|
3132
3257
|
cta?.secondary && renderLink({
|
|
3133
3258
|
href: cta.secondary.href,
|
|
@@ -3151,7 +3276,7 @@ function Navbar({
|
|
|
3151
3276
|
/* @__PURE__ */ jsx("span", { children: brand.name })
|
|
3152
3277
|
] })
|
|
3153
3278
|
}) }) }),
|
|
3154
|
-
controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(
|
|
3279
|
+
controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React20.Fragment, { children: control }, i)) }) : null,
|
|
3155
3280
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
3156
3281
|
/* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, children: renderLink({
|
|
3157
3282
|
href: link.href,
|
|
@@ -3300,7 +3425,7 @@ function LogoStrip({
|
|
|
3300
3425
|
] });
|
|
3301
3426
|
}
|
|
3302
3427
|
function GridUnderlay({ variant, size = 24, className, ...props }) {
|
|
3303
|
-
const id =
|
|
3428
|
+
const id = React20.useId();
|
|
3304
3429
|
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: [
|
|
3305
3430
|
/* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
|
|
3306
3431
|
/* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
|
|
@@ -3328,9 +3453,9 @@ function GlowUnderlay({
|
|
|
3328
3453
|
style,
|
|
3329
3454
|
...props
|
|
3330
3455
|
}) {
|
|
3331
|
-
const [pos, setPos] =
|
|
3332
|
-
const containerRef =
|
|
3333
|
-
|
|
3456
|
+
const [pos, setPos] = React20.useState({ x: "50%", y: "50%" });
|
|
3457
|
+
const containerRef = React20.useRef(null);
|
|
3458
|
+
React20.useEffect(() => {
|
|
3334
3459
|
if (!followCursor) return;
|
|
3335
3460
|
const handleMouseMove = (e) => {
|
|
3336
3461
|
const el = containerRef.current;
|
|
@@ -3469,8 +3594,8 @@ function PageTransition({
|
|
|
3469
3594
|
style,
|
|
3470
3595
|
...props
|
|
3471
3596
|
}) {
|
|
3472
|
-
const [mounted, setMounted] =
|
|
3473
|
-
|
|
3597
|
+
const [mounted, setMounted] = React20.useState(false);
|
|
3598
|
+
React20.useEffect(() => {
|
|
3474
3599
|
const id = requestAnimationFrame(() => setMounted(true));
|
|
3475
3600
|
return () => cancelAnimationFrame(id);
|
|
3476
3601
|
}, []);
|
|
@@ -3545,7 +3670,7 @@ function Sortable({
|
|
|
3545
3670
|
if (oldIndex === -1 || newIndex === -1) return;
|
|
3546
3671
|
onReorder(arrayMove(items, oldIndex, newIndex));
|
|
3547
3672
|
};
|
|
3548
|
-
const childArray =
|
|
3673
|
+
const childArray = React20.Children.toArray(children);
|
|
3549
3674
|
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(
|
|
3550
3675
|
(child, i) => items[i] === void 0 ? null : /* @__PURE__ */ jsx(SortableItem, { id: items[i], disabled, className: itemClassName, children: child }, items[i])
|
|
3551
3676
|
) }) }) });
|
|
@@ -3557,8 +3682,8 @@ function Spotlight({
|
|
|
3557
3682
|
triggerClassName,
|
|
3558
3683
|
shortcut = "\u2318K"
|
|
3559
3684
|
}) {
|
|
3560
|
-
const [open, setOpen] =
|
|
3561
|
-
|
|
3685
|
+
const [open, setOpen] = React20.useState(false);
|
|
3686
|
+
React20.useEffect(() => {
|
|
3562
3687
|
const down = (e) => {
|
|
3563
3688
|
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
|
3564
3689
|
e.preventDefault();
|
|
@@ -3588,7 +3713,7 @@ function Spotlight({
|
|
|
3588
3713
|
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
3589
3714
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
3590
3715
|
/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
|
|
3591
|
-
groups.map((group, i) => /* @__PURE__ */ jsxs(
|
|
3716
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React20.Fragment, { children: [
|
|
3592
3717
|
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
3593
3718
|
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
3594
3719
|
CommandItem,
|
|
@@ -3613,8 +3738,8 @@ function Spotlight({
|
|
|
3613
3738
|
var BASE = 40;
|
|
3614
3739
|
var MAGNIFY_RADIUS = 80;
|
|
3615
3740
|
function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
|
|
3616
|
-
const [mouseX, setMouseX] =
|
|
3617
|
-
const containerRef =
|
|
3741
|
+
const [mouseX, setMouseX] = React20.useState(null);
|
|
3742
|
+
const containerRef = React20.useRef(null);
|
|
3618
3743
|
function getScale(index) {
|
|
3619
3744
|
if (mouseX === null || !containerRef.current) return 1;
|
|
3620
3745
|
const container = containerRef.current;
|
|
@@ -3713,7 +3838,7 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
|
|
|
3713
3838
|
] }) });
|
|
3714
3839
|
}
|
|
3715
3840
|
function RegisterHotkeys({ hotkeys }) {
|
|
3716
|
-
|
|
3841
|
+
React20.useEffect(() => {
|
|
3717
3842
|
const handleKeyDown = (event) => {
|
|
3718
3843
|
for (const hotkey of hotkeys) {
|
|
3719
3844
|
const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
|
|
@@ -3730,10 +3855,10 @@ function RegisterHotkeys({ hotkeys }) {
|
|
|
3730
3855
|
return null;
|
|
3731
3856
|
}
|
|
3732
3857
|
function useControlledOpen(defaultOpen = false) {
|
|
3733
|
-
const [isOpen, setIsOpen] =
|
|
3734
|
-
const open =
|
|
3735
|
-
const close =
|
|
3736
|
-
const toggle =
|
|
3858
|
+
const [isOpen, setIsOpen] = React20.useState(defaultOpen);
|
|
3859
|
+
const open = React20.useCallback(() => setIsOpen(true), []);
|
|
3860
|
+
const close = React20.useCallback(() => setIsOpen(false), []);
|
|
3861
|
+
const toggle = React20.useCallback(() => setIsOpen((prev) => !prev), []);
|
|
3737
3862
|
return { isOpen, open, close, toggle, setIsOpen };
|
|
3738
3863
|
}
|
|
3739
3864
|
function CommandMenu({
|
|
@@ -3746,7 +3871,7 @@ function CommandMenu({
|
|
|
3746
3871
|
}) {
|
|
3747
3872
|
const internal = useControlledOpen(false);
|
|
3748
3873
|
const isOpen = openProp ?? internal.isOpen;
|
|
3749
|
-
const setOpen =
|
|
3874
|
+
const setOpen = React20.useCallback(
|
|
3750
3875
|
(next) => {
|
|
3751
3876
|
if (openProp === void 0) internal.setIsOpen(next);
|
|
3752
3877
|
onOpenChange?.(next);
|
|
@@ -3771,7 +3896,7 @@ function CommandMenu({
|
|
|
3771
3896
|
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
3772
3897
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
3773
3898
|
/* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
|
|
3774
|
-
groups.map((group, i) => /* @__PURE__ */ jsxs(
|
|
3899
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React20.Fragment, { children: [
|
|
3775
3900
|
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
3776
3901
|
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
3777
3902
|
CommandItem,
|
|
@@ -3826,7 +3951,7 @@ function DataView({
|
|
|
3826
3951
|
}
|
|
3827
3952
|
);
|
|
3828
3953
|
}
|
|
3829
|
-
return /* @__PURE__ */ jsx(AppGrid, { columns: gridColumns, gap, className: cn$1(className), children: items.map((item) => /* @__PURE__ */ jsx(
|
|
3954
|
+
return /* @__PURE__ */ jsx(AppGrid, { columns: gridColumns, gap, className: cn$1(className), children: items.map((item) => /* @__PURE__ */ jsx(React20.Fragment, { children: renderCard(item) }, getRowId(item))) });
|
|
3830
3955
|
}
|
|
3831
3956
|
var TOKEN_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
|
|
3832
3957
|
var seriesColor = (s, i) => s.color ?? TOKEN_COLORS[i % TOKEN_COLORS.length];
|
|
@@ -4000,20 +4125,20 @@ function FileUpload({
|
|
|
4000
4125
|
hint,
|
|
4001
4126
|
className
|
|
4002
4127
|
}) {
|
|
4003
|
-
const [internalFiles, setInternalFiles] =
|
|
4004
|
-
const [isDragging, setIsDragging] =
|
|
4005
|
-
const [validationError, setValidationError] =
|
|
4006
|
-
const inputRef =
|
|
4007
|
-
const dragDepth =
|
|
4128
|
+
const [internalFiles, setInternalFiles] = React20.useState(defaultFiles ?? []);
|
|
4129
|
+
const [isDragging, setIsDragging] = React20.useState(false);
|
|
4130
|
+
const [validationError, setValidationError] = React20.useState(null);
|
|
4131
|
+
const inputRef = React20.useRef(null);
|
|
4132
|
+
const dragDepth = React20.useRef(0);
|
|
4008
4133
|
const files = filesProp ?? internalFiles;
|
|
4009
|
-
const setFiles =
|
|
4134
|
+
const setFiles = React20.useCallback(
|
|
4010
4135
|
(next) => {
|
|
4011
4136
|
if (!filesProp) setInternalFiles(next);
|
|
4012
4137
|
onFilesChange?.(next);
|
|
4013
4138
|
},
|
|
4014
4139
|
[filesProp, onFilesChange]
|
|
4015
4140
|
);
|
|
4016
|
-
const acceptList =
|
|
4141
|
+
const acceptList = React20.useMemo(
|
|
4017
4142
|
() => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
|
|
4018
4143
|
[accept]
|
|
4019
4144
|
);
|
|
@@ -4737,7 +4862,7 @@ function PageHeader({
|
|
|
4737
4862
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
|
|
4738
4863
|
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) => {
|
|
4739
4864
|
const isLast = i === breadcrumbs.length - 1;
|
|
4740
|
-
return /* @__PURE__ */ jsxs(
|
|
4865
|
+
return /* @__PURE__ */ jsxs(React20.Fragment, { children: [
|
|
4741
4866
|
i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
|
|
4742
4867
|
isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
|
|
4743
4868
|
href: crumb.href,
|
|
@@ -4773,7 +4898,7 @@ function PageHeader({
|
|
|
4773
4898
|
] });
|
|
4774
4899
|
}
|
|
4775
4900
|
function Suspensed({ children, fallback }) {
|
|
4776
|
-
return /* @__PURE__ */ jsx(
|
|
4901
|
+
return /* @__PURE__ */ jsx(React20.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
|
|
4777
4902
|
}
|
|
4778
4903
|
function getInitialTheme() {
|
|
4779
4904
|
if (typeof document === "undefined") return "dark";
|
|
@@ -4781,7 +4906,7 @@ function getInitialTheme() {
|
|
|
4781
4906
|
return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
|
|
4782
4907
|
}
|
|
4783
4908
|
function ThemeSwitchMinimal() {
|
|
4784
|
-
const [theme, setThemeState] =
|
|
4909
|
+
const [theme, setThemeState] = React20.useState(getInitialTheme);
|
|
4785
4910
|
const toggle = () => {
|
|
4786
4911
|
const next = theme === "dark" ? "light" : "dark";
|
|
4787
4912
|
setThemeState(next);
|
|
@@ -4907,9 +5032,9 @@ function getInitialColorName() {
|
|
|
4907
5032
|
return localStorage.getItem(STORAGE_KEY) ?? "emerald";
|
|
4908
5033
|
}
|
|
4909
5034
|
function BrandColorSwitchMinimal() {
|
|
4910
|
-
const [active, setActive] =
|
|
4911
|
-
const [open, setOpen] =
|
|
4912
|
-
|
|
5035
|
+
const [active, setActive] = React20.useState(getInitialColorName);
|
|
5036
|
+
const [open, setOpen] = React20.useState(false);
|
|
5037
|
+
React20.useEffect(() => {
|
|
4913
5038
|
const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
|
|
4914
5039
|
const color = BRAND_COLORS.find((c) => c.name === saved);
|
|
4915
5040
|
if (color) applyColor(color);
|
|
@@ -4970,7 +5095,7 @@ function BrandColorSwitchMinimal() {
|
|
|
4970
5095
|
] });
|
|
4971
5096
|
}
|
|
4972
5097
|
function ThemeColorUpdater({ colorTheme }) {
|
|
4973
|
-
|
|
5098
|
+
React20.useEffect(() => {
|
|
4974
5099
|
if (colorTheme) {
|
|
4975
5100
|
document.documentElement.setAttribute("data-theme", colorTheme);
|
|
4976
5101
|
} else {
|
|
@@ -4980,7 +5105,7 @@ function ThemeColorUpdater({ colorTheme }) {
|
|
|
4980
5105
|
return null;
|
|
4981
5106
|
}
|
|
4982
5107
|
function VersionBanner({ version, message, className, onDismiss }) {
|
|
4983
|
-
const [dismissed, setDismissed] =
|
|
5108
|
+
const [dismissed, setDismissed] = React20.useState(false);
|
|
4984
5109
|
if (dismissed) return null;
|
|
4985
5110
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
|
|
4986
5111
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -5237,29 +5362,29 @@ function useMounted() {
|
|
|
5237
5362
|
return mounted;
|
|
5238
5363
|
}
|
|
5239
5364
|
function useConfirm() {
|
|
5240
|
-
const [isOpen, setIsOpen] =
|
|
5241
|
-
const [options, setOptions] =
|
|
5242
|
-
const resolveRef =
|
|
5243
|
-
const confirm =
|
|
5365
|
+
const [isOpen, setIsOpen] = React20.useState(false);
|
|
5366
|
+
const [options, setOptions] = React20.useState({});
|
|
5367
|
+
const resolveRef = React20.useRef(null);
|
|
5368
|
+
const confirm = React20.useCallback((opts = {}) => {
|
|
5244
5369
|
setOptions(opts);
|
|
5245
5370
|
setIsOpen(true);
|
|
5246
5371
|
return new Promise((resolve) => {
|
|
5247
5372
|
resolveRef.current = resolve;
|
|
5248
5373
|
});
|
|
5249
5374
|
}, []);
|
|
5250
|
-
const handleConfirm =
|
|
5375
|
+
const handleConfirm = React20.useCallback(() => {
|
|
5251
5376
|
resolveRef.current?.(true);
|
|
5252
5377
|
setIsOpen(false);
|
|
5253
5378
|
}, []);
|
|
5254
|
-
const handleCancel =
|
|
5379
|
+
const handleCancel = React20.useCallback(() => {
|
|
5255
5380
|
resolveRef.current?.(false);
|
|
5256
5381
|
setIsOpen(false);
|
|
5257
5382
|
}, []);
|
|
5258
5383
|
return { confirm, isOpen, options, handleConfirm, handleCancel };
|
|
5259
5384
|
}
|
|
5260
5385
|
function useScrolledPast(threshold) {
|
|
5261
|
-
const [scrolledPast, setScrolledPast] =
|
|
5262
|
-
|
|
5386
|
+
const [scrolledPast, setScrolledPast] = React20.useState(false);
|
|
5387
|
+
React20.useEffect(() => {
|
|
5263
5388
|
const handleScroll = () => {
|
|
5264
5389
|
setScrolledPast(window.scrollY > threshold);
|
|
5265
5390
|
};
|
|
@@ -5270,28 +5395,28 @@ function useScrolledPast(threshold) {
|
|
|
5270
5395
|
return scrolledPast;
|
|
5271
5396
|
}
|
|
5272
5397
|
function useCopyToClipboard() {
|
|
5273
|
-
const [copied, setCopied] =
|
|
5274
|
-
const timeoutRef =
|
|
5275
|
-
const copy =
|
|
5398
|
+
const [copied, setCopied] = React20.useState(false);
|
|
5399
|
+
const timeoutRef = React20.useRef(void 0);
|
|
5400
|
+
const copy = React20.useCallback((text) => {
|
|
5276
5401
|
navigator.clipboard.writeText(text).then(() => {
|
|
5277
5402
|
setCopied(true);
|
|
5278
5403
|
clearTimeout(timeoutRef.current);
|
|
5279
5404
|
timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
5280
5405
|
});
|
|
5281
5406
|
}, []);
|
|
5282
|
-
|
|
5407
|
+
React20.useEffect(() => () => clearTimeout(timeoutRef.current), []);
|
|
5283
5408
|
return [copied, copy];
|
|
5284
5409
|
}
|
|
5285
5410
|
function useDebounce(value, delay) {
|
|
5286
|
-
const [debounced, setDebounced] =
|
|
5287
|
-
|
|
5411
|
+
const [debounced, setDebounced] = React20.useState(value);
|
|
5412
|
+
React20.useEffect(() => {
|
|
5288
5413
|
const timer = setTimeout(() => setDebounced(value), delay);
|
|
5289
5414
|
return () => clearTimeout(timer);
|
|
5290
5415
|
}, [value, delay]);
|
|
5291
5416
|
return debounced;
|
|
5292
5417
|
}
|
|
5293
5418
|
function useLocalStorage(key, initialValue) {
|
|
5294
|
-
const [storedValue, setStoredValue] =
|
|
5419
|
+
const [storedValue, setStoredValue] = React20.useState(() => {
|
|
5295
5420
|
if (typeof window === "undefined") return initialValue;
|
|
5296
5421
|
try {
|
|
5297
5422
|
const item = window.localStorage.getItem(key);
|
|
@@ -5300,7 +5425,7 @@ function useLocalStorage(key, initialValue) {
|
|
|
5300
5425
|
return initialValue;
|
|
5301
5426
|
}
|
|
5302
5427
|
});
|
|
5303
|
-
const setValue =
|
|
5428
|
+
const setValue = React20.useCallback(
|
|
5304
5429
|
(value) => {
|
|
5305
5430
|
try {
|
|
5306
5431
|
setStoredValue(value);
|
|
@@ -5322,11 +5447,11 @@ function useViewMode(key = "view-mode", fallback = "cards") {
|
|
|
5322
5447
|
return { view: mounted ? stored : fallback, setView: setStored, ready: mounted };
|
|
5323
5448
|
}
|
|
5324
5449
|
function useMediaQuery(query) {
|
|
5325
|
-
const [matches, setMatches] =
|
|
5450
|
+
const [matches, setMatches] = React20.useState(() => {
|
|
5326
5451
|
if (typeof window === "undefined") return false;
|
|
5327
5452
|
return window.matchMedia(query).matches;
|
|
5328
5453
|
});
|
|
5329
|
-
|
|
5454
|
+
React20.useEffect(() => {
|
|
5330
5455
|
if (typeof window === "undefined") return;
|
|
5331
5456
|
const mql = window.matchMedia(query);
|
|
5332
5457
|
setMatches(mql.matches);
|
|
@@ -5337,18 +5462,18 @@ function useMediaQuery(query) {
|
|
|
5337
5462
|
return matches;
|
|
5338
5463
|
}
|
|
5339
5464
|
function usePagination(total, pageSize) {
|
|
5340
|
-
const [page, setPage] =
|
|
5465
|
+
const [page, setPage] = React20.useState(1);
|
|
5341
5466
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
5342
5467
|
const offset = (page - 1) * pageSize;
|
|
5343
|
-
const goTo =
|
|
5468
|
+
const goTo = React20.useCallback(
|
|
5344
5469
|
(p) => setPage(Math.min(Math.max(1, p), totalPages)),
|
|
5345
5470
|
[totalPages]
|
|
5346
5471
|
);
|
|
5347
|
-
const next =
|
|
5348
|
-
const prev =
|
|
5472
|
+
const next = React20.useCallback(() => goTo(page + 1), [page, goTo]);
|
|
5473
|
+
const prev = React20.useCallback(() => goTo(page - 1), [page, goTo]);
|
|
5349
5474
|
return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
|
|
5350
5475
|
}
|
|
5351
5476
|
|
|
5352
|
-
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, DataView, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, MediaCard, 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, UpdateBanner, UpgradePrompt, VersionBanner, ViewToggle, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode, useViewMode };
|
|
5477
|
+
export { ActivityFeed, AnimatedPill, AnimatedSwap, 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, DataView, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, MediaCard, 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, UpdateBanner, UpgradePrompt, VersionBanner, ViewToggle, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode, useViewMode };
|
|
5353
5478
|
//# sourceMappingURL=index.js.map
|
|
5354
5479
|
//# sourceMappingURL=index.js.map
|