@olwiba/ui 0.1.1 → 0.1.3
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 +25 -4
- package/dist/index.js +364 -129
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/FeatureCard.tsx +32 -7
- package/src/components/ModeSwitchMinimal.tsx +40 -0
- package/src/components/PricingCard.tsx +29 -5
- package/src/components/TestimonialCard.tsx +30 -6
- package/src/context/OlwibaUIContext.tsx +5 -2
- package/src/index.ts +2 -0
- package/src/marketing/CarouselSection.tsx +126 -0
- package/src/marketing/CtaSection.tsx +10 -3
- package/src/marketing/FaqSection.tsx +9 -2
- package/src/marketing/FeaturesSection.tsx +9 -1
- package/src/marketing/Navbar.tsx +30 -20
- package/src/marketing/PricingSection.tsx +17 -6
- package/src/marketing/StatsSection.tsx +9 -2
- package/src/marketing/TestimonialsSection.tsx +9 -1
- package/src/motion/StaggerChildren.tsx +1 -0
package/dist/index.js
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
import { cn } from './chunk-AMWLSHD6.js';
|
|
2
2
|
export { MdxContent, cn } from './chunk-AMWLSHD6.js';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React26 from 'react';
|
|
4
4
|
import { useState, useEffect } from 'react';
|
|
5
|
-
import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from '@olwiba/cn';
|
|
5
|
+
import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Spinner, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from '@olwiba/cn';
|
|
6
6
|
export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@olwiba/cn';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
-
import { Compass, ArrowLeft, X, ArrowRight, Sparkles, Check, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, Phone, MapPin, MessageSquare, Send,
|
|
8
|
+
import { Compass, ArrowLeft, X, ArrowRight, ChevronLeft, ChevronRight, Sparkles, Check, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, Phone, MapPin, MessageSquare, Send, Menu, Search, TrendingUp, TrendingDown, Sun, Moon, AlertTriangle, Loader2, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
|
|
9
9
|
|
|
10
|
-
var OlwibaUIContext =
|
|
10
|
+
var OlwibaUIContext = React26.createContext({
|
|
11
11
|
isMobile: false,
|
|
12
|
-
mode: "default"
|
|
12
|
+
mode: "default",
|
|
13
|
+
setMode: () => {
|
|
14
|
+
}
|
|
13
15
|
});
|
|
14
|
-
function OlwibaUIProvider({ children, isMobile: isMobileProp, mode = "default" }) {
|
|
16
|
+
function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
|
|
15
17
|
const detectedMobile = useIsMobile();
|
|
16
18
|
const isMobile = isMobileProp ?? detectedMobile;
|
|
19
|
+
const [mode, setMode] = React26.useState(initialMode);
|
|
17
20
|
const variant = mode !== "default" ? mode : void 0;
|
|
18
|
-
return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
|
|
21
|
+
return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
|
|
19
22
|
}
|
|
20
23
|
function useOlwibaUI() {
|
|
21
|
-
return
|
|
24
|
+
return React26.useContext(OlwibaUIContext);
|
|
22
25
|
}
|
|
23
26
|
function useUIMode() {
|
|
24
|
-
return
|
|
27
|
+
return React26.useContext(OlwibaUIContext).mode;
|
|
25
28
|
}
|
|
26
|
-
var Button =
|
|
29
|
+
var Button = React26.forwardRef(
|
|
27
30
|
({ mode, ...props }, ref) => {
|
|
28
31
|
const ctxMode = useUIMode();
|
|
29
32
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -31,7 +34,7 @@ var Button = React25.forwardRef(
|
|
|
31
34
|
}
|
|
32
35
|
);
|
|
33
36
|
Button.displayName = "Button";
|
|
34
|
-
var Card =
|
|
37
|
+
var Card = React26.forwardRef(
|
|
35
38
|
({ mode, ...props }, ref) => {
|
|
36
39
|
const ctxMode = useUIMode();
|
|
37
40
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -42,7 +45,7 @@ Card.displayName = "Card";
|
|
|
42
45
|
function Badge(props) {
|
|
43
46
|
return /* @__PURE__ */ jsx(Badge$1, { ...props });
|
|
44
47
|
}
|
|
45
|
-
var Input =
|
|
48
|
+
var Input = React26.forwardRef(
|
|
46
49
|
({ mode, ...props }, ref) => {
|
|
47
50
|
const ctxMode = useUIMode();
|
|
48
51
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -50,7 +53,7 @@ var Input = React25.forwardRef(
|
|
|
50
53
|
}
|
|
51
54
|
);
|
|
52
55
|
Input.displayName = "Input";
|
|
53
|
-
var Textarea =
|
|
56
|
+
var Textarea = React26.forwardRef(
|
|
54
57
|
({ mode, ...props }, ref) => {
|
|
55
58
|
const ctxMode = useUIMode();
|
|
56
59
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
@@ -58,13 +61,13 @@ var Textarea = React25.forwardRef(
|
|
|
58
61
|
}
|
|
59
62
|
);
|
|
60
63
|
Textarea.displayName = "Textarea";
|
|
61
|
-
var Checkbox =
|
|
64
|
+
var Checkbox = React26.forwardRef(({ mode, ...props }, ref) => {
|
|
62
65
|
const ctxMode = useUIMode();
|
|
63
66
|
const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
|
|
64
67
|
return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
|
|
65
68
|
});
|
|
66
69
|
Checkbox.displayName = "Checkbox";
|
|
67
|
-
var Switch =
|
|
70
|
+
var Switch = React26.forwardRef(({ mode: _mode, ...props }, ref) => {
|
|
68
71
|
return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
|
|
69
72
|
});
|
|
70
73
|
Switch.displayName = "Switch";
|
|
@@ -754,9 +757,9 @@ function FadeIn({
|
|
|
754
757
|
style,
|
|
755
758
|
...props
|
|
756
759
|
}) {
|
|
757
|
-
const ref =
|
|
758
|
-
const [visible, setVisible] =
|
|
759
|
-
|
|
760
|
+
const ref = React26.useRef(null);
|
|
761
|
+
const [visible, setVisible] = React26.useState(false);
|
|
762
|
+
React26.useEffect(() => {
|
|
760
763
|
const el = ref.current;
|
|
761
764
|
if (!el) return;
|
|
762
765
|
const observer = new IntersectionObserver(
|
|
@@ -889,17 +892,28 @@ function HeroSection({
|
|
|
889
892
|
] }) });
|
|
890
893
|
}
|
|
891
894
|
function FeatureCard({ icon: Icon, title, description, href, className, ...props }) {
|
|
892
|
-
const
|
|
895
|
+
const mode = useUIVariant();
|
|
896
|
+
const cardInner = /* @__PURE__ */ jsxs(
|
|
893
897
|
"div",
|
|
894
898
|
{
|
|
895
899
|
className: cn$1(
|
|
896
|
-
"group relative flex flex-col gap-4
|
|
900
|
+
"group relative flex h-full flex-col gap-4 border bg-card p-6 transition-all duration-200",
|
|
901
|
+
mode === "playful" ? "rounded-2xl rotate-[0.3deg]" : mode === "smooth" ? "rounded-3xl hover:-translate-y-0.5 hover:shadow-md" : "rounded-2xl hover:-translate-y-0.5 hover:shadow-md",
|
|
897
902
|
href && "cursor-pointer",
|
|
898
|
-
className
|
|
903
|
+
mode !== "playful" && className
|
|
899
904
|
),
|
|
900
|
-
...props,
|
|
905
|
+
...mode !== "playful" ? props : {},
|
|
901
906
|
children: [
|
|
902
|
-
/* @__PURE__ */ jsx(
|
|
907
|
+
/* @__PURE__ */ jsx(
|
|
908
|
+
"div",
|
|
909
|
+
{
|
|
910
|
+
className: cn$1(
|
|
911
|
+
"flex h-10 w-10 items-center justify-center bg-primary/10 text-primary transition-colors group-hover:bg-primary group-hover:text-primary-foreground",
|
|
912
|
+
mode === "smooth" ? "rounded-2xl" : "rounded-xl"
|
|
913
|
+
),
|
|
914
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "size-5" })
|
|
915
|
+
}
|
|
916
|
+
),
|
|
903
917
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
904
918
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
905
919
|
/* @__PURE__ */ jsx("h3", { className: "font-semibold leading-tight", children: title }),
|
|
@@ -910,8 +924,18 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
|
|
|
910
924
|
]
|
|
911
925
|
}
|
|
912
926
|
);
|
|
927
|
+
const content = mode === "playful" ? /* @__PURE__ */ jsxs("div", { className: cn$1("group/playful relative h-full", className), ...props, children: [
|
|
928
|
+
/* @__PURE__ */ jsx(
|
|
929
|
+
"div",
|
|
930
|
+
{
|
|
931
|
+
"aria-hidden": "true",
|
|
932
|
+
className: "absolute inset-0 rounded-2xl bg-border transition-transform duration-200 translate-x-[5px] translate-y-[5px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:translate-x-[6px] group-hover/playful:translate-y-[6px]"
|
|
933
|
+
}
|
|
934
|
+
),
|
|
935
|
+
cardInner
|
|
936
|
+
] }) : cardInner;
|
|
913
937
|
if (href) {
|
|
914
|
-
return /* @__PURE__ */ jsx("a", { href, className: "block", children: content });
|
|
938
|
+
return /* @__PURE__ */ jsx("a", { href, className: "block h-full", children: content });
|
|
915
939
|
}
|
|
916
940
|
return content;
|
|
917
941
|
}
|
|
@@ -932,9 +956,9 @@ function StaggerChildren({
|
|
|
932
956
|
className,
|
|
933
957
|
...props
|
|
934
958
|
}) {
|
|
935
|
-
const ref =
|
|
936
|
-
const [visible, setVisible] =
|
|
937
|
-
|
|
959
|
+
const ref = React26.useRef(null);
|
|
960
|
+
const [visible, setVisible] = React26.useState(false);
|
|
961
|
+
React26.useEffect(() => {
|
|
938
962
|
const el = ref.current;
|
|
939
963
|
if (!el) return;
|
|
940
964
|
const observer = new IntersectionObserver(
|
|
@@ -952,9 +976,10 @@ function StaggerChildren({
|
|
|
952
976
|
return () => observer.disconnect();
|
|
953
977
|
}, [once]);
|
|
954
978
|
const [tx, ty] = translateMap2[direction];
|
|
955
|
-
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children:
|
|
979
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React26.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
|
|
956
980
|
"div",
|
|
957
981
|
{
|
|
982
|
+
className: "h-full",
|
|
958
983
|
style: {
|
|
959
984
|
transition: `opacity ${duration}ms ease, transform ${duration}ms ease`,
|
|
960
985
|
transitionDelay: `${delay + i * stagger}ms`,
|
|
@@ -971,7 +996,14 @@ function FeaturesSection({
|
|
|
971
996
|
badge = "Features",
|
|
972
997
|
features
|
|
973
998
|
}) {
|
|
974
|
-
|
|
999
|
+
const mode = useUIVariant();
|
|
1000
|
+
const sectionClasses = cn$1(
|
|
1001
|
+
"overflow-hidden bg-card",
|
|
1002
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1003
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1004
|
+
!mode && "rounded-2xl border"
|
|
1005
|
+
);
|
|
1006
|
+
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-4xl", children: [
|
|
975
1007
|
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
976
1008
|
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: features.map((feature) => /* @__PURE__ */ jsx(
|
|
977
1009
|
FeatureCard,
|
|
@@ -985,6 +1017,106 @@ function FeaturesSection({
|
|
|
985
1017
|
)) })
|
|
986
1018
|
] }) }) });
|
|
987
1019
|
}
|
|
1020
|
+
function CarouselSection({
|
|
1021
|
+
title = "Explore the platform",
|
|
1022
|
+
description,
|
|
1023
|
+
badge = "Features",
|
|
1024
|
+
features
|
|
1025
|
+
}) {
|
|
1026
|
+
const mode = useUIVariant();
|
|
1027
|
+
const trackRef = React26.useRef(null);
|
|
1028
|
+
const [canPrev, setCanPrev] = React26.useState(false);
|
|
1029
|
+
const [canNext, setCanNext] = React26.useState(true);
|
|
1030
|
+
const updateScrollState = React26.useCallback(() => {
|
|
1031
|
+
const track = trackRef.current;
|
|
1032
|
+
if (!track) return;
|
|
1033
|
+
setCanPrev(track.scrollLeft > 8);
|
|
1034
|
+
setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
|
|
1035
|
+
}, []);
|
|
1036
|
+
React26.useEffect(() => {
|
|
1037
|
+
const track = trackRef.current;
|
|
1038
|
+
if (!track) return;
|
|
1039
|
+
updateScrollState();
|
|
1040
|
+
track.addEventListener("scroll", updateScrollState, { passive: true });
|
|
1041
|
+
const observer = new ResizeObserver(updateScrollState);
|
|
1042
|
+
observer.observe(track);
|
|
1043
|
+
return () => {
|
|
1044
|
+
track.removeEventListener("scroll", updateScrollState);
|
|
1045
|
+
observer.disconnect();
|
|
1046
|
+
};
|
|
1047
|
+
}, [updateScrollState]);
|
|
1048
|
+
const scrollByCard = (direction) => {
|
|
1049
|
+
const track = trackRef.current;
|
|
1050
|
+
if (!track) return;
|
|
1051
|
+
const card = track.querySelector("[data-carousel-item]");
|
|
1052
|
+
const step = card ? card.offsetWidth + 24 : track.clientWidth * 0.8;
|
|
1053
|
+
track.scrollBy({ left: direction * step, behavior: "smooth" });
|
|
1054
|
+
};
|
|
1055
|
+
const sectionClasses = cn$1(
|
|
1056
|
+
"overflow-hidden bg-card",
|
|
1057
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1058
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1059
|
+
!mode && "rounded-2xl border"
|
|
1060
|
+
);
|
|
1061
|
+
const controlClasses = cn$1(
|
|
1062
|
+
"flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
|
|
1063
|
+
"hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
|
|
1064
|
+
mode === "smooth" ? "rounded-2xl" : "rounded-xl"
|
|
1065
|
+
);
|
|
1066
|
+
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: [
|
|
1067
|
+
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1068
|
+
/* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
|
|
1069
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-end gap-2", children: [
|
|
1070
|
+
/* @__PURE__ */ jsx(
|
|
1071
|
+
"button",
|
|
1072
|
+
{
|
|
1073
|
+
type: "button",
|
|
1074
|
+
"aria-label": "Previous",
|
|
1075
|
+
className: controlClasses,
|
|
1076
|
+
onClick: () => scrollByCard(-1),
|
|
1077
|
+
disabled: !canPrev,
|
|
1078
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
1079
|
+
}
|
|
1080
|
+
),
|
|
1081
|
+
/* @__PURE__ */ jsx(
|
|
1082
|
+
"button",
|
|
1083
|
+
{
|
|
1084
|
+
type: "button",
|
|
1085
|
+
"aria-label": "Next",
|
|
1086
|
+
className: controlClasses,
|
|
1087
|
+
onClick: () => scrollByCard(1),
|
|
1088
|
+
disabled: !canNext,
|
|
1089
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
1090
|
+
}
|
|
1091
|
+
)
|
|
1092
|
+
] }),
|
|
1093
|
+
/* @__PURE__ */ jsx(
|
|
1094
|
+
"div",
|
|
1095
|
+
{
|
|
1096
|
+
ref: trackRef,
|
|
1097
|
+
className: "mt-4 flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1098
|
+
children: features.map((feature) => /* @__PURE__ */ jsx(
|
|
1099
|
+
"div",
|
|
1100
|
+
{
|
|
1101
|
+
"data-carousel-item": true,
|
|
1102
|
+
className: "w-[280px] shrink-0 snap-start sm:w-[320px]",
|
|
1103
|
+
children: /* @__PURE__ */ jsx(
|
|
1104
|
+
FeatureCard,
|
|
1105
|
+
{
|
|
1106
|
+
icon: feature.icon,
|
|
1107
|
+
title: feature.title,
|
|
1108
|
+
description: feature.description,
|
|
1109
|
+
href: feature.href
|
|
1110
|
+
}
|
|
1111
|
+
)
|
|
1112
|
+
},
|
|
1113
|
+
feature.title
|
|
1114
|
+
))
|
|
1115
|
+
}
|
|
1116
|
+
)
|
|
1117
|
+
] })
|
|
1118
|
+
] }) }) });
|
|
1119
|
+
}
|
|
988
1120
|
var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
989
1121
|
function CtaSection({
|
|
990
1122
|
heading,
|
|
@@ -994,10 +1126,17 @@ function CtaSection({
|
|
|
994
1126
|
footnote,
|
|
995
1127
|
renderLink = defaultRenderLink5
|
|
996
1128
|
}) {
|
|
997
|
-
|
|
1129
|
+
const mode = useUIVariant();
|
|
1130
|
+
const sectionClasses = cn$1(
|
|
1131
|
+
"overflow-hidden bg-card",
|
|
1132
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1133
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1134
|
+
!mode && "rounded-2xl border"
|
|
1135
|
+
);
|
|
1136
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
|
|
998
1137
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
|
|
999
1138
|
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
|
|
1000
|
-
/* @__PURE__ */ jsx("div", { className: "mb-4 inline-flex h-10 w-10 items-center justify-center
|
|
1139
|
+
/* @__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" }) }),
|
|
1001
1140
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
|
|
1002
1141
|
/* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
|
|
1003
1142
|
/* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
|
|
@@ -1030,15 +1169,17 @@ function PricingCard({
|
|
|
1030
1169
|
className,
|
|
1031
1170
|
...props
|
|
1032
1171
|
}) {
|
|
1033
|
-
|
|
1172
|
+
const mode = useUIVariant();
|
|
1173
|
+
const cardInner = /* @__PURE__ */ jsxs(
|
|
1034
1174
|
"div",
|
|
1035
1175
|
{
|
|
1036
1176
|
className: cn$1(
|
|
1037
|
-
"relative flex flex-col
|
|
1177
|
+
"relative flex flex-col border p-6",
|
|
1178
|
+
mode === "playful" ? "rounded-2xl rotate-[0.3deg]" : mode === "smooth" ? "rounded-3xl" : "rounded-2xl",
|
|
1038
1179
|
highlighted ? "border-primary bg-primary/5 shadow-sm" : "bg-card",
|
|
1039
|
-
className
|
|
1180
|
+
mode !== "playful" && className
|
|
1040
1181
|
),
|
|
1041
|
-
...props,
|
|
1182
|
+
...mode !== "playful" ? props : {},
|
|
1042
1183
|
children: [
|
|
1043
1184
|
badge && /* @__PURE__ */ jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx(Badge$1, { children: badge }) }),
|
|
1044
1185
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
@@ -1069,6 +1210,22 @@ function PricingCard({
|
|
|
1069
1210
|
]
|
|
1070
1211
|
}
|
|
1071
1212
|
);
|
|
1213
|
+
if (mode === "playful") {
|
|
1214
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("group/playful relative", className), ...props, children: [
|
|
1215
|
+
/* @__PURE__ */ jsx(
|
|
1216
|
+
"div",
|
|
1217
|
+
{
|
|
1218
|
+
"aria-hidden": "true",
|
|
1219
|
+
className: cn$1(
|
|
1220
|
+
"absolute inset-0 rounded-2xl transition-transform duration-200 translate-x-[5px] translate-y-[5px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:translate-x-[6px] group-hover/playful:translate-y-[6px]",
|
|
1221
|
+
highlighted ? "bg-primary/20" : "bg-border"
|
|
1222
|
+
)
|
|
1223
|
+
}
|
|
1224
|
+
),
|
|
1225
|
+
cardInner
|
|
1226
|
+
] });
|
|
1227
|
+
}
|
|
1228
|
+
return cardInner;
|
|
1072
1229
|
}
|
|
1073
1230
|
var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1074
1231
|
function PricingSection({
|
|
@@ -1081,16 +1238,25 @@ function PricingSection({
|
|
|
1081
1238
|
renderLink = defaultRenderLink6,
|
|
1082
1239
|
footnote
|
|
1083
1240
|
}) {
|
|
1084
|
-
const [annual, setAnnual] =
|
|
1085
|
-
|
|
1241
|
+
const [annual, setAnnual] = React26.useState(false);
|
|
1242
|
+
const mode = useUIVariant();
|
|
1243
|
+
const sectionClasses = cn$1(
|
|
1244
|
+
"overflow-hidden bg-card",
|
|
1245
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1246
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1247
|
+
!mode && "rounded-2xl border"
|
|
1248
|
+
);
|
|
1249
|
+
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: [
|
|
1086
1250
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1087
1251
|
badge && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
|
|
1088
1252
|
/* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
|
|
1089
1253
|
description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description }),
|
|
1090
1254
|
/* @__PURE__ */ jsxs("div", { className: "mt-6 inline-flex items-center gap-3 rounded-full border bg-muted p-1", children: [
|
|
1091
1255
|
/* @__PURE__ */ jsx(
|
|
1092
|
-
|
|
1256
|
+
Button$1,
|
|
1093
1257
|
{
|
|
1258
|
+
variant: "ghost",
|
|
1259
|
+
size: "sm",
|
|
1094
1260
|
onClick: () => setAnnual(false),
|
|
1095
1261
|
className: cn$1(
|
|
1096
1262
|
"rounded-full px-4 py-1.5 text-sm font-medium transition-colors",
|
|
@@ -1100,8 +1266,10 @@ function PricingSection({
|
|
|
1100
1266
|
}
|
|
1101
1267
|
),
|
|
1102
1268
|
/* @__PURE__ */ jsxs(
|
|
1103
|
-
|
|
1269
|
+
Button$1,
|
|
1104
1270
|
{
|
|
1271
|
+
variant: "ghost",
|
|
1272
|
+
size: "sm",
|
|
1105
1273
|
onClick: () => setAnnual(true),
|
|
1106
1274
|
className: cn$1(
|
|
1107
1275
|
"flex items-center gap-2 rounded-full px-4 py-1.5 text-sm font-medium transition-colors",
|
|
@@ -1146,11 +1314,16 @@ function TestimonialCard({
|
|
|
1146
1314
|
className,
|
|
1147
1315
|
...props
|
|
1148
1316
|
}) {
|
|
1149
|
-
|
|
1317
|
+
const mode = useUIVariant();
|
|
1318
|
+
const cardInner = /* @__PURE__ */ jsxs(
|
|
1150
1319
|
"div",
|
|
1151
1320
|
{
|
|
1152
|
-
className: cn$1(
|
|
1153
|
-
|
|
1321
|
+
className: cn$1(
|
|
1322
|
+
"flex flex-col gap-4 border bg-card p-6",
|
|
1323
|
+
mode === "playful" ? "rounded-2xl rotate-[0.3deg]" : mode === "smooth" ? "rounded-3xl" : "rounded-2xl",
|
|
1324
|
+
mode !== "playful" && className
|
|
1325
|
+
),
|
|
1326
|
+
...mode !== "playful" ? props : {},
|
|
1154
1327
|
children: [
|
|
1155
1328
|
/* @__PURE__ */ jsx(Quote, { className: "size-4 text-muted-foreground/40" }),
|
|
1156
1329
|
rating != null && /* @__PURE__ */ jsx("div", { className: "flex gap-0.5", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx(
|
|
@@ -1165,9 +1338,9 @@ function TestimonialCard({
|
|
|
1165
1338
|
)) }),
|
|
1166
1339
|
/* @__PURE__ */ jsx("p", { className: "flex-1 text-sm leading-relaxed", children: quote }),
|
|
1167
1340
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1168
|
-
/* @__PURE__ */ jsxs(Avatar, { className: "size-9
|
|
1341
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-9", children: [
|
|
1169
1342
|
/* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: name }),
|
|
1170
|
-
/* @__PURE__ */ jsx(AvatarFallback, { className: "
|
|
1343
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: initials ?? name.slice(0, 2).toUpperCase() })
|
|
1171
1344
|
] }),
|
|
1172
1345
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1173
1346
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium leading-tight", children: name }),
|
|
@@ -1180,6 +1353,19 @@ function TestimonialCard({
|
|
|
1180
1353
|
]
|
|
1181
1354
|
}
|
|
1182
1355
|
);
|
|
1356
|
+
if (mode === "playful") {
|
|
1357
|
+
return /* @__PURE__ */ jsxs("div", { className: cn$1("group/playful relative", className), ...props, children: [
|
|
1358
|
+
/* @__PURE__ */ jsx(
|
|
1359
|
+
"div",
|
|
1360
|
+
{
|
|
1361
|
+
"aria-hidden": "true",
|
|
1362
|
+
className: "absolute inset-0 rounded-2xl bg-border transition-transform duration-200 translate-x-[5px] translate-y-[5px] -rotate-[0.5deg] group-hover/playful:-rotate-[1.5deg] group-hover/playful:translate-x-[6px] group-hover/playful:translate-y-[6px]"
|
|
1363
|
+
}
|
|
1364
|
+
),
|
|
1365
|
+
cardInner
|
|
1366
|
+
] });
|
|
1367
|
+
}
|
|
1368
|
+
return cardInner;
|
|
1183
1369
|
}
|
|
1184
1370
|
function TestimonialsSection({
|
|
1185
1371
|
title = "Trusted by teams shipping real products",
|
|
@@ -1187,7 +1373,14 @@ function TestimonialsSection({
|
|
|
1187
1373
|
badge = "Testimonials",
|
|
1188
1374
|
testimonials
|
|
1189
1375
|
}) {
|
|
1190
|
-
|
|
1376
|
+
const mode = useUIVariant();
|
|
1377
|
+
const sectionClasses = cn$1(
|
|
1378
|
+
"overflow-hidden bg-card",
|
|
1379
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1380
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1381
|
+
!mode && "rounded-2xl border"
|
|
1382
|
+
);
|
|
1383
|
+
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: [
|
|
1191
1384
|
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1192
1385
|
/* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 columns-1 gap-4 sm:columns-2 lg:columns-3", children: testimonials.map((t) => /* @__PURE__ */ jsx(
|
|
1193
1386
|
"div",
|
|
@@ -1293,7 +1486,14 @@ function FaqSection({
|
|
|
1293
1486
|
badge = "FAQ",
|
|
1294
1487
|
items
|
|
1295
1488
|
}) {
|
|
1296
|
-
|
|
1489
|
+
const mode = useUIVariant();
|
|
1490
|
+
const sectionClasses = cn$1(
|
|
1491
|
+
"overflow-hidden bg-card",
|
|
1492
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1493
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1494
|
+
!mode && "rounded-2xl border"
|
|
1495
|
+
);
|
|
1496
|
+
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-3xl", children: [
|
|
1297
1497
|
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
1298
1498
|
/* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "mt-10 w-full", children: items.map((item, i) => /* @__PURE__ */ jsxs(AccordionItem, { value: `item-${i}`, children: [
|
|
1299
1499
|
/* @__PURE__ */ jsx(AccordionTrigger, { className: "text-left text-sm font-medium", children: item.question }),
|
|
@@ -1312,10 +1512,10 @@ function CountUp({
|
|
|
1312
1512
|
className,
|
|
1313
1513
|
...props
|
|
1314
1514
|
}) {
|
|
1315
|
-
const ref =
|
|
1316
|
-
const [value, setValue] =
|
|
1317
|
-
const hasStarted =
|
|
1318
|
-
|
|
1515
|
+
const ref = React26.useRef(null);
|
|
1516
|
+
const [value, setValue] = React26.useState(from);
|
|
1517
|
+
const hasStarted = React26.useRef(false);
|
|
1518
|
+
React26.useEffect(() => {
|
|
1319
1519
|
const el = ref.current;
|
|
1320
1520
|
if (!el) return;
|
|
1321
1521
|
const observer = new IntersectionObserver(
|
|
@@ -1363,7 +1563,14 @@ function StatsSection({
|
|
|
1363
1563
|
badge = "By the numbers",
|
|
1364
1564
|
stats
|
|
1365
1565
|
}) {
|
|
1366
|
-
|
|
1566
|
+
const mode = useUIVariant();
|
|
1567
|
+
const sectionClasses = cn$1(
|
|
1568
|
+
"overflow-hidden bg-card",
|
|
1569
|
+
mode === "smooth" && "rounded-3xl border",
|
|
1570
|
+
mode === "playful" && "rounded-2xl border-primary/25 border",
|
|
1571
|
+
!mode && "rounded-2xl border"
|
|
1572
|
+
);
|
|
1573
|
+
return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
|
|
1367
1574
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,hsl(var(--primary)/0.08),transparent_60%)]" }),
|
|
1368
1575
|
/* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-4xl", children: [
|
|
1369
1576
|
/* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
|
|
@@ -1401,8 +1608,8 @@ function NewsletterSection({
|
|
|
1401
1608
|
successDescription = defaults.successDescription,
|
|
1402
1609
|
onSubscribe
|
|
1403
1610
|
} = {}) {
|
|
1404
|
-
const [email, setEmail] =
|
|
1405
|
-
const [submitted, setSubmitted] =
|
|
1611
|
+
const [email, setEmail] = React26.useState("");
|
|
1612
|
+
const [submitted, setSubmitted] = React26.useState(false);
|
|
1406
1613
|
async function handleSubmit(e) {
|
|
1407
1614
|
e.preventDefault();
|
|
1408
1615
|
if (!email) return;
|
|
@@ -1480,7 +1687,7 @@ function ContactSection({
|
|
|
1480
1687
|
sendAnotherLabel = defaults2.sendAnotherLabel,
|
|
1481
1688
|
onSubmit
|
|
1482
1689
|
} = {}) {
|
|
1483
|
-
const [submitted, setSubmitted] =
|
|
1690
|
+
const [submitted, setSubmitted] = React26.useState(false);
|
|
1484
1691
|
async function handleSubmit(e) {
|
|
1485
1692
|
e.preventDefault();
|
|
1486
1693
|
await onSubmit?.(e);
|
|
@@ -1569,38 +1776,17 @@ function ContactSection({
|
|
|
1569
1776
|
] }) })
|
|
1570
1777
|
] }) });
|
|
1571
1778
|
}
|
|
1572
|
-
function ThemeSwitchMinimal() {
|
|
1573
|
-
const [theme, setThemeState] = React25.useState("dark");
|
|
1574
|
-
React25.useEffect(() => {
|
|
1575
|
-
const stored = localStorage.getItem("theme");
|
|
1576
|
-
const resolved = stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
|
|
1577
|
-
setThemeState(resolved);
|
|
1578
|
-
}, []);
|
|
1579
|
-
const toggle = () => {
|
|
1580
|
-
const next = theme === "dark" ? "light" : "dark";
|
|
1581
|
-
setThemeState(next);
|
|
1582
|
-
localStorage.setItem("theme", next);
|
|
1583
|
-
document.documentElement.classList.toggle("dark", next === "dark");
|
|
1584
|
-
};
|
|
1585
|
-
return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: toggle, className: "size-8", "aria-label": "Toggle theme", children: /* @__PURE__ */ jsx(
|
|
1586
|
-
"span",
|
|
1587
|
-
{
|
|
1588
|
-
className: "inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 spin-in-90 duration-200 motion-reduce:animate-none",
|
|
1589
|
-
children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "size-4" }) : /* @__PURE__ */ jsx(Moon, { className: "size-4" })
|
|
1590
|
-
},
|
|
1591
|
-
theme
|
|
1592
|
-
) });
|
|
1593
|
-
}
|
|
1594
1779
|
var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1595
1780
|
function Navbar({
|
|
1596
1781
|
brand,
|
|
1597
1782
|
navLinks,
|
|
1598
1783
|
cta,
|
|
1599
|
-
|
|
1784
|
+
controls,
|
|
1600
1785
|
renderLink = defaultRenderLink7
|
|
1601
1786
|
}) {
|
|
1602
|
-
const [open, setOpen] =
|
|
1787
|
+
const [open, setOpen] = React26.useState(false);
|
|
1603
1788
|
const brandHref = brand.href ?? "/";
|
|
1789
|
+
const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
|
|
1604
1790
|
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: [
|
|
1605
1791
|
renderLink({
|
|
1606
1792
|
href: brandHref,
|
|
@@ -1609,12 +1795,14 @@ function Navbar({
|
|
|
1609
1795
|
/* @__PURE__ */ jsx("span", { children: brand.name })
|
|
1610
1796
|
] })
|
|
1611
1797
|
}),
|
|
1612
|
-
/* @__PURE__ */ jsx("div", { className:
|
|
1798
|
+
/* @__PURE__ */ jsx("div", { className: `hidden items-center gap-1 md:flex ${!hasRightContent ? "col-span-2 justify-end" : ""}`, children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { children: renderLink({
|
|
1613
1799
|
href: link.href,
|
|
1614
1800
|
className: "rounded-md px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
1615
1801
|
children: link.label
|
|
1616
1802
|
}) }, link.label)) }),
|
|
1617
|
-
/* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-
|
|
1803
|
+
hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
|
|
1804
|
+
controls?.map((control, i) => /* @__PURE__ */ jsx(React26.Fragment, { children: control }, i)),
|
|
1805
|
+
(cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
|
|
1618
1806
|
cta?.secondary && renderLink({
|
|
1619
1807
|
href: cta.secondary.href,
|
|
1620
1808
|
children: /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "sm", children: cta.secondary.label })
|
|
@@ -1622,8 +1810,7 @@ function Navbar({
|
|
|
1622
1810
|
cta?.primary && renderLink({
|
|
1623
1811
|
href: cta.primary.href,
|
|
1624
1812
|
children: /* @__PURE__ */ jsx(Button$1, { size: "sm", children: cta.primary.label })
|
|
1625
|
-
})
|
|
1626
|
-
showThemeToggle && /* @__PURE__ */ jsx(ThemeSwitchMinimal, {})
|
|
1813
|
+
})
|
|
1627
1814
|
] }),
|
|
1628
1815
|
/* @__PURE__ */ jsxs(Sheet, { open, onOpenChange: setOpen, children: [
|
|
1629
1816
|
/* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button$1, { variant: "ghost", size: "icon", className: "justify-self-end md:hidden", children: [
|
|
@@ -1640,7 +1827,7 @@ function Navbar({
|
|
|
1640
1827
|
] })
|
|
1641
1828
|
}) }),
|
|
1642
1829
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1643
|
-
|
|
1830
|
+
controls?.map((control, i) => /* @__PURE__ */ jsx(React26.Fragment, { children: control }, i)),
|
|
1644
1831
|
/* @__PURE__ */ jsxs(Button$1, { variant: "ghost", size: "icon", onClick: () => setOpen(false), children: [
|
|
1645
1832
|
/* @__PURE__ */ jsx(X, { className: "size-4" }),
|
|
1646
1833
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close menu" })
|
|
@@ -1785,7 +1972,7 @@ function LogoStrip({
|
|
|
1785
1972
|
] });
|
|
1786
1973
|
}
|
|
1787
1974
|
function GridUnderlay({ variant, size = 24, className, ...props }) {
|
|
1788
|
-
const id =
|
|
1975
|
+
const id = React26.useId();
|
|
1789
1976
|
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: [
|
|
1790
1977
|
/* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
|
|
1791
1978
|
/* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
|
|
@@ -1813,9 +2000,9 @@ function GlowUnderlay({
|
|
|
1813
2000
|
style,
|
|
1814
2001
|
...props
|
|
1815
2002
|
}) {
|
|
1816
|
-
const [pos, setPos] =
|
|
1817
|
-
const containerRef =
|
|
1818
|
-
|
|
2003
|
+
const [pos, setPos] = React26.useState({ x: "50%", y: "50%" });
|
|
2004
|
+
const containerRef = React26.useRef(null);
|
|
2005
|
+
React26.useEffect(() => {
|
|
1819
2006
|
if (!followCursor) return;
|
|
1820
2007
|
const handleMouseMove = (e) => {
|
|
1821
2008
|
const el = containerRef.current;
|
|
@@ -1907,8 +2094,8 @@ function PageTransition({
|
|
|
1907
2094
|
style,
|
|
1908
2095
|
...props
|
|
1909
2096
|
}) {
|
|
1910
|
-
const [mounted, setMounted] =
|
|
1911
|
-
|
|
2097
|
+
const [mounted, setMounted] = React26.useState(false);
|
|
2098
|
+
React26.useEffect(() => {
|
|
1912
2099
|
const id = requestAnimationFrame(() => setMounted(true));
|
|
1913
2100
|
return () => cancelAnimationFrame(id);
|
|
1914
2101
|
}, []);
|
|
@@ -1935,8 +2122,8 @@ function Spotlight({
|
|
|
1935
2122
|
triggerClassName,
|
|
1936
2123
|
shortcut = "\u2318K"
|
|
1937
2124
|
}) {
|
|
1938
|
-
const [open, setOpen] =
|
|
1939
|
-
|
|
2125
|
+
const [open, setOpen] = React26.useState(false);
|
|
2126
|
+
React26.useEffect(() => {
|
|
1940
2127
|
const down = (e) => {
|
|
1941
2128
|
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
|
1942
2129
|
e.preventDefault();
|
|
@@ -1966,7 +2153,7 @@ function Spotlight({
|
|
|
1966
2153
|
/* @__PURE__ */ jsx(CommandInput, { placeholder }),
|
|
1967
2154
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
1968
2155
|
/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
|
|
1969
|
-
groups.map((group, i) => /* @__PURE__ */ jsxs(
|
|
2156
|
+
groups.map((group, i) => /* @__PURE__ */ jsxs(React26.Fragment, { children: [
|
|
1970
2157
|
i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
|
|
1971
2158
|
/* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
1972
2159
|
CommandItem,
|
|
@@ -1991,8 +2178,8 @@ function Spotlight({
|
|
|
1991
2178
|
var BASE = 40;
|
|
1992
2179
|
var MAGNIFY_RADIUS = 80;
|
|
1993
2180
|
function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
|
|
1994
|
-
const [mouseX, setMouseX] =
|
|
1995
|
-
const containerRef =
|
|
2181
|
+
const [mouseX, setMouseX] = React26.useState(null);
|
|
2182
|
+
const containerRef = React26.useRef(null);
|
|
1996
2183
|
function getScale(index) {
|
|
1997
2184
|
if (mouseX === null || !containerRef.current) return 1;
|
|
1998
2185
|
const container = containerRef.current;
|
|
@@ -2217,7 +2404,7 @@ function PageHeader({
|
|
|
2217
2404
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
|
|
2218
2405
|
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) => {
|
|
2219
2406
|
const isLast = i === breadcrumbs.length - 1;
|
|
2220
|
-
return /* @__PURE__ */ jsxs(
|
|
2407
|
+
return /* @__PURE__ */ jsxs(React26.Fragment, { children: [
|
|
2221
2408
|
i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
|
|
2222
2409
|
isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
|
|
2223
2410
|
href: crumb.href,
|
|
@@ -2253,10 +2440,58 @@ function PageHeader({
|
|
|
2253
2440
|
] });
|
|
2254
2441
|
}
|
|
2255
2442
|
function Suspensed({ children, fallback }) {
|
|
2256
|
-
return /* @__PURE__ */ jsx(
|
|
2443
|
+
return /* @__PURE__ */ jsx(React26.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
|
|
2444
|
+
}
|
|
2445
|
+
function ThemeSwitchMinimal() {
|
|
2446
|
+
const [theme, setThemeState] = React26.useState("dark");
|
|
2447
|
+
React26.useEffect(() => {
|
|
2448
|
+
const stored = localStorage.getItem("theme");
|
|
2449
|
+
const resolved = stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
|
|
2450
|
+
setThemeState(resolved);
|
|
2451
|
+
}, []);
|
|
2452
|
+
const toggle = () => {
|
|
2453
|
+
const next = theme === "dark" ? "light" : "dark";
|
|
2454
|
+
setThemeState(next);
|
|
2455
|
+
localStorage.setItem("theme", next);
|
|
2456
|
+
document.documentElement.classList.toggle("dark", next === "dark");
|
|
2457
|
+
};
|
|
2458
|
+
return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: toggle, className: "size-8", "aria-label": "Toggle theme", children: /* @__PURE__ */ jsx(
|
|
2459
|
+
"span",
|
|
2460
|
+
{
|
|
2461
|
+
className: "inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 spin-in-90 duration-200 motion-reduce:animate-none",
|
|
2462
|
+
children: theme === "dark" ? /* @__PURE__ */ jsx(Sun, { className: "size-4" }) : /* @__PURE__ */ jsx(Moon, { className: "size-4" })
|
|
2463
|
+
},
|
|
2464
|
+
theme
|
|
2465
|
+
) });
|
|
2466
|
+
}
|
|
2467
|
+
var MODES = ["default", "playful", "smooth"];
|
|
2468
|
+
var MODE_ICONS = {
|
|
2469
|
+
default: /* @__PURE__ */ jsx(LayoutGrid, { className: "size-4" }),
|
|
2470
|
+
playful: /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
|
|
2471
|
+
smooth: /* @__PURE__ */ jsx(Layers, { className: "size-4" })
|
|
2472
|
+
};
|
|
2473
|
+
var MODE_LABELS = {
|
|
2474
|
+
default: "Default mode",
|
|
2475
|
+
playful: "Playful mode",
|
|
2476
|
+
smooth: "Smooth mode"
|
|
2477
|
+
};
|
|
2478
|
+
function ModeSwitchMinimal() {
|
|
2479
|
+
const { mode, setMode } = useOlwibaUI();
|
|
2480
|
+
const cycle = () => {
|
|
2481
|
+
const idx = MODES.indexOf(mode);
|
|
2482
|
+
setMode(MODES[(idx + 1) % MODES.length]);
|
|
2483
|
+
};
|
|
2484
|
+
return /* @__PURE__ */ jsx(Button$1, { variant: "ghost", size: "icon", onClick: cycle, className: "size-8", "aria-label": MODE_LABELS[mode], children: /* @__PURE__ */ jsx(
|
|
2485
|
+
"span",
|
|
2486
|
+
{
|
|
2487
|
+
className: "inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 duration-200 motion-reduce:animate-none",
|
|
2488
|
+
children: MODE_ICONS[mode]
|
|
2489
|
+
},
|
|
2490
|
+
mode
|
|
2491
|
+
) });
|
|
2257
2492
|
}
|
|
2258
2493
|
function ThemeColorUpdater({ colorTheme }) {
|
|
2259
|
-
|
|
2494
|
+
React26.useEffect(() => {
|
|
2260
2495
|
if (colorTheme) {
|
|
2261
2496
|
document.documentElement.setAttribute("data-theme", colorTheme);
|
|
2262
2497
|
} else {
|
|
@@ -2266,7 +2501,7 @@ function ThemeColorUpdater({ colorTheme }) {
|
|
|
2266
2501
|
return null;
|
|
2267
2502
|
}
|
|
2268
2503
|
function VersionBanner({ version, message, className, onDismiss }) {
|
|
2269
|
-
const [dismissed, setDismissed] =
|
|
2504
|
+
const [dismissed, setDismissed] = React26.useState(false);
|
|
2270
2505
|
if (dismissed) return null;
|
|
2271
2506
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
|
|
2272
2507
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -2306,7 +2541,7 @@ function DevBanner({ environment = "development", className }) {
|
|
|
2306
2541
|
);
|
|
2307
2542
|
}
|
|
2308
2543
|
function RegisterHotkeys({ hotkeys }) {
|
|
2309
|
-
|
|
2544
|
+
React26.useEffect(() => {
|
|
2310
2545
|
const handleKeyDown = (event) => {
|
|
2311
2546
|
for (const hotkey of hotkeys) {
|
|
2312
2547
|
const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
|
|
@@ -2426,36 +2661,36 @@ function useMounted() {
|
|
|
2426
2661
|
return mounted;
|
|
2427
2662
|
}
|
|
2428
2663
|
function useConfirm() {
|
|
2429
|
-
const [isOpen, setIsOpen] =
|
|
2430
|
-
const [options, setOptions] =
|
|
2431
|
-
const resolveRef =
|
|
2432
|
-
const confirm =
|
|
2664
|
+
const [isOpen, setIsOpen] = React26.useState(false);
|
|
2665
|
+
const [options, setOptions] = React26.useState({});
|
|
2666
|
+
const resolveRef = React26.useRef(null);
|
|
2667
|
+
const confirm = React26.useCallback((opts = {}) => {
|
|
2433
2668
|
setOptions(opts);
|
|
2434
2669
|
setIsOpen(true);
|
|
2435
2670
|
return new Promise((resolve) => {
|
|
2436
2671
|
resolveRef.current = resolve;
|
|
2437
2672
|
});
|
|
2438
2673
|
}, []);
|
|
2439
|
-
const handleConfirm =
|
|
2674
|
+
const handleConfirm = React26.useCallback(() => {
|
|
2440
2675
|
resolveRef.current?.(true);
|
|
2441
2676
|
setIsOpen(false);
|
|
2442
2677
|
}, []);
|
|
2443
|
-
const handleCancel =
|
|
2678
|
+
const handleCancel = React26.useCallback(() => {
|
|
2444
2679
|
resolveRef.current?.(false);
|
|
2445
2680
|
setIsOpen(false);
|
|
2446
2681
|
}, []);
|
|
2447
2682
|
return { confirm, isOpen, options, handleConfirm, handleCancel };
|
|
2448
2683
|
}
|
|
2449
2684
|
function useControlledOpen(defaultOpen = false) {
|
|
2450
|
-
const [isOpen, setIsOpen] =
|
|
2451
|
-
const open =
|
|
2452
|
-
const close =
|
|
2453
|
-
const toggle =
|
|
2685
|
+
const [isOpen, setIsOpen] = React26.useState(defaultOpen);
|
|
2686
|
+
const open = React26.useCallback(() => setIsOpen(true), []);
|
|
2687
|
+
const close = React26.useCallback(() => setIsOpen(false), []);
|
|
2688
|
+
const toggle = React26.useCallback(() => setIsOpen((prev) => !prev), []);
|
|
2454
2689
|
return { isOpen, open, close, toggle, setIsOpen };
|
|
2455
2690
|
}
|
|
2456
2691
|
function useScrolledPast(threshold) {
|
|
2457
|
-
const [scrolledPast, setScrolledPast] =
|
|
2458
|
-
|
|
2692
|
+
const [scrolledPast, setScrolledPast] = React26.useState(false);
|
|
2693
|
+
React26.useEffect(() => {
|
|
2459
2694
|
const handleScroll = () => {
|
|
2460
2695
|
setScrolledPast(window.scrollY > threshold);
|
|
2461
2696
|
};
|
|
@@ -2466,30 +2701,30 @@ function useScrolledPast(threshold) {
|
|
|
2466
2701
|
return scrolledPast;
|
|
2467
2702
|
}
|
|
2468
2703
|
function useCopyToClipboard() {
|
|
2469
|
-
const [copied, setCopied] =
|
|
2470
|
-
const timeoutRef =
|
|
2471
|
-
const copy =
|
|
2704
|
+
const [copied, setCopied] = React26.useState(false);
|
|
2705
|
+
const timeoutRef = React26.useRef(void 0);
|
|
2706
|
+
const copy = React26.useCallback((text) => {
|
|
2472
2707
|
navigator.clipboard.writeText(text).then(() => {
|
|
2473
2708
|
setCopied(true);
|
|
2474
2709
|
clearTimeout(timeoutRef.current);
|
|
2475
2710
|
timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
2476
2711
|
});
|
|
2477
2712
|
}, []);
|
|
2478
|
-
|
|
2713
|
+
React26.useEffect(() => () => clearTimeout(timeoutRef.current), []);
|
|
2479
2714
|
return [copied, copy];
|
|
2480
2715
|
}
|
|
2481
2716
|
function useDebounce(value, delay) {
|
|
2482
|
-
const [debounced, setDebounced] =
|
|
2483
|
-
|
|
2717
|
+
const [debounced, setDebounced] = React26.useState(value);
|
|
2718
|
+
React26.useEffect(() => {
|
|
2484
2719
|
const timer = setTimeout(() => setDebounced(value), delay);
|
|
2485
2720
|
return () => clearTimeout(timer);
|
|
2486
2721
|
}, [value, delay]);
|
|
2487
2722
|
return debounced;
|
|
2488
2723
|
}
|
|
2489
2724
|
function useIntersectionObserver(options) {
|
|
2490
|
-
const ref =
|
|
2491
|
-
const [isIntersecting, setIsIntersecting] =
|
|
2492
|
-
|
|
2725
|
+
const ref = React26.useRef(null);
|
|
2726
|
+
const [isIntersecting, setIsIntersecting] = React26.useState(false);
|
|
2727
|
+
React26.useEffect(() => {
|
|
2493
2728
|
const el = ref.current;
|
|
2494
2729
|
if (!el) return;
|
|
2495
2730
|
const observer = new IntersectionObserver(([entry]) => {
|
|
@@ -2501,7 +2736,7 @@ function useIntersectionObserver(options) {
|
|
|
2501
2736
|
return [ref, isIntersecting];
|
|
2502
2737
|
}
|
|
2503
2738
|
function useLocalStorage(key, initialValue) {
|
|
2504
|
-
const [storedValue, setStoredValue] =
|
|
2739
|
+
const [storedValue, setStoredValue] = React26.useState(() => {
|
|
2505
2740
|
if (typeof window === "undefined") return initialValue;
|
|
2506
2741
|
try {
|
|
2507
2742
|
const item = window.localStorage.getItem(key);
|
|
@@ -2510,7 +2745,7 @@ function useLocalStorage(key, initialValue) {
|
|
|
2510
2745
|
return initialValue;
|
|
2511
2746
|
}
|
|
2512
2747
|
});
|
|
2513
|
-
const setValue =
|
|
2748
|
+
const setValue = React26.useCallback(
|
|
2514
2749
|
(value) => {
|
|
2515
2750
|
try {
|
|
2516
2751
|
setStoredValue(value);
|
|
@@ -2525,11 +2760,11 @@ function useLocalStorage(key, initialValue) {
|
|
|
2525
2760
|
return [storedValue, setValue];
|
|
2526
2761
|
}
|
|
2527
2762
|
function useMediaQuery(query) {
|
|
2528
|
-
const [matches, setMatches] =
|
|
2763
|
+
const [matches, setMatches] = React26.useState(() => {
|
|
2529
2764
|
if (typeof window === "undefined") return false;
|
|
2530
2765
|
return window.matchMedia(query).matches;
|
|
2531
2766
|
});
|
|
2532
|
-
|
|
2767
|
+
React26.useEffect(() => {
|
|
2533
2768
|
if (typeof window === "undefined") return;
|
|
2534
2769
|
const mql = window.matchMedia(query);
|
|
2535
2770
|
setMatches(mql.matches);
|
|
@@ -2540,18 +2775,18 @@ function useMediaQuery(query) {
|
|
|
2540
2775
|
return matches;
|
|
2541
2776
|
}
|
|
2542
2777
|
function usePagination(total, pageSize) {
|
|
2543
|
-
const [page, setPage] =
|
|
2778
|
+
const [page, setPage] = React26.useState(1);
|
|
2544
2779
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
2545
2780
|
const offset = (page - 1) * pageSize;
|
|
2546
|
-
const goTo =
|
|
2781
|
+
const goTo = React26.useCallback(
|
|
2547
2782
|
(p) => setPage(Math.min(Math.max(1, p), totalPages)),
|
|
2548
2783
|
[totalPages]
|
|
2549
2784
|
);
|
|
2550
|
-
const next =
|
|
2551
|
-
const prev =
|
|
2785
|
+
const next = React26.useCallback(() => goTo(page + 1), [page, goTo]);
|
|
2786
|
+
const prev = React26.useCallback(() => goTo(page - 1), [page, goTo]);
|
|
2552
2787
|
return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
|
|
2553
2788
|
}
|
|
2554
2789
|
|
|
2555
|
-
export { AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, Button, Card, ChangelogCard, ChangelogList, Checkbox, ConfirmDialog, ContactSection, ContextMenu, CountUp, CtaSection, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeaturesSection, Footer, FullPageSpinner, GlassCard, Grid, GridItem, HeroSection, ImageCard, Input, LogoStrip, Navbar, NewsletterSection, OlwibaUIProvider, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, Suspensed, Switch, TeamSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
|
|
2790
|
+
export { AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, Button, Card, CarouselSection, ChangelogCard, ChangelogList, Checkbox, ConfirmDialog, ContactSection, ContextMenu, CountUp, CtaSection, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeaturesSection, Footer, FullPageSpinner, GlassCard, Grid, GridItem, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, OlwibaUIProvider, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, Suspensed, Switch, TeamSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
|
|
2556
2791
|
//# sourceMappingURL=index.js.map
|
|
2557
2792
|
//# sourceMappingURL=index.js.map
|