@olwiba/ui 0.2.5 → 0.2.7

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,11 +1,11 @@
1
1
  import { cn } from './chunk-AMWLSHD6.js';
2
2
  export { MdxContent, cn } from './chunk-AMWLSHD6.js';
3
- import * as React34 from 'react';
3
+ import * as React35 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';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, Loader2, ArrowLeft, ArrowUpRight, X, Sparkles, RefreshCw, ArrowRight, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, CheckCircle2, AlertCircle, Bell, Inbox, Activity, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Rocket, Info, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
8
+ import { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, Loader2, ArrowLeft, ArrowUpRight, X, Sparkles, RefreshCw, ArrowRight, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, CheckCircle2, AlertCircle, Bell, Inbox, Activity, TrendingUp, TrendingDown, LayoutGrid, List, Sun, Moon, Palette, AlertTriangle, Rocket, Info, Layers, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
9
9
  import { useReactTable, getPaginationRowModel, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
10
10
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core';
11
11
  import { sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, horizontalListSortingStrategy, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
@@ -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 = React34.createContext({
16
+ var OlwibaUIContext = React35.createContext({
17
17
  isMobile: false,
18
18
  mode: "default",
19
19
  setMode: () => {
@@ -22,17 +22,17 @@ var OlwibaUIContext = React34.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] = React34.useState(initialMode);
25
+ const [mode, setMode] = React35.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 React34.useContext(OlwibaUIContext);
30
+ return React35.useContext(OlwibaUIContext);
31
31
  }
32
32
  function useUIMode() {
33
- return React34.useContext(OlwibaUIContext).mode;
33
+ return React35.useContext(OlwibaUIContext).mode;
34
34
  }
35
- var Button = React34.forwardRef(
35
+ var Button = React35.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 = React34.forwardRef(
40
40
  }
41
41
  );
42
42
  Button.displayName = "Button";
43
- var Card = React34.forwardRef(
43
+ var Card = React35.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 = React34.forwardRef(
54
+ var Input = React35.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 = React34.forwardRef(
59
59
  }
60
60
  );
61
61
  Input.displayName = "Input";
62
- var Textarea = React34.forwardRef(
62
+ var Textarea = React35.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 = React34.forwardRef(
67
67
  }
68
68
  );
69
69
  Textarea.displayName = "Textarea";
70
- var Checkbox = React34.forwardRef(({ mode, ...props }, ref) => {
70
+ var Checkbox = React35.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 = React34.forwardRef(({ mode: _mode, ...props }, ref) => {
76
+ var Switch = React35.forwardRef(({ mode: _mode, ...props }, ref) => {
77
77
  return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
78
78
  });
79
79
  Switch.displayName = "Switch";
@@ -212,19 +212,22 @@ function AppContent({
212
212
  }
213
213
  var columnsMap = {
214
214
  1: "grid-cols-1",
215
- 2: "grid-cols-1 md:grid-cols-2",
216
- 3: "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
217
- 4: "grid-cols-1 md:grid-cols-2 xl:grid-cols-4"
215
+ 2: "grid-cols-1 sm:grid-cols-2",
216
+ 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
217
+ 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
218
+ 5: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",
219
+ 6: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
218
220
  };
219
221
  var gapMap3 = {
222
+ none: "gap-0",
220
223
  sm: "gap-3",
221
224
  md: "gap-4",
222
225
  lg: "gap-6"
223
226
  };
224
227
  var spanMap2 = {
225
- 1: "md:col-span-1",
226
- 2: "md:col-span-2",
227
- 3: "xl:col-span-3",
228
+ 1: "sm:col-span-1",
229
+ 2: "sm:col-span-2",
230
+ 3: "lg:col-span-3",
228
231
  4: "xl:col-span-4",
229
232
  full: "col-span-full"
230
233
  };
@@ -429,6 +432,7 @@ function AppShell({
429
432
  collapsible = "icon",
430
433
  sidebarPosition = "viewport",
431
434
  side = "left",
435
+ contentClassName = "p-6",
432
436
  children
433
437
  } = {}) {
434
438
  const isContained = sidebarPosition === "contained";
@@ -453,7 +457,7 @@ function AppShell({
453
457
  ),
454
458
  /* @__PURE__ */ jsxs(SidebarInset, { className: isContained ? void 0 : "overflow-y-auto", children: [
455
459
  /* @__PURE__ */ jsx(ShellHeader, { pageTitle, headerStart, headerEnd }),
456
- children
460
+ /* @__PURE__ */ jsx("div", { className: contentClassName, children })
457
461
  ] })
458
462
  ]
459
463
  }
@@ -530,7 +534,7 @@ function DefaultForm({
530
534
  const isForgotPassword = mode === "forgot-password";
531
535
  const isResetPassword = mode === "reset-password";
532
536
  const isVerify = mode === "verify";
533
- const [code, setCode] = React34.useState("");
537
+ const [code, setCode] = React35.useState("");
534
538
  const hasPrefill = !!(defaultEmail || defaultPassword);
535
539
  const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
536
540
  const copy = authCopy[mode];
@@ -690,10 +694,10 @@ function DataTable({
690
694
  emptyMessage = "No results.",
691
695
  className
692
696
  }) {
693
- const [sorting, setSorting] = React34.useState([]);
694
- const [rowSelection, setRowSelection] = React34.useState({});
695
- const [globalFilter, setGlobalFilter] = React34.useState("");
696
- const allColumns = React34.useMemo(
697
+ const [sorting, setSorting] = React35.useState([]);
698
+ const [rowSelection, setRowSelection] = React35.useState({});
699
+ const [globalFilter, setGlobalFilter] = React35.useState("");
700
+ const allColumns = React35.useMemo(
697
701
  () => selectable ? [selectColumn, ...columns] : columns,
698
702
  [selectable, columns]
699
703
  );
@@ -715,7 +719,7 @@ function DataTable({
715
719
  initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
716
720
  enableRowSelection: selectable
717
721
  });
718
- React34.useEffect(() => {
722
+ React35.useEffect(() => {
719
723
  if (!onSelectionChange) return;
720
724
  onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
721
725
  }, [rowSelection]);
@@ -795,8 +799,8 @@ function DataTable({
795
799
  ] });
796
800
  }
797
801
  function InviteDialog({ roles, onInvite, onClose }) {
798
- const [email, setEmail] = React34.useState("");
799
- const [role, setRole] = React34.useState(roles[roles.length - 1] ?? roles[0]);
802
+ const [email, setEmail] = React35.useState("");
803
+ const [role, setRole] = React35.useState(roles[roles.length - 1] ?? roles[0]);
800
804
  return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
801
805
  "form",
802
806
  {
@@ -840,8 +844,8 @@ function TeamMembersPanel({
840
844
  title = "Team members",
841
845
  description = "Manage who has access to this workspace."
842
846
  }) {
843
- const [inviteOpen, setInviteOpen] = React34.useState(false);
844
- const columns = React34.useMemo(() => [
847
+ const [inviteOpen, setInviteOpen] = React35.useState(false);
848
+ const columns = React35.useMemo(() => [
845
849
  {
846
850
  accessorKey: "name",
847
851
  header: "Name",
@@ -910,7 +914,7 @@ function BillingPanel({
910
914
  onUpdatePaymentMethod,
911
915
  className
912
916
  }) {
913
- const invoiceColumns = React34.useMemo(() => [
917
+ const invoiceColumns = React35.useMemo(() => [
914
918
  { accessorKey: "date", header: "Date", enableSorting: false },
915
919
  { accessorKey: "amount", header: "Amount", enableSorting: false },
916
920
  {
@@ -989,9 +993,9 @@ function OnboardingWizard({
989
993
  completeLabel = "Finish",
990
994
  className
991
995
  }) {
992
- const [internalStep, setInternalStep] = React34.useState(0);
993
- const [error, setError] = React34.useState(null);
994
- const [pending, setPending] = React34.useState(false);
996
+ const [internalStep, setInternalStep] = React35.useState(0);
997
+ const [error, setError] = React35.useState(null);
998
+ const [pending, setPending] = React35.useState(false);
995
999
  const index = stepProp ?? internalStep;
996
1000
  const current = steps[index];
997
1001
  const isLast = index === steps.length - 1;
@@ -1321,10 +1325,10 @@ function UpdateBanner({
1321
1325
  buttonLabel = "Refresh now",
1322
1326
  className
1323
1327
  }) {
1324
- const [show, setShow] = React34.useState(forceShow);
1325
- const [entered, setEntered] = React34.useState(false);
1326
- const [refreshing, setRefreshing] = React34.useState(false);
1327
- React34.useEffect(() => {
1328
+ const [show, setShow] = React35.useState(forceShow);
1329
+ const [entered, setEntered] = React35.useState(false);
1330
+ const [refreshing, setRefreshing] = React35.useState(false);
1331
+ React35.useEffect(() => {
1328
1332
  if (forceShow) {
1329
1333
  setShow(true);
1330
1334
  return;
@@ -1347,7 +1351,7 @@ function UpdateBanner({
1347
1351
  clearInterval(id);
1348
1352
  };
1349
1353
  }, [currentVersion, fetchVersion, forceShow, intervalMs]);
1350
- React34.useEffect(() => {
1354
+ React35.useEffect(() => {
1351
1355
  if (!show) {
1352
1356
  setEntered(false);
1353
1357
  return;
@@ -1420,9 +1424,9 @@ function FadeIn({
1420
1424
  style,
1421
1425
  ...props
1422
1426
  }) {
1423
- const ref = React34.useRef(null);
1424
- const [visible, setVisible] = React34.useState(false);
1425
- React34.useEffect(() => {
1427
+ const ref = React35.useRef(null);
1428
+ const [visible, setVisible] = React35.useState(false);
1429
+ React35.useEffect(() => {
1426
1430
  const el = ref.current;
1427
1431
  if (!el) return;
1428
1432
  const observer = new IntersectionObserver(
@@ -1617,16 +1621,16 @@ function Carousel({
1617
1621
  className
1618
1622
  }) {
1619
1623
  const mode = useUIVariant();
1620
- const trackRef = React34.useRef(null);
1621
- const [canPrev, setCanPrev] = React34.useState(false);
1622
- const [canNext, setCanNext] = React34.useState(true);
1623
- const updateScrollState = React34.useCallback(() => {
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(() => {
1624
1628
  const track = trackRef.current;
1625
1629
  if (!track) return;
1626
1630
  setCanPrev(track.scrollLeft > 8);
1627
1631
  setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1628
1632
  }, []);
1629
- React34.useEffect(() => {
1633
+ React35.useEffect(() => {
1630
1634
  const track = trackRef.current;
1631
1635
  if (!track) return;
1632
1636
  updateScrollState();
@@ -1683,7 +1687,7 @@ function Carousel({
1683
1687
  role: "region",
1684
1688
  "aria-label": ariaLabel,
1685
1689
  className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1686
- children: React34.Children.map(
1690
+ children: React35.Children.map(
1687
1691
  children,
1688
1692
  (child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
1689
1693
  )
@@ -1708,9 +1712,9 @@ function StaggerChildren({
1708
1712
  className,
1709
1713
  ...props
1710
1714
  }) {
1711
- const ref = React34.useRef(null);
1712
- const [visible, setVisible] = React34.useState(false);
1713
- React34.useEffect(() => {
1715
+ const ref = React35.useRef(null);
1716
+ const [visible, setVisible] = React35.useState(false);
1717
+ React35.useEffect(() => {
1714
1718
  const el = ref.current;
1715
1719
  if (!el) return;
1716
1720
  const observer = new IntersectionObserver(
@@ -1728,7 +1732,7 @@ function StaggerChildren({
1728
1732
  return () => observer.disconnect();
1729
1733
  }, [once]);
1730
1734
  const [tx, ty] = translateMap2[direction];
1731
- return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React34.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1735
+ return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React35.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1732
1736
  "div",
1733
1737
  {
1734
1738
  className: "h-full",
@@ -1870,7 +1874,7 @@ function StepsSection({
1870
1874
  /* @__PURE__ */ jsx("div", { className: "mt-12", children: resolvedGroups.map((group, groupIndex) => {
1871
1875
  const startNumber = nextNumber;
1872
1876
  nextNumber += group.steps.length;
1873
- return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
1877
+ return /* @__PURE__ */ jsxs(React35.Fragment, { children: [
1874
1878
  /* @__PURE__ */ jsx("div", { className: cn$1(groupIndex > 0 && "mt-10"), children: /* @__PURE__ */ jsx(TimelineGroup, { steps: group.steps, startNumber, fadeIn: groupIndex > 0 }) }),
1875
1879
  group.after && /* @__PURE__ */ jsx("div", { className: "mt-16", children: group.after })
1876
1880
  ] }, groupIndex);
@@ -2040,9 +2044,9 @@ function FeatureMarqueeSection({
2040
2044
  ] });
2041
2045
  }
2042
2046
  function useIntersectionObserver(options) {
2043
- const ref = React34.useRef(null);
2044
- const [isIntersecting, setIsIntersecting] = React34.useState(false);
2045
- React34.useEffect(() => {
2047
+ const ref = React35.useRef(null);
2048
+ const [isIntersecting, setIsIntersecting] = React35.useState(false);
2049
+ React35.useEffect(() => {
2046
2050
  const el = ref.current;
2047
2051
  if (!el) return;
2048
2052
  const observer = new IntersectionObserver(([entry]) => {
@@ -2064,8 +2068,8 @@ function ShowcaseCta({
2064
2068
  sectionClasses
2065
2069
  }) {
2066
2070
  const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
2067
- const [visible, setVisible] = React34.useState(false);
2068
- React34.useEffect(() => {
2071
+ const [visible, setVisible] = React35.useState(false);
2072
+ React35.useEffect(() => {
2069
2073
  if (intersecting) setVisible(true);
2070
2074
  }, [intersecting]);
2071
2075
  return /* @__PURE__ */ jsxs(
@@ -2242,8 +2246,8 @@ function CountdownTimer({
2242
2246
  className,
2243
2247
  compact = false
2244
2248
  }) {
2245
- const [timeLeft, setTimeLeft] = React34.useState(() => getTimeLeft(deadline));
2246
- React34.useEffect(() => {
2249
+ const [timeLeft, setTimeLeft] = React35.useState(() => getTimeLeft(deadline));
2250
+ React35.useEffect(() => {
2247
2251
  setTimeLeft(getTimeLeft(deadline));
2248
2252
  const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
2249
2253
  return () => clearInterval(id);
@@ -2269,7 +2273,7 @@ function CountdownTimer({
2269
2273
  ];
2270
2274
  return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
2271
2275
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
2272
- /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
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: [
2273
2277
  /* @__PURE__ */ jsxs("span", { children: [
2274
2278
  String(value).padStart(2, "0"),
2275
2279
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
@@ -2294,7 +2298,7 @@ function PricingSection({
2294
2298
  highlightedBadgeLabel = "Founding member",
2295
2299
  renderPlanFooter
2296
2300
  }) {
2297
- const [annual, setAnnual] = React34.useState(false);
2301
+ const [annual, setAnnual] = React35.useState(false);
2298
2302
  const isOneTime = mode === "one-time";
2299
2303
  const uiMode = useUIVariant();
2300
2304
  const sectionClasses = cn$1(
@@ -2563,10 +2567,10 @@ function CountUp({
2563
2567
  className,
2564
2568
  ...props
2565
2569
  }) {
2566
- const ref = React34.useRef(null);
2567
- const [value, setValue] = React34.useState(from);
2568
- const hasStarted = React34.useRef(false);
2569
- React34.useEffect(() => {
2570
+ const ref = React35.useRef(null);
2571
+ const [value, setValue] = React35.useState(from);
2572
+ const hasStarted = React35.useRef(false);
2573
+ React35.useEffect(() => {
2570
2574
  const el = ref.current;
2571
2575
  if (!el) return;
2572
2576
  const observer = new IntersectionObserver(
@@ -2659,8 +2663,8 @@ function NewsletterSection({
2659
2663
  successDescription = defaults.successDescription,
2660
2664
  onSubscribe
2661
2665
  } = {}) {
2662
- const [email, setEmail] = React34.useState("");
2663
- const [submitted, setSubmitted] = React34.useState(false);
2666
+ const [email, setEmail] = React35.useState("");
2667
+ const [submitted, setSubmitted] = React35.useState(false);
2664
2668
  const mode = useUIVariant();
2665
2669
  const sectionClasses = cn$1(
2666
2670
  "overflow-hidden bg-card",
@@ -2751,7 +2755,7 @@ function ContactSection({
2751
2755
  sendAnotherLabel = defaults2.sendAnotherLabel,
2752
2756
  onSubmit
2753
2757
  } = {}) {
2754
- const [submitted, setSubmitted] = React34.useState(false);
2758
+ const [submitted, setSubmitted] = React35.useState(false);
2755
2759
  const mode = useUIVariant();
2756
2760
  const sectionClasses = cn$1(
2757
2761
  "overflow-hidden bg-card",
@@ -3049,9 +3053,10 @@ function Navbar({
3049
3053
  controls,
3050
3054
  renderLink = defaultRenderLink7
3051
3055
  }) {
3052
- const [open, setOpen] = React34.useState(false);
3056
+ const [open, setOpen] = React35.useState(false);
3053
3057
  const brandHref = brand.href ?? "/";
3054
3058
  const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
3059
+ const closeMobileMenu = React35.useCallback(() => setOpen(false), []);
3055
3060
  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: [
3056
3061
  renderLink({
3057
3062
  href: brandHref,
@@ -3066,7 +3071,7 @@ function Navbar({
3066
3071
  children: link.label
3067
3072
  }) }, link.label)) }),
3068
3073
  hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
3069
- controls?.map((control, i) => /* @__PURE__ */ jsx(React34.Fragment, { children: control }, i)),
3074
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React35.Fragment, { children: control }, i)),
3070
3075
  (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
3071
3076
  cta?.secondary && renderLink({
3072
3077
  href: cta.secondary.href,
@@ -3083,29 +3088,29 @@ function Navbar({
3083
3088
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Open menu" })
3084
3089
  ] }) }),
3085
3090
  /* @__PURE__ */ jsxs(SheetContent, { side: "left", className: "w-72", children: [
3086
- /* @__PURE__ */ jsx("div", { className: "flex items-center pb-4", children: /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), className: "cursor-pointer", children: renderLink({
3091
+ /* @__PURE__ */ jsx("div", { className: "flex items-center pb-4", children: /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, className: "cursor-pointer", children: renderLink({
3087
3092
  href: brandHref,
3088
3093
  children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 font-semibold", children: [
3089
3094
  brand.logo,
3090
3095
  /* @__PURE__ */ jsx("span", { children: brand.name })
3091
3096
  ] })
3092
3097
  }) }) }),
3093
- controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React34.Fragment, { children: control }, i)) }) : null,
3098
+ 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,
3094
3099
  /* @__PURE__ */ jsx(Separator, {}),
3095
- /* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
3100
+ /* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, children: renderLink({
3096
3101
  href: link.href,
3097
3102
  className: "block rounded-md px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
3098
3103
  children: link.label
3099
3104
  }) }, link.label)) }),
3100
3105
  /* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-col gap-2", children: [
3101
- cta?.secondary && renderLink({
3106
+ cta?.secondary && /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, children: renderLink({
3102
3107
  href: cta.secondary.href,
3103
3108
  children: /* @__PURE__ */ jsx(Button$1, { variant: "outline", className: "w-full", children: cta.secondary.label })
3104
- }),
3105
- cta?.primary && renderLink({
3109
+ }) }),
3110
+ cta?.primary && /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, children: renderLink({
3106
3111
  href: cta.primary.href,
3107
3112
  children: /* @__PURE__ */ jsx(Button$1, { className: "w-full", children: cta.primary.label })
3108
- })
3113
+ }) })
3109
3114
  ] })
3110
3115
  ] })
3111
3116
  ] })
@@ -3239,7 +3244,7 @@ function LogoStrip({
3239
3244
  ] });
3240
3245
  }
3241
3246
  function GridUnderlay({ variant, size = 24, className, ...props }) {
3242
- const id = React34.useId();
3247
+ const id = React35.useId();
3243
3248
  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: [
3244
3249
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
3245
3250
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -3267,9 +3272,9 @@ function GlowUnderlay({
3267
3272
  style,
3268
3273
  ...props
3269
3274
  }) {
3270
- const [pos, setPos] = React34.useState({ x: "50%", y: "50%" });
3271
- const containerRef = React34.useRef(null);
3272
- React34.useEffect(() => {
3275
+ const [pos, setPos] = React35.useState({ x: "50%", y: "50%" });
3276
+ const containerRef = React35.useRef(null);
3277
+ React35.useEffect(() => {
3273
3278
  if (!followCursor) return;
3274
3279
  const handleMouseMove = (e) => {
3275
3280
  const el = containerRef.current;
@@ -3408,8 +3413,8 @@ function PageTransition({
3408
3413
  style,
3409
3414
  ...props
3410
3415
  }) {
3411
- const [mounted, setMounted] = React34.useState(false);
3412
- React34.useEffect(() => {
3416
+ const [mounted, setMounted] = React35.useState(false);
3417
+ React35.useEffect(() => {
3413
3418
  const id = requestAnimationFrame(() => setMounted(true));
3414
3419
  return () => cancelAnimationFrame(id);
3415
3420
  }, []);
@@ -3484,7 +3489,7 @@ function Sortable({
3484
3489
  if (oldIndex === -1 || newIndex === -1) return;
3485
3490
  onReorder(arrayMove(items, oldIndex, newIndex));
3486
3491
  };
3487
- const childArray = React34.Children.toArray(children);
3492
+ const childArray = React35.Children.toArray(children);
3488
3493
  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(
3489
3494
  (child, i) => items[i] === void 0 ? null : /* @__PURE__ */ jsx(SortableItem, { id: items[i], disabled, className: itemClassName, children: child }, items[i])
3490
3495
  ) }) }) });
@@ -3496,8 +3501,8 @@ function Spotlight({
3496
3501
  triggerClassName,
3497
3502
  shortcut = "\u2318K"
3498
3503
  }) {
3499
- const [open, setOpen] = React34.useState(false);
3500
- React34.useEffect(() => {
3504
+ const [open, setOpen] = React35.useState(false);
3505
+ React35.useEffect(() => {
3501
3506
  const down = (e) => {
3502
3507
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
3503
3508
  e.preventDefault();
@@ -3527,7 +3532,7 @@ function Spotlight({
3527
3532
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3528
3533
  /* @__PURE__ */ jsxs(CommandList, { children: [
3529
3534
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
3530
- groups.map((group, i) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
3535
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React35.Fragment, { children: [
3531
3536
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3532
3537
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3533
3538
  CommandItem,
@@ -3552,8 +3557,8 @@ function Spotlight({
3552
3557
  var BASE = 40;
3553
3558
  var MAGNIFY_RADIUS = 80;
3554
3559
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
3555
- const [mouseX, setMouseX] = React34.useState(null);
3556
- const containerRef = React34.useRef(null);
3560
+ const [mouseX, setMouseX] = React35.useState(null);
3561
+ const containerRef = React35.useRef(null);
3557
3562
  function getScale(index) {
3558
3563
  if (mouseX === null || !containerRef.current) return 1;
3559
3564
  const container = containerRef.current;
@@ -3652,7 +3657,7 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
3652
3657
  ] }) });
3653
3658
  }
3654
3659
  function RegisterHotkeys({ hotkeys }) {
3655
- React34.useEffect(() => {
3660
+ React35.useEffect(() => {
3656
3661
  const handleKeyDown = (event) => {
3657
3662
  for (const hotkey of hotkeys) {
3658
3663
  const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
@@ -3669,10 +3674,10 @@ function RegisterHotkeys({ hotkeys }) {
3669
3674
  return null;
3670
3675
  }
3671
3676
  function useControlledOpen(defaultOpen = false) {
3672
- const [isOpen, setIsOpen] = React34.useState(defaultOpen);
3673
- const open = React34.useCallback(() => setIsOpen(true), []);
3674
- const close = React34.useCallback(() => setIsOpen(false), []);
3675
- const toggle = React34.useCallback(() => setIsOpen((prev) => !prev), []);
3677
+ const [isOpen, setIsOpen] = React35.useState(defaultOpen);
3678
+ const open = React35.useCallback(() => setIsOpen(true), []);
3679
+ const close = React35.useCallback(() => setIsOpen(false), []);
3680
+ const toggle = React35.useCallback(() => setIsOpen((prev) => !prev), []);
3676
3681
  return { isOpen, open, close, toggle, setIsOpen };
3677
3682
  }
3678
3683
  function CommandMenu({
@@ -3685,7 +3690,7 @@ function CommandMenu({
3685
3690
  }) {
3686
3691
  const internal = useControlledOpen(false);
3687
3692
  const isOpen = openProp ?? internal.isOpen;
3688
- const setOpen = React34.useCallback(
3693
+ const setOpen = React35.useCallback(
3689
3694
  (next) => {
3690
3695
  if (openProp === void 0) internal.setIsOpen(next);
3691
3696
  onOpenChange?.(next);
@@ -3710,7 +3715,7 @@ function CommandMenu({
3710
3715
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3711
3716
  /* @__PURE__ */ jsxs(CommandList, { children: [
3712
3717
  /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
3713
- groups.map((group, i) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
3718
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React35.Fragment, { children: [
3714
3719
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3715
3720
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3716
3721
  CommandItem,
@@ -3730,6 +3735,43 @@ function CommandMenu({
3730
3735
  ] })
3731
3736
  ] });
3732
3737
  }
3738
+ function DataView({
3739
+ items,
3740
+ getRowId,
3741
+ renderCard,
3742
+ columns,
3743
+ view,
3744
+ ready = true,
3745
+ gridColumns = 3,
3746
+ gap = "sm",
3747
+ empty,
3748
+ placeholder = null,
3749
+ searchKey,
3750
+ searchPlaceholder,
3751
+ pageSize = 25,
3752
+ emptyMessage,
3753
+ onRowClick,
3754
+ className
3755
+ }) {
3756
+ if (!ready) return /* @__PURE__ */ jsx(Fragment, { children: placeholder });
3757
+ if (items.length === 0 && empty) return /* @__PURE__ */ jsx(Fragment, { children: empty });
3758
+ if (view === "list") {
3759
+ return /* @__PURE__ */ jsx(
3760
+ DataTable,
3761
+ {
3762
+ columns,
3763
+ data: items,
3764
+ searchKey,
3765
+ searchPlaceholder,
3766
+ pageSize,
3767
+ emptyMessage,
3768
+ onRowClick,
3769
+ className
3770
+ }
3771
+ );
3772
+ }
3773
+ return /* @__PURE__ */ jsx(AppGrid, { columns: gridColumns, gap, className: cn$1(className), children: items.map((item) => /* @__PURE__ */ jsx(React35.Fragment, { children: renderCard(item) }, getRowId(item))) });
3774
+ }
3733
3775
  var TOKEN_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
3734
3776
  var seriesColor = (s, i) => s.color ?? TOKEN_COLORS[i % TOKEN_COLORS.length];
3735
3777
  function ChartTooltip({
@@ -3902,20 +3944,20 @@ function FileUpload({
3902
3944
  hint,
3903
3945
  className
3904
3946
  }) {
3905
- const [internalFiles, setInternalFiles] = React34.useState(defaultFiles ?? []);
3906
- const [isDragging, setIsDragging] = React34.useState(false);
3907
- const [validationError, setValidationError] = React34.useState(null);
3908
- const inputRef = React34.useRef(null);
3909
- const dragDepth = React34.useRef(0);
3947
+ const [internalFiles, setInternalFiles] = React35.useState(defaultFiles ?? []);
3948
+ const [isDragging, setIsDragging] = React35.useState(false);
3949
+ const [validationError, setValidationError] = React35.useState(null);
3950
+ const inputRef = React35.useRef(null);
3951
+ const dragDepth = React35.useRef(0);
3910
3952
  const files = filesProp ?? internalFiles;
3911
- const setFiles = React34.useCallback(
3953
+ const setFiles = React35.useCallback(
3912
3954
  (next) => {
3913
3955
  if (!filesProp) setInternalFiles(next);
3914
3956
  onFilesChange?.(next);
3915
3957
  },
3916
3958
  [filesProp, onFilesChange]
3917
3959
  );
3918
- const acceptList = React34.useMemo(
3960
+ const acceptList = React35.useMemo(
3919
3961
  () => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
3920
3962
  [accept]
3921
3963
  );
@@ -4295,6 +4337,63 @@ function ImageCard({
4295
4337
  }
4296
4338
  );
4297
4339
  }
4340
+ var bannerAspectClass = {
4341
+ wide: "aspect-[3/1]",
4342
+ video: "aspect-video",
4343
+ square: "aspect-square"
4344
+ };
4345
+ function MediaCard({
4346
+ banner,
4347
+ bannerAspect = "wide",
4348
+ footer,
4349
+ children,
4350
+ className,
4351
+ ...props
4352
+ }) {
4353
+ return /* @__PURE__ */ jsxs(
4354
+ Card$1,
4355
+ {
4356
+ className: cn$1("flex h-full w-full flex-col overflow-hidden", banner && "pt-0", className),
4357
+ ...props,
4358
+ children: [
4359
+ banner && /* @__PURE__ */ jsx("div", { className: cn$1("w-full shrink-0 overflow-hidden", bannerAspectClass[bannerAspect]), children: banner }),
4360
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3 p-4", children: [
4361
+ children,
4362
+ footer && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-1", children: footer })
4363
+ ] })
4364
+ ]
4365
+ }
4366
+ );
4367
+ }
4368
+ function ViewToggle({ view, onChange, labels, className, ...props }) {
4369
+ const options = [
4370
+ { mode: "cards", label: labels?.cards ?? "Card view", Icon: LayoutGrid },
4371
+ { mode: "list", label: labels?.list ?? "List view", Icon: List }
4372
+ ];
4373
+ return /* @__PURE__ */ jsx(
4374
+ "div",
4375
+ {
4376
+ className: cn$1("inline-flex items-center rounded-md border p-0.5", className),
4377
+ role: "group",
4378
+ "aria-label": "View mode",
4379
+ ...props,
4380
+ children: options.map(({ mode, label, Icon }) => /* @__PURE__ */ jsx(
4381
+ Button$1,
4382
+ {
4383
+ type: "button",
4384
+ size: "sm",
4385
+ variant: view === mode ? "secondary" : "ghost",
4386
+ className: "h-7 px-2",
4387
+ "aria-label": label,
4388
+ "aria-pressed": view === mode,
4389
+ onClick: () => onChange(mode),
4390
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-4" })
4391
+ },
4392
+ mode
4393
+ ))
4394
+ }
4395
+ );
4396
+ }
4298
4397
  var DASH_ON = "5px";
4299
4398
  var DASH_OFF = "4px";
4300
4399
  function FlowConnector({
@@ -4582,7 +4681,7 @@ function PageHeader({
4582
4681
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
4583
4682
  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) => {
4584
4683
  const isLast = i === breadcrumbs.length - 1;
4585
- return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
4684
+ return /* @__PURE__ */ jsxs(React35.Fragment, { children: [
4586
4685
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
4587
4686
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
4588
4687
  href: crumb.href,
@@ -4618,7 +4717,7 @@ function PageHeader({
4618
4717
  ] });
4619
4718
  }
4620
4719
  function Suspensed({ children, fallback }) {
4621
- return /* @__PURE__ */ jsx(React34.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4720
+ return /* @__PURE__ */ jsx(React35.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4622
4721
  }
4623
4722
  function getInitialTheme() {
4624
4723
  if (typeof document === "undefined") return "dark";
@@ -4626,7 +4725,7 @@ function getInitialTheme() {
4626
4725
  return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
4627
4726
  }
4628
4727
  function ThemeSwitchMinimal() {
4629
- const [theme, setThemeState] = React34.useState(getInitialTheme);
4728
+ const [theme, setThemeState] = React35.useState(getInitialTheme);
4630
4729
  const toggle = () => {
4631
4730
  const next = theme === "dark" ? "light" : "dark";
4632
4731
  setThemeState(next);
@@ -4752,9 +4851,9 @@ function getInitialColorName() {
4752
4851
  return localStorage.getItem(STORAGE_KEY) ?? "emerald";
4753
4852
  }
4754
4853
  function BrandColorSwitchMinimal() {
4755
- const [active, setActive] = React34.useState(getInitialColorName);
4756
- const [open, setOpen] = React34.useState(false);
4757
- React34.useEffect(() => {
4854
+ const [active, setActive] = React35.useState(getInitialColorName);
4855
+ const [open, setOpen] = React35.useState(false);
4856
+ React35.useEffect(() => {
4758
4857
  const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
4759
4858
  const color = BRAND_COLORS.find((c) => c.name === saved);
4760
4859
  if (color) applyColor(color);
@@ -4815,7 +4914,7 @@ function BrandColorSwitchMinimal() {
4815
4914
  ] });
4816
4915
  }
4817
4916
  function ThemeColorUpdater({ colorTheme }) {
4818
- React34.useEffect(() => {
4917
+ React35.useEffect(() => {
4819
4918
  if (colorTheme) {
4820
4919
  document.documentElement.setAttribute("data-theme", colorTheme);
4821
4920
  } else {
@@ -4825,7 +4924,7 @@ function ThemeColorUpdater({ colorTheme }) {
4825
4924
  return null;
4826
4925
  }
4827
4926
  function VersionBanner({ version, message, className, onDismiss }) {
4828
- const [dismissed, setDismissed] = React34.useState(false);
4927
+ const [dismissed, setDismissed] = React35.useState(false);
4829
4928
  if (dismissed) return null;
4830
4929
  return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
4831
4930
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -5082,29 +5181,29 @@ function useMounted() {
5082
5181
  return mounted;
5083
5182
  }
5084
5183
  function useConfirm() {
5085
- const [isOpen, setIsOpen] = React34.useState(false);
5086
- const [options, setOptions] = React34.useState({});
5087
- const resolveRef = React34.useRef(null);
5088
- const confirm = React34.useCallback((opts = {}) => {
5184
+ const [isOpen, setIsOpen] = React35.useState(false);
5185
+ const [options, setOptions] = React35.useState({});
5186
+ const resolveRef = React35.useRef(null);
5187
+ const confirm = React35.useCallback((opts = {}) => {
5089
5188
  setOptions(opts);
5090
5189
  setIsOpen(true);
5091
5190
  return new Promise((resolve) => {
5092
5191
  resolveRef.current = resolve;
5093
5192
  });
5094
5193
  }, []);
5095
- const handleConfirm = React34.useCallback(() => {
5194
+ const handleConfirm = React35.useCallback(() => {
5096
5195
  resolveRef.current?.(true);
5097
5196
  setIsOpen(false);
5098
5197
  }, []);
5099
- const handleCancel = React34.useCallback(() => {
5198
+ const handleCancel = React35.useCallback(() => {
5100
5199
  resolveRef.current?.(false);
5101
5200
  setIsOpen(false);
5102
5201
  }, []);
5103
5202
  return { confirm, isOpen, options, handleConfirm, handleCancel };
5104
5203
  }
5105
5204
  function useScrolledPast(threshold) {
5106
- const [scrolledPast, setScrolledPast] = React34.useState(false);
5107
- React34.useEffect(() => {
5205
+ const [scrolledPast, setScrolledPast] = React35.useState(false);
5206
+ React35.useEffect(() => {
5108
5207
  const handleScroll = () => {
5109
5208
  setScrolledPast(window.scrollY > threshold);
5110
5209
  };
@@ -5115,28 +5214,28 @@ function useScrolledPast(threshold) {
5115
5214
  return scrolledPast;
5116
5215
  }
5117
5216
  function useCopyToClipboard() {
5118
- const [copied, setCopied] = React34.useState(false);
5119
- const timeoutRef = React34.useRef(void 0);
5120
- const copy = React34.useCallback((text) => {
5217
+ const [copied, setCopied] = React35.useState(false);
5218
+ const timeoutRef = React35.useRef(void 0);
5219
+ const copy = React35.useCallback((text) => {
5121
5220
  navigator.clipboard.writeText(text).then(() => {
5122
5221
  setCopied(true);
5123
5222
  clearTimeout(timeoutRef.current);
5124
5223
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
5125
5224
  });
5126
5225
  }, []);
5127
- React34.useEffect(() => () => clearTimeout(timeoutRef.current), []);
5226
+ React35.useEffect(() => () => clearTimeout(timeoutRef.current), []);
5128
5227
  return [copied, copy];
5129
5228
  }
5130
5229
  function useDebounce(value, delay) {
5131
- const [debounced, setDebounced] = React34.useState(value);
5132
- React34.useEffect(() => {
5230
+ const [debounced, setDebounced] = React35.useState(value);
5231
+ React35.useEffect(() => {
5133
5232
  const timer = setTimeout(() => setDebounced(value), delay);
5134
5233
  return () => clearTimeout(timer);
5135
5234
  }, [value, delay]);
5136
5235
  return debounced;
5137
5236
  }
5138
5237
  function useLocalStorage(key, initialValue) {
5139
- const [storedValue, setStoredValue] = React34.useState(() => {
5238
+ const [storedValue, setStoredValue] = React35.useState(() => {
5140
5239
  if (typeof window === "undefined") return initialValue;
5141
5240
  try {
5142
5241
  const item = window.localStorage.getItem(key);
@@ -5145,7 +5244,7 @@ function useLocalStorage(key, initialValue) {
5145
5244
  return initialValue;
5146
5245
  }
5147
5246
  });
5148
- const setValue = React34.useCallback(
5247
+ const setValue = React35.useCallback(
5149
5248
  (value) => {
5150
5249
  try {
5151
5250
  setStoredValue(value);
@@ -5159,12 +5258,19 @@ function useLocalStorage(key, initialValue) {
5159
5258
  );
5160
5259
  return [storedValue, setValue];
5161
5260
  }
5261
+
5262
+ // src/hooks/use-view-mode.ts
5263
+ function useViewMode(key = "view-mode", fallback = "cards") {
5264
+ const [stored, setStored] = useLocalStorage(key, fallback);
5265
+ const mounted = useMounted();
5266
+ return { view: mounted ? stored : fallback, setView: setStored, ready: mounted };
5267
+ }
5162
5268
  function useMediaQuery(query) {
5163
- const [matches, setMatches] = React34.useState(() => {
5269
+ const [matches, setMatches] = React35.useState(() => {
5164
5270
  if (typeof window === "undefined") return false;
5165
5271
  return window.matchMedia(query).matches;
5166
5272
  });
5167
- React34.useEffect(() => {
5273
+ React35.useEffect(() => {
5168
5274
  if (typeof window === "undefined") return;
5169
5275
  const mql = window.matchMedia(query);
5170
5276
  setMatches(mql.matches);
@@ -5175,18 +5281,18 @@ function useMediaQuery(query) {
5175
5281
  return matches;
5176
5282
  }
5177
5283
  function usePagination(total, pageSize) {
5178
- const [page, setPage] = React34.useState(1);
5284
+ const [page, setPage] = React35.useState(1);
5179
5285
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
5180
5286
  const offset = (page - 1) * pageSize;
5181
- const goTo = React34.useCallback(
5287
+ const goTo = React35.useCallback(
5182
5288
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
5183
5289
  [totalPages]
5184
5290
  );
5185
- const next = React34.useCallback(() => goTo(page + 1), [page, goTo]);
5186
- const prev = React34.useCallback(() => goTo(page - 1), [page, goTo]);
5291
+ const next = React35.useCallback(() => goTo(page + 1), [page, goTo]);
5292
+ const prev = React35.useCallback(() => goTo(page - 1), [page, goTo]);
5187
5293
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
5188
5294
  }
5189
5295
 
5190
- export { ActivityFeed, AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, Carousel, ChangelogCard, ChangelogList, Chart, Checkbox, CommandMenu, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, CtaSection, DataTable, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, NotificationToast, NotificationsPopover, OlwibaUIProvider, OnboardingWizard, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, QualificationSection, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, SettingsSection, Sortable, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, StepsSection, Suspensed, Switch, TeamMembersPanel, TeamSection, TechStackSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpdateBanner, UpgradePrompt, VersionBanner, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
5296
+ 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 };
5191
5297
  //# sourceMappingURL=index.js.map
5192
5298
  //# sourceMappingURL=index.js.map