@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.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 React35 from 'react';
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 = React35.createContext({
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] = React35.useState(initialMode);
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 React35.useContext(OlwibaUIContext);
30
+ return React20.useContext(OlwibaUIContext);
31
31
  }
32
32
  function useUIMode() {
33
- return React35.useContext(OlwibaUIContext).mode;
33
+ return React20.useContext(OlwibaUIContext).mode;
34
34
  }
35
- var Button = React35.forwardRef(
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 = React35.forwardRef(
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 = React35.forwardRef(
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 = React35.forwardRef(
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 = React35.forwardRef(({ mode, ...props }, ref) => {
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 = React35.forwardRef(({ mode: _mode, ...props }, ref) => {
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
- className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground 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",
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
- tooltip: typeof brand.name === "string" ? brand.name : void 0,
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] = React35.useState("");
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] = React35.useState([]);
698
- const [rowSelection, setRowSelection] = React35.useState({});
699
- const [globalFilter, setGlobalFilter] = React35.useState("");
700
- const allColumns = React35.useMemo(
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
- React35.useEffect(() => {
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] = React35.useState("");
803
- const [role, setRole] = React35.useState(roles[roles.length - 1] ?? roles[0]);
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] = React35.useState(false);
848
- const columns = React35.useMemo(() => [
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 = React35.useMemo(() => [
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] = React35.useState(0);
997
- const [error, setError] = React35.useState(null);
998
- const [pending, setPending] = React35.useState(false);
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] = React35.useState(forceShow);
1329
- const [entered, setEntered] = React35.useState(false);
1330
- const [refreshing, setRefreshing] = React35.useState(false);
1331
- React35.useEffect(() => {
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
- React35.useEffect(() => {
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 = React35.useRef(null);
1428
- const [visible, setVisible] = React35.useState(false);
1429
- React35.useEffect(() => {
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 = React35.useRef(null);
1625
- const [canPrev, setCanPrev] = React35.useState(false);
1626
- const [canNext, setCanNext] = React35.useState(true);
1627
- const updateScrollState = React35.useCallback(() => {
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
- React35.useEffect(() => {
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: React35.Children.map(
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 = React35.useRef(null);
1716
- const [visible, setVisible] = React35.useState(false);
1717
- React35.useEffect(() => {
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: React35.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
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(React35.Fragment, { children: [
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 = React35.useRef(null);
2048
- const [isIntersecting, setIsIntersecting] = React35.useState(false);
2049
- React35.useEffect(() => {
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] = React35.useState(false);
2072
- React35.useEffect(() => {
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("span", { className: "text-4xl font-bold tracking-tight", children: price }),
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] = React35.useState(() => getTimeLeft(deadline));
2250
- React35.useEffect(() => {
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(React35.Fragment, { children: [
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] = React35.useState(false);
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] = React35.useState(initialCadence);
2455
+ const [activeCadence, setActiveCadence] = React20.useState(initialCadence);
2336
2456
  const cadence = cadences?.find((c) => c.key === activeCadence);
2337
- const saveBadges = React35.useMemo(
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] && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "text-xs", children: 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: "secondary", className: "text-xs", children: saveBadge })
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 = React35.useRef(null);
2627
- const [value, setValue] = React35.useState(from);
2628
- const hasStarted = React35.useRef(false);
2629
- React35.useEffect(() => {
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] = React35.useState("");
2723
- const [submitted, setSubmitted] = React35.useState(false);
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] = React35.useState(false);
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] = React35.useState(false);
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 = React35.useCallback(() => setOpen(false), []);
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(React35.Fragment, { children: control }, i)),
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(React35.Fragment, { children: control }, i)) }) : null,
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 = React35.useId();
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] = React35.useState({ x: "50%", y: "50%" });
3332
- const containerRef = React35.useRef(null);
3333
- React35.useEffect(() => {
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] = React35.useState(false);
3473
- React35.useEffect(() => {
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 = React35.Children.toArray(children);
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] = React35.useState(false);
3561
- React35.useEffect(() => {
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(React35.Fragment, { children: [
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] = React35.useState(null);
3617
- const containerRef = React35.useRef(null);
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
- React35.useEffect(() => {
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] = React35.useState(defaultOpen);
3734
- const open = React35.useCallback(() => setIsOpen(true), []);
3735
- const close = React35.useCallback(() => setIsOpen(false), []);
3736
- const toggle = React35.useCallback(() => setIsOpen((prev) => !prev), []);
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 = React35.useCallback(
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(React35.Fragment, { children: [
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(React35.Fragment, { children: renderCard(item) }, getRowId(item))) });
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] = React35.useState(defaultFiles ?? []);
4004
- const [isDragging, setIsDragging] = React35.useState(false);
4005
- const [validationError, setValidationError] = React35.useState(null);
4006
- const inputRef = React35.useRef(null);
4007
- const dragDepth = React35.useRef(0);
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 = React35.useCallback(
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 = React35.useMemo(
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(React35.Fragment, { children: [
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(React35.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
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] = React35.useState(getInitialTheme);
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] = React35.useState(getInitialColorName);
4911
- const [open, setOpen] = React35.useState(false);
4912
- React35.useEffect(() => {
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
- React35.useEffect(() => {
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] = React35.useState(false);
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] = React35.useState(false);
5241
- const [options, setOptions] = React35.useState({});
5242
- const resolveRef = React35.useRef(null);
5243
- const confirm = React35.useCallback((opts = {}) => {
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 = React35.useCallback(() => {
5375
+ const handleConfirm = React20.useCallback(() => {
5251
5376
  resolveRef.current?.(true);
5252
5377
  setIsOpen(false);
5253
5378
  }, []);
5254
- const handleCancel = React35.useCallback(() => {
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] = React35.useState(false);
5262
- React35.useEffect(() => {
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] = React35.useState(false);
5274
- const timeoutRef = React35.useRef(void 0);
5275
- const copy = React35.useCallback((text) => {
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
- React35.useEffect(() => () => clearTimeout(timeoutRef.current), []);
5407
+ React20.useEffect(() => () => clearTimeout(timeoutRef.current), []);
5283
5408
  return [copied, copy];
5284
5409
  }
5285
5410
  function useDebounce(value, delay) {
5286
- const [debounced, setDebounced] = React35.useState(value);
5287
- React35.useEffect(() => {
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] = React35.useState(() => {
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 = React35.useCallback(
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] = React35.useState(() => {
5450
+ const [matches, setMatches] = React20.useState(() => {
5326
5451
  if (typeof window === "undefined") return false;
5327
5452
  return window.matchMedia(query).matches;
5328
5453
  });
5329
- React35.useEffect(() => {
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] = React35.useState(1);
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 = React35.useCallback(
5468
+ const goTo = React20.useCallback(
5344
5469
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
5345
5470
  [totalPages]
5346
5471
  );
5347
- const next = React35.useCallback(() => goTo(page + 1), [page, goTo]);
5348
- const prev = React35.useCallback(() => goTo(page - 1), [page, goTo]);
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