@olwiba/ui 0.2.6 → 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
  };
@@ -531,7 +534,7 @@ function DefaultForm({
531
534
  const isForgotPassword = mode === "forgot-password";
532
535
  const isResetPassword = mode === "reset-password";
533
536
  const isVerify = mode === "verify";
534
- const [code, setCode] = React34.useState("");
537
+ const [code, setCode] = React35.useState("");
535
538
  const hasPrefill = !!(defaultEmail || defaultPassword);
536
539
  const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
537
540
  const copy = authCopy[mode];
@@ -691,10 +694,10 @@ function DataTable({
691
694
  emptyMessage = "No results.",
692
695
  className
693
696
  }) {
694
- const [sorting, setSorting] = React34.useState([]);
695
- const [rowSelection, setRowSelection] = React34.useState({});
696
- const [globalFilter, setGlobalFilter] = React34.useState("");
697
- 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(
698
701
  () => selectable ? [selectColumn, ...columns] : columns,
699
702
  [selectable, columns]
700
703
  );
@@ -716,7 +719,7 @@ function DataTable({
716
719
  initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
717
720
  enableRowSelection: selectable
718
721
  });
719
- React34.useEffect(() => {
722
+ React35.useEffect(() => {
720
723
  if (!onSelectionChange) return;
721
724
  onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
722
725
  }, [rowSelection]);
@@ -796,8 +799,8 @@ function DataTable({
796
799
  ] });
797
800
  }
798
801
  function InviteDialog({ roles, onInvite, onClose }) {
799
- const [email, setEmail] = React34.useState("");
800
- 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]);
801
804
  return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
802
805
  "form",
803
806
  {
@@ -841,8 +844,8 @@ function TeamMembersPanel({
841
844
  title = "Team members",
842
845
  description = "Manage who has access to this workspace."
843
846
  }) {
844
- const [inviteOpen, setInviteOpen] = React34.useState(false);
845
- const columns = React34.useMemo(() => [
847
+ const [inviteOpen, setInviteOpen] = React35.useState(false);
848
+ const columns = React35.useMemo(() => [
846
849
  {
847
850
  accessorKey: "name",
848
851
  header: "Name",
@@ -911,7 +914,7 @@ function BillingPanel({
911
914
  onUpdatePaymentMethod,
912
915
  className
913
916
  }) {
914
- const invoiceColumns = React34.useMemo(() => [
917
+ const invoiceColumns = React35.useMemo(() => [
915
918
  { accessorKey: "date", header: "Date", enableSorting: false },
916
919
  { accessorKey: "amount", header: "Amount", enableSorting: false },
917
920
  {
@@ -990,9 +993,9 @@ function OnboardingWizard({
990
993
  completeLabel = "Finish",
991
994
  className
992
995
  }) {
993
- const [internalStep, setInternalStep] = React34.useState(0);
994
- const [error, setError] = React34.useState(null);
995
- 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);
996
999
  const index = stepProp ?? internalStep;
997
1000
  const current = steps[index];
998
1001
  const isLast = index === steps.length - 1;
@@ -1322,10 +1325,10 @@ function UpdateBanner({
1322
1325
  buttonLabel = "Refresh now",
1323
1326
  className
1324
1327
  }) {
1325
- const [show, setShow] = React34.useState(forceShow);
1326
- const [entered, setEntered] = React34.useState(false);
1327
- const [refreshing, setRefreshing] = React34.useState(false);
1328
- 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(() => {
1329
1332
  if (forceShow) {
1330
1333
  setShow(true);
1331
1334
  return;
@@ -1348,7 +1351,7 @@ function UpdateBanner({
1348
1351
  clearInterval(id);
1349
1352
  };
1350
1353
  }, [currentVersion, fetchVersion, forceShow, intervalMs]);
1351
- React34.useEffect(() => {
1354
+ React35.useEffect(() => {
1352
1355
  if (!show) {
1353
1356
  setEntered(false);
1354
1357
  return;
@@ -1421,9 +1424,9 @@ function FadeIn({
1421
1424
  style,
1422
1425
  ...props
1423
1426
  }) {
1424
- const ref = React34.useRef(null);
1425
- const [visible, setVisible] = React34.useState(false);
1426
- React34.useEffect(() => {
1427
+ const ref = React35.useRef(null);
1428
+ const [visible, setVisible] = React35.useState(false);
1429
+ React35.useEffect(() => {
1427
1430
  const el = ref.current;
1428
1431
  if (!el) return;
1429
1432
  const observer = new IntersectionObserver(
@@ -1618,16 +1621,16 @@ function Carousel({
1618
1621
  className
1619
1622
  }) {
1620
1623
  const mode = useUIVariant();
1621
- const trackRef = React34.useRef(null);
1622
- const [canPrev, setCanPrev] = React34.useState(false);
1623
- const [canNext, setCanNext] = React34.useState(true);
1624
- 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(() => {
1625
1628
  const track = trackRef.current;
1626
1629
  if (!track) return;
1627
1630
  setCanPrev(track.scrollLeft > 8);
1628
1631
  setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1629
1632
  }, []);
1630
- React34.useEffect(() => {
1633
+ React35.useEffect(() => {
1631
1634
  const track = trackRef.current;
1632
1635
  if (!track) return;
1633
1636
  updateScrollState();
@@ -1684,7 +1687,7 @@ function Carousel({
1684
1687
  role: "region",
1685
1688
  "aria-label": ariaLabel,
1686
1689
  className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1687
- children: React34.Children.map(
1690
+ children: React35.Children.map(
1688
1691
  children,
1689
1692
  (child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
1690
1693
  )
@@ -1709,9 +1712,9 @@ function StaggerChildren({
1709
1712
  className,
1710
1713
  ...props
1711
1714
  }) {
1712
- const ref = React34.useRef(null);
1713
- const [visible, setVisible] = React34.useState(false);
1714
- React34.useEffect(() => {
1715
+ const ref = React35.useRef(null);
1716
+ const [visible, setVisible] = React35.useState(false);
1717
+ React35.useEffect(() => {
1715
1718
  const el = ref.current;
1716
1719
  if (!el) return;
1717
1720
  const observer = new IntersectionObserver(
@@ -1729,7 +1732,7 @@ function StaggerChildren({
1729
1732
  return () => observer.disconnect();
1730
1733
  }, [once]);
1731
1734
  const [tx, ty] = translateMap2[direction];
1732
- 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(
1733
1736
  "div",
1734
1737
  {
1735
1738
  className: "h-full",
@@ -1871,7 +1874,7 @@ function StepsSection({
1871
1874
  /* @__PURE__ */ jsx("div", { className: "mt-12", children: resolvedGroups.map((group, groupIndex) => {
1872
1875
  const startNumber = nextNumber;
1873
1876
  nextNumber += group.steps.length;
1874
- return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
1877
+ return /* @__PURE__ */ jsxs(React35.Fragment, { children: [
1875
1878
  /* @__PURE__ */ jsx("div", { className: cn$1(groupIndex > 0 && "mt-10"), children: /* @__PURE__ */ jsx(TimelineGroup, { steps: group.steps, startNumber, fadeIn: groupIndex > 0 }) }),
1876
1879
  group.after && /* @__PURE__ */ jsx("div", { className: "mt-16", children: group.after })
1877
1880
  ] }, groupIndex);
@@ -2041,9 +2044,9 @@ function FeatureMarqueeSection({
2041
2044
  ] });
2042
2045
  }
2043
2046
  function useIntersectionObserver(options) {
2044
- const ref = React34.useRef(null);
2045
- const [isIntersecting, setIsIntersecting] = React34.useState(false);
2046
- React34.useEffect(() => {
2047
+ const ref = React35.useRef(null);
2048
+ const [isIntersecting, setIsIntersecting] = React35.useState(false);
2049
+ React35.useEffect(() => {
2047
2050
  const el = ref.current;
2048
2051
  if (!el) return;
2049
2052
  const observer = new IntersectionObserver(([entry]) => {
@@ -2065,8 +2068,8 @@ function ShowcaseCta({
2065
2068
  sectionClasses
2066
2069
  }) {
2067
2070
  const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
2068
- const [visible, setVisible] = React34.useState(false);
2069
- React34.useEffect(() => {
2071
+ const [visible, setVisible] = React35.useState(false);
2072
+ React35.useEffect(() => {
2070
2073
  if (intersecting) setVisible(true);
2071
2074
  }, [intersecting]);
2072
2075
  return /* @__PURE__ */ jsxs(
@@ -2243,8 +2246,8 @@ function CountdownTimer({
2243
2246
  className,
2244
2247
  compact = false
2245
2248
  }) {
2246
- const [timeLeft, setTimeLeft] = React34.useState(() => getTimeLeft(deadline));
2247
- React34.useEffect(() => {
2249
+ const [timeLeft, setTimeLeft] = React35.useState(() => getTimeLeft(deadline));
2250
+ React35.useEffect(() => {
2248
2251
  setTimeLeft(getTimeLeft(deadline));
2249
2252
  const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
2250
2253
  return () => clearInterval(id);
@@ -2270,7 +2273,7 @@ function CountdownTimer({
2270
2273
  ];
2271
2274
  return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
2272
2275
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
2273
- /* @__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: [
2274
2277
  /* @__PURE__ */ jsxs("span", { children: [
2275
2278
  String(value).padStart(2, "0"),
2276
2279
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
@@ -2295,7 +2298,7 @@ function PricingSection({
2295
2298
  highlightedBadgeLabel = "Founding member",
2296
2299
  renderPlanFooter
2297
2300
  }) {
2298
- const [annual, setAnnual] = React34.useState(false);
2301
+ const [annual, setAnnual] = React35.useState(false);
2299
2302
  const isOneTime = mode === "one-time";
2300
2303
  const uiMode = useUIVariant();
2301
2304
  const sectionClasses = cn$1(
@@ -2564,10 +2567,10 @@ function CountUp({
2564
2567
  className,
2565
2568
  ...props
2566
2569
  }) {
2567
- const ref = React34.useRef(null);
2568
- const [value, setValue] = React34.useState(from);
2569
- const hasStarted = React34.useRef(false);
2570
- React34.useEffect(() => {
2570
+ const ref = React35.useRef(null);
2571
+ const [value, setValue] = React35.useState(from);
2572
+ const hasStarted = React35.useRef(false);
2573
+ React35.useEffect(() => {
2571
2574
  const el = ref.current;
2572
2575
  if (!el) return;
2573
2576
  const observer = new IntersectionObserver(
@@ -2660,8 +2663,8 @@ function NewsletterSection({
2660
2663
  successDescription = defaults.successDescription,
2661
2664
  onSubscribe
2662
2665
  } = {}) {
2663
- const [email, setEmail] = React34.useState("");
2664
- const [submitted, setSubmitted] = React34.useState(false);
2666
+ const [email, setEmail] = React35.useState("");
2667
+ const [submitted, setSubmitted] = React35.useState(false);
2665
2668
  const mode = useUIVariant();
2666
2669
  const sectionClasses = cn$1(
2667
2670
  "overflow-hidden bg-card",
@@ -2752,7 +2755,7 @@ function ContactSection({
2752
2755
  sendAnotherLabel = defaults2.sendAnotherLabel,
2753
2756
  onSubmit
2754
2757
  } = {}) {
2755
- const [submitted, setSubmitted] = React34.useState(false);
2758
+ const [submitted, setSubmitted] = React35.useState(false);
2756
2759
  const mode = useUIVariant();
2757
2760
  const sectionClasses = cn$1(
2758
2761
  "overflow-hidden bg-card",
@@ -3050,9 +3053,10 @@ function Navbar({
3050
3053
  controls,
3051
3054
  renderLink = defaultRenderLink7
3052
3055
  }) {
3053
- const [open, setOpen] = React34.useState(false);
3056
+ const [open, setOpen] = React35.useState(false);
3054
3057
  const brandHref = brand.href ?? "/";
3055
3058
  const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
3059
+ const closeMobileMenu = React35.useCallback(() => setOpen(false), []);
3056
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: [
3057
3061
  renderLink({
3058
3062
  href: brandHref,
@@ -3067,7 +3071,7 @@ function Navbar({
3067
3071
  children: link.label
3068
3072
  }) }, link.label)) }),
3069
3073
  hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
3070
- controls?.map((control, i) => /* @__PURE__ */ jsx(React34.Fragment, { children: control }, i)),
3074
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React35.Fragment, { children: control }, i)),
3071
3075
  (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
3072
3076
  cta?.secondary && renderLink({
3073
3077
  href: cta.secondary.href,
@@ -3084,29 +3088,29 @@ function Navbar({
3084
3088
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Open menu" })
3085
3089
  ] }) }),
3086
3090
  /* @__PURE__ */ jsxs(SheetContent, { side: "left", className: "w-72", children: [
3087
- /* @__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({
3088
3092
  href: brandHref,
3089
3093
  children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 font-semibold", children: [
3090
3094
  brand.logo,
3091
3095
  /* @__PURE__ */ jsx("span", { children: brand.name })
3092
3096
  ] })
3093
3097
  }) }) }),
3094
- 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,
3095
3099
  /* @__PURE__ */ jsx(Separator, {}),
3096
- /* @__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({
3097
3101
  href: link.href,
3098
3102
  className: "block rounded-md px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
3099
3103
  children: link.label
3100
3104
  }) }, link.label)) }),
3101
3105
  /* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-col gap-2", children: [
3102
- cta?.secondary && renderLink({
3106
+ cta?.secondary && /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, children: renderLink({
3103
3107
  href: cta.secondary.href,
3104
3108
  children: /* @__PURE__ */ jsx(Button$1, { variant: "outline", className: "w-full", children: cta.secondary.label })
3105
- }),
3106
- cta?.primary && renderLink({
3109
+ }) }),
3110
+ cta?.primary && /* @__PURE__ */ jsx("span", { onClickCapture: closeMobileMenu, children: renderLink({
3107
3111
  href: cta.primary.href,
3108
3112
  children: /* @__PURE__ */ jsx(Button$1, { className: "w-full", children: cta.primary.label })
3109
- })
3113
+ }) })
3110
3114
  ] })
3111
3115
  ] })
3112
3116
  ] })
@@ -3240,7 +3244,7 @@ function LogoStrip({
3240
3244
  ] });
3241
3245
  }
3242
3246
  function GridUnderlay({ variant, size = 24, className, ...props }) {
3243
- const id = React34.useId();
3247
+ const id = React35.useId();
3244
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: [
3245
3249
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
3246
3250
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -3268,9 +3272,9 @@ function GlowUnderlay({
3268
3272
  style,
3269
3273
  ...props
3270
3274
  }) {
3271
- const [pos, setPos] = React34.useState({ x: "50%", y: "50%" });
3272
- const containerRef = React34.useRef(null);
3273
- React34.useEffect(() => {
3275
+ const [pos, setPos] = React35.useState({ x: "50%", y: "50%" });
3276
+ const containerRef = React35.useRef(null);
3277
+ React35.useEffect(() => {
3274
3278
  if (!followCursor) return;
3275
3279
  const handleMouseMove = (e) => {
3276
3280
  const el = containerRef.current;
@@ -3409,8 +3413,8 @@ function PageTransition({
3409
3413
  style,
3410
3414
  ...props
3411
3415
  }) {
3412
- const [mounted, setMounted] = React34.useState(false);
3413
- React34.useEffect(() => {
3416
+ const [mounted, setMounted] = React35.useState(false);
3417
+ React35.useEffect(() => {
3414
3418
  const id = requestAnimationFrame(() => setMounted(true));
3415
3419
  return () => cancelAnimationFrame(id);
3416
3420
  }, []);
@@ -3485,7 +3489,7 @@ function Sortable({
3485
3489
  if (oldIndex === -1 || newIndex === -1) return;
3486
3490
  onReorder(arrayMove(items, oldIndex, newIndex));
3487
3491
  };
3488
- const childArray = React34.Children.toArray(children);
3492
+ const childArray = React35.Children.toArray(children);
3489
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(
3490
3494
  (child, i) => items[i] === void 0 ? null : /* @__PURE__ */ jsx(SortableItem, { id: items[i], disabled, className: itemClassName, children: child }, items[i])
3491
3495
  ) }) }) });
@@ -3497,8 +3501,8 @@ function Spotlight({
3497
3501
  triggerClassName,
3498
3502
  shortcut = "\u2318K"
3499
3503
  }) {
3500
- const [open, setOpen] = React34.useState(false);
3501
- React34.useEffect(() => {
3504
+ const [open, setOpen] = React35.useState(false);
3505
+ React35.useEffect(() => {
3502
3506
  const down = (e) => {
3503
3507
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
3504
3508
  e.preventDefault();
@@ -3528,7 +3532,7 @@ function Spotlight({
3528
3532
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3529
3533
  /* @__PURE__ */ jsxs(CommandList, { children: [
3530
3534
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
3531
- groups.map((group, i) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
3535
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React35.Fragment, { children: [
3532
3536
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3533
3537
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3534
3538
  CommandItem,
@@ -3553,8 +3557,8 @@ function Spotlight({
3553
3557
  var BASE = 40;
3554
3558
  var MAGNIFY_RADIUS = 80;
3555
3559
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
3556
- const [mouseX, setMouseX] = React34.useState(null);
3557
- const containerRef = React34.useRef(null);
3560
+ const [mouseX, setMouseX] = React35.useState(null);
3561
+ const containerRef = React35.useRef(null);
3558
3562
  function getScale(index) {
3559
3563
  if (mouseX === null || !containerRef.current) return 1;
3560
3564
  const container = containerRef.current;
@@ -3653,7 +3657,7 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
3653
3657
  ] }) });
3654
3658
  }
3655
3659
  function RegisterHotkeys({ hotkeys }) {
3656
- React34.useEffect(() => {
3660
+ React35.useEffect(() => {
3657
3661
  const handleKeyDown = (event) => {
3658
3662
  for (const hotkey of hotkeys) {
3659
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;
@@ -3670,10 +3674,10 @@ function RegisterHotkeys({ hotkeys }) {
3670
3674
  return null;
3671
3675
  }
3672
3676
  function useControlledOpen(defaultOpen = false) {
3673
- const [isOpen, setIsOpen] = React34.useState(defaultOpen);
3674
- const open = React34.useCallback(() => setIsOpen(true), []);
3675
- const close = React34.useCallback(() => setIsOpen(false), []);
3676
- 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), []);
3677
3681
  return { isOpen, open, close, toggle, setIsOpen };
3678
3682
  }
3679
3683
  function CommandMenu({
@@ -3686,7 +3690,7 @@ function CommandMenu({
3686
3690
  }) {
3687
3691
  const internal = useControlledOpen(false);
3688
3692
  const isOpen = openProp ?? internal.isOpen;
3689
- const setOpen = React34.useCallback(
3693
+ const setOpen = React35.useCallback(
3690
3694
  (next) => {
3691
3695
  if (openProp === void 0) internal.setIsOpen(next);
3692
3696
  onOpenChange?.(next);
@@ -3711,7 +3715,7 @@ function CommandMenu({
3711
3715
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3712
3716
  /* @__PURE__ */ jsxs(CommandList, { children: [
3713
3717
  /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
3714
- groups.map((group, i) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
3718
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React35.Fragment, { children: [
3715
3719
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3716
3720
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3717
3721
  CommandItem,
@@ -3731,6 +3735,43 @@ function CommandMenu({
3731
3735
  ] })
3732
3736
  ] });
3733
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
+ }
3734
3775
  var TOKEN_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
3735
3776
  var seriesColor = (s, i) => s.color ?? TOKEN_COLORS[i % TOKEN_COLORS.length];
3736
3777
  function ChartTooltip({
@@ -3903,20 +3944,20 @@ function FileUpload({
3903
3944
  hint,
3904
3945
  className
3905
3946
  }) {
3906
- const [internalFiles, setInternalFiles] = React34.useState(defaultFiles ?? []);
3907
- const [isDragging, setIsDragging] = React34.useState(false);
3908
- const [validationError, setValidationError] = React34.useState(null);
3909
- const inputRef = React34.useRef(null);
3910
- 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);
3911
3952
  const files = filesProp ?? internalFiles;
3912
- const setFiles = React34.useCallback(
3953
+ const setFiles = React35.useCallback(
3913
3954
  (next) => {
3914
3955
  if (!filesProp) setInternalFiles(next);
3915
3956
  onFilesChange?.(next);
3916
3957
  },
3917
3958
  [filesProp, onFilesChange]
3918
3959
  );
3919
- const acceptList = React34.useMemo(
3960
+ const acceptList = React35.useMemo(
3920
3961
  () => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
3921
3962
  [accept]
3922
3963
  );
@@ -4296,6 +4337,63 @@ function ImageCard({
4296
4337
  }
4297
4338
  );
4298
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
+ }
4299
4397
  var DASH_ON = "5px";
4300
4398
  var DASH_OFF = "4px";
4301
4399
  function FlowConnector({
@@ -4583,7 +4681,7 @@ function PageHeader({
4583
4681
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
4584
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) => {
4585
4683
  const isLast = i === breadcrumbs.length - 1;
4586
- return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
4684
+ return /* @__PURE__ */ jsxs(React35.Fragment, { children: [
4587
4685
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
4588
4686
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
4589
4687
  href: crumb.href,
@@ -4619,7 +4717,7 @@ function PageHeader({
4619
4717
  ] });
4620
4718
  }
4621
4719
  function Suspensed({ children, fallback }) {
4622
- return /* @__PURE__ */ jsx(React34.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4720
+ return /* @__PURE__ */ jsx(React35.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4623
4721
  }
4624
4722
  function getInitialTheme() {
4625
4723
  if (typeof document === "undefined") return "dark";
@@ -4627,7 +4725,7 @@ function getInitialTheme() {
4627
4725
  return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
4628
4726
  }
4629
4727
  function ThemeSwitchMinimal() {
4630
- const [theme, setThemeState] = React34.useState(getInitialTheme);
4728
+ const [theme, setThemeState] = React35.useState(getInitialTheme);
4631
4729
  const toggle = () => {
4632
4730
  const next = theme === "dark" ? "light" : "dark";
4633
4731
  setThemeState(next);
@@ -4753,9 +4851,9 @@ function getInitialColorName() {
4753
4851
  return localStorage.getItem(STORAGE_KEY) ?? "emerald";
4754
4852
  }
4755
4853
  function BrandColorSwitchMinimal() {
4756
- const [active, setActive] = React34.useState(getInitialColorName);
4757
- const [open, setOpen] = React34.useState(false);
4758
- React34.useEffect(() => {
4854
+ const [active, setActive] = React35.useState(getInitialColorName);
4855
+ const [open, setOpen] = React35.useState(false);
4856
+ React35.useEffect(() => {
4759
4857
  const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
4760
4858
  const color = BRAND_COLORS.find((c) => c.name === saved);
4761
4859
  if (color) applyColor(color);
@@ -4816,7 +4914,7 @@ function BrandColorSwitchMinimal() {
4816
4914
  ] });
4817
4915
  }
4818
4916
  function ThemeColorUpdater({ colorTheme }) {
4819
- React34.useEffect(() => {
4917
+ React35.useEffect(() => {
4820
4918
  if (colorTheme) {
4821
4919
  document.documentElement.setAttribute("data-theme", colorTheme);
4822
4920
  } else {
@@ -4826,7 +4924,7 @@ function ThemeColorUpdater({ colorTheme }) {
4826
4924
  return null;
4827
4925
  }
4828
4926
  function VersionBanner({ version, message, className, onDismiss }) {
4829
- const [dismissed, setDismissed] = React34.useState(false);
4927
+ const [dismissed, setDismissed] = React35.useState(false);
4830
4928
  if (dismissed) return null;
4831
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: [
4832
4930
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -5083,29 +5181,29 @@ function useMounted() {
5083
5181
  return mounted;
5084
5182
  }
5085
5183
  function useConfirm() {
5086
- const [isOpen, setIsOpen] = React34.useState(false);
5087
- const [options, setOptions] = React34.useState({});
5088
- const resolveRef = React34.useRef(null);
5089
- 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 = {}) => {
5090
5188
  setOptions(opts);
5091
5189
  setIsOpen(true);
5092
5190
  return new Promise((resolve) => {
5093
5191
  resolveRef.current = resolve;
5094
5192
  });
5095
5193
  }, []);
5096
- const handleConfirm = React34.useCallback(() => {
5194
+ const handleConfirm = React35.useCallback(() => {
5097
5195
  resolveRef.current?.(true);
5098
5196
  setIsOpen(false);
5099
5197
  }, []);
5100
- const handleCancel = React34.useCallback(() => {
5198
+ const handleCancel = React35.useCallback(() => {
5101
5199
  resolveRef.current?.(false);
5102
5200
  setIsOpen(false);
5103
5201
  }, []);
5104
5202
  return { confirm, isOpen, options, handleConfirm, handleCancel };
5105
5203
  }
5106
5204
  function useScrolledPast(threshold) {
5107
- const [scrolledPast, setScrolledPast] = React34.useState(false);
5108
- React34.useEffect(() => {
5205
+ const [scrolledPast, setScrolledPast] = React35.useState(false);
5206
+ React35.useEffect(() => {
5109
5207
  const handleScroll = () => {
5110
5208
  setScrolledPast(window.scrollY > threshold);
5111
5209
  };
@@ -5116,28 +5214,28 @@ function useScrolledPast(threshold) {
5116
5214
  return scrolledPast;
5117
5215
  }
5118
5216
  function useCopyToClipboard() {
5119
- const [copied, setCopied] = React34.useState(false);
5120
- const timeoutRef = React34.useRef(void 0);
5121
- 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) => {
5122
5220
  navigator.clipboard.writeText(text).then(() => {
5123
5221
  setCopied(true);
5124
5222
  clearTimeout(timeoutRef.current);
5125
5223
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
5126
5224
  });
5127
5225
  }, []);
5128
- React34.useEffect(() => () => clearTimeout(timeoutRef.current), []);
5226
+ React35.useEffect(() => () => clearTimeout(timeoutRef.current), []);
5129
5227
  return [copied, copy];
5130
5228
  }
5131
5229
  function useDebounce(value, delay) {
5132
- const [debounced, setDebounced] = React34.useState(value);
5133
- React34.useEffect(() => {
5230
+ const [debounced, setDebounced] = React35.useState(value);
5231
+ React35.useEffect(() => {
5134
5232
  const timer = setTimeout(() => setDebounced(value), delay);
5135
5233
  return () => clearTimeout(timer);
5136
5234
  }, [value, delay]);
5137
5235
  return debounced;
5138
5236
  }
5139
5237
  function useLocalStorage(key, initialValue) {
5140
- const [storedValue, setStoredValue] = React34.useState(() => {
5238
+ const [storedValue, setStoredValue] = React35.useState(() => {
5141
5239
  if (typeof window === "undefined") return initialValue;
5142
5240
  try {
5143
5241
  const item = window.localStorage.getItem(key);
@@ -5146,7 +5244,7 @@ function useLocalStorage(key, initialValue) {
5146
5244
  return initialValue;
5147
5245
  }
5148
5246
  });
5149
- const setValue = React34.useCallback(
5247
+ const setValue = React35.useCallback(
5150
5248
  (value) => {
5151
5249
  try {
5152
5250
  setStoredValue(value);
@@ -5160,12 +5258,19 @@ function useLocalStorage(key, initialValue) {
5160
5258
  );
5161
5259
  return [storedValue, setValue];
5162
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
+ }
5163
5268
  function useMediaQuery(query) {
5164
- const [matches, setMatches] = React34.useState(() => {
5269
+ const [matches, setMatches] = React35.useState(() => {
5165
5270
  if (typeof window === "undefined") return false;
5166
5271
  return window.matchMedia(query).matches;
5167
5272
  });
5168
- React34.useEffect(() => {
5273
+ React35.useEffect(() => {
5169
5274
  if (typeof window === "undefined") return;
5170
5275
  const mql = window.matchMedia(query);
5171
5276
  setMatches(mql.matches);
@@ -5176,18 +5281,18 @@ function useMediaQuery(query) {
5176
5281
  return matches;
5177
5282
  }
5178
5283
  function usePagination(total, pageSize) {
5179
- const [page, setPage] = React34.useState(1);
5284
+ const [page, setPage] = React35.useState(1);
5180
5285
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
5181
5286
  const offset = (page - 1) * pageSize;
5182
- const goTo = React34.useCallback(
5287
+ const goTo = React35.useCallback(
5183
5288
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
5184
5289
  [totalPages]
5185
5290
  );
5186
- const next = React34.useCallback(() => goTo(page + 1), [page, goTo]);
5187
- 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]);
5188
5293
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
5189
5294
  }
5190
5295
 
5191
- 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 };
5192
5297
  //# sourceMappingURL=index.js.map
5193
5298
  //# sourceMappingURL=index.js.map