@hexclave/dashboard-ui-components 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import * as React$1 from "react";
2
2
  import * as RechartsPrimitive from "recharts";
3
3
 
4
4
  //#region src/components/chart-legend.d.ts
5
- declare const DesignChartLegendContent: React$1.ForwardRefExoticComponent<Omit<React$1.ClassAttributes<HTMLDivElement> & React$1.HTMLAttributes<HTMLDivElement> & Pick<RechartsPrimitive.LegendProps, "verticalAlign" | "payload"> & {
5
+ declare const DesignChartLegendContent: React$1.ForwardRefExoticComponent<Omit<React$1.ClassAttributes<HTMLDivElement> & React$1.HTMLAttributes<HTMLDivElement> & Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
6
6
  hideIcon?: boolean;
7
7
  nameKey?: string;
8
8
  }, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"pill-toggle.d.ts","names":[],"sources":["../../../src/components/pill-toggle.tsx"],"mappings":";;;KAQK,oBAAA;AAAA,KACA,wBAAA;AAAA,KAEO,sBAAA;EACV,EAAA;EACA,KAAA;EACA,IAAA,GAAO,KAAA,CAAM,WAAA;AAAA;AAAA,KAGH,qBAAA;EACV,OAAA,EAAS,sBAAA;EACT,QAAA;EACA,QAAA,GAAW,EAAA,oBAAsB,OAAA;EACjC,IAAA,GAAO,oBAAA;EACP,YAAA;EACA,QAAA,GAAW,wBAAA,EAZqB;EAchC,UAAA;EACA,SAAA;AAAA;AAAA,iBA+Bc,gBAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA;EACA,YAAA,EAAc,gBAAA;EACd,QAAA;EACA,UAAA;EACA;AAAA,GACC,qBAAA,GAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"pill-toggle.d.ts","names":[],"sources":["../../../src/components/pill-toggle.tsx"],"mappings":";;;KAOK,oBAAA;AAAA,KACA,wBAAA;AAAA,KAEO,sBAAA;EACV,EAAA;EACA,KAAA;EACA,IAAA,GAAO,KAAA,CAAM,WAAA;AAAA;AAAA,KAGH,qBAAA;EACV,OAAA,EAAS,sBAAA;EACT,QAAA;EACA,QAAA,GAAW,EAAA,oBAAsB,OAAA;EACjC,IAAA,GAAO,oBAAA;EACP,YAAA;EACA,QAAA,GAAW,wBAAA,EAZqB;EAchC,UAAA;EACA,SAAA;AAAA;AAAA,iBAuCc,gBAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA;EACA,YAAA,EAAc,gBAAA;EACd,QAAA;EACA,UAAA;EACA;AAAA,GACC,qBAAA,GAAqB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1,24 +1,27 @@
1
1
  "use client";
2
2
 
3
- import { Spinner, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn } from "@hexclave/ui";
3
+ import { Spinner, Tooltip, TooltipContent, TooltipPortal, TooltipProvider, TooltipTrigger, cn } from "@hexclave/ui";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { useState } from "react";
5
+ import { useEffect, useRef, useState } from "react";
6
6
  import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
7
- import { TooltipPortal } from "@radix-ui/react-tooltip";
8
7
  import { useGlassmorphicDefault } from "./card.js";
9
8
 
10
9
  //#region src/components/pill-toggle.tsx
10
+ const sliderTransition = "transform 200ms ease-out, width 200ms ease-out";
11
11
  const sizeClasses = new Map([
12
12
  ["sm", {
13
13
  button: "px-3 py-1.5 text-xs",
14
+ iconOnlyButton: "h-7 w-7 text-xs",
14
15
  icon: "h-3.5 w-3.5"
15
16
  }],
16
17
  ["md", {
17
18
  button: "px-4 py-2 text-sm",
19
+ iconOnlyButton: "h-9 w-9 text-sm",
18
20
  icon: "h-4 w-4"
19
21
  }],
20
22
  ["lg", {
21
23
  button: "px-5 py-2.5 text-sm",
24
+ iconOnlyButton: "h-10 w-10 text-sm",
22
25
  icon: "h-4 w-4"
23
26
  }]
24
27
  ]);
@@ -40,6 +43,10 @@ function DesignPillToggle({ options, selected, onSelect, size = "md", glassmorph
40
43
  const sizeClass = getMapValueOrThrow(sizeClasses, size, "sizeClasses");
41
44
  const activeRingClass = getMapValueOrThrow(gradientClasses, gradient, "gradientClasses");
42
45
  const [loadingOptionId, setLoadingOptionId] = useState(null);
46
+ const [sliderMetrics, setSliderMetrics] = useState(null);
47
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
48
+ const toggleRef = useRef(null);
49
+ const optionRefs = useRef(/* @__PURE__ */ new Map());
43
50
  const handleClick = (optionId) => {
44
51
  const result = onSelect(optionId);
45
52
  if (result && typeof result.then === "function") {
@@ -47,16 +54,57 @@ function DesignPillToggle({ options, selected, onSelect, size = "md", glassmorph
47
54
  runAsynchronouslyWithAlert(Promise.resolve(result).finally(() => setLoadingOptionId(null)));
48
55
  }
49
56
  };
50
- return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx("div", {
51
- className: cn("inline-flex items-center gap-1 p-1 rounded-xl", glassmorphic ? "bg-foreground/[0.04] backdrop-blur-sm" : "bg-black/[0.08] dark:bg-white/[0.04]", className),
52
- children: options.map((option) => {
57
+ useEffect(() => {
58
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
59
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
60
+ const updatePrefersReducedMotion = () => setPrefersReducedMotion(mediaQuery.matches);
61
+ updatePrefersReducedMotion();
62
+ mediaQuery.addEventListener("change", updatePrefersReducedMotion);
63
+ return () => mediaQuery.removeEventListener("change", updatePrefersReducedMotion);
64
+ }, []);
65
+ useEffect(() => {
66
+ const toggle = toggleRef.current;
67
+ const selectedButton = optionRefs.current.get(selected);
68
+ if (!toggle || !selectedButton) {
69
+ setSliderMetrics(null);
70
+ return;
71
+ }
72
+ const updateSliderMetrics = () => {
73
+ setSliderMetrics({
74
+ left: selectedButton.offsetLeft,
75
+ width: selectedButton.offsetWidth
76
+ });
77
+ };
78
+ updateSliderMetrics();
79
+ if (typeof ResizeObserver === "undefined") return;
80
+ const resizeObserver = new ResizeObserver(updateSliderMetrics);
81
+ resizeObserver.observe(toggle);
82
+ resizeObserver.observe(selectedButton);
83
+ return () => resizeObserver.disconnect();
84
+ }, [options, selected]);
85
+ const body = /* @__PURE__ */ jsxs("div", {
86
+ ref: toggleRef,
87
+ className: cn("relative inline-flex items-center gap-1 p-1 rounded-xl", glassmorphic ? "bg-foreground/[0.04] backdrop-blur-sm" : "bg-black/[0.08] dark:bg-white/[0.04]", className),
88
+ children: [sliderMetrics != null && /* @__PURE__ */ jsx("div", {
89
+ className: cn("pointer-events-none absolute inset-y-1 left-0 z-0 rounded-lg bg-background shadow-sm ring-1", glassmorphic ? "ring-foreground/[0.06] dark:bg-[hsl(240,71%,70%)]/10 dark:ring-[hsl(240,71%,70%)]/20" : activeRingClass),
90
+ style: {
91
+ transition: prefersReducedMotion ? void 0 : sliderTransition,
92
+ transform: `translateX(${sliderMetrics.left}px)`,
93
+ width: sliderMetrics.width
94
+ }
95
+ }), options.map((option) => {
53
96
  const isActive = selected === option.id;
54
97
  const Icon = option.icon;
55
98
  const pill = /* @__PURE__ */ jsxs("button", {
56
99
  type: "button",
100
+ ref: (element) => {
101
+ if (element) optionRefs.current.set(option.id, element);
102
+ else optionRefs.current.delete(option.id);
103
+ },
57
104
  onClick: () => handleClick(option.id),
58
105
  disabled: loadingOptionId !== null,
59
- className: cn("relative flex items-center gap-2 font-medium rounded-lg transition-all duration-150 hover:transition-none", sizeClass.button, isActive ? cn("bg-white dark:bg-background text-foreground shadow-sm ring-1", glassmorphic ? "ring-foreground/[0.06] dark:bg-[hsl(240,71%,70%)]/10 dark:text-[hsl(240,71%,90%)] dark:ring-[hsl(240,71%,70%)]/20" : activeRingClass) : cn("text-muted-foreground hover:text-foreground", glassmorphic ? "hover:bg-background/50" : "hover:bg-black/[0.06] dark:hover:bg-white/[0.04]")),
106
+ "aria-label": showLabels ? void 0 : option.label,
107
+ className: cn("relative z-10 flex items-center gap-2 font-medium rounded-lg transition-all duration-150 hover:transition-none", showLabels ? sizeClass.button : sizeClass.iconOnlyButton, !showLabels && "justify-center p-0", isActive ? cn("text-foreground", glassmorphic && "dark:text-[hsl(240,71%,90%)]") : cn("text-muted-foreground hover:text-foreground", glassmorphic ? "hover:bg-background/50" : "hover:bg-black/[0.06] dark:hover:bg-white/[0.04]")),
60
108
  children: [loadingOptionId === option.id && /* @__PURE__ */ jsx(Spinner, {
61
109
  size: 12,
62
110
  className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
@@ -76,8 +124,9 @@ function DesignPillToggle({ options, selected, onSelect, size = "md", glassmorph
76
124
  }) })]
77
125
  }, option.id);
78
126
  return pill;
79
- })
80
- }) });
127
+ })]
128
+ });
129
+ return showLabels ? body : /* @__PURE__ */ jsx(TooltipProvider, { children: body });
81
130
  }
82
131
 
83
132
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"pill-toggle.js","names":[],"sources":["../../../src/components/pill-toggle.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { cn, Spinner, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@hexclave/ui\";\nimport { TooltipPortal } from \"@radix-ui/react-tooltip\";\nimport { runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { useGlassmorphicDefault } from \"./card\";\n\ntype DesignPillToggleSize = \"sm\" | \"md\" | \"lg\";\ntype DesignPillToggleGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nexport type DesignPillToggleOption = {\n id: string,\n label: string,\n icon?: React.ElementType,\n};\n\nexport type DesignPillToggleProps = {\n options: DesignPillToggleOption[],\n selected: string,\n onSelect: (id: string) => void | Promise<void>,\n size?: DesignPillToggleSize,\n glassmorphic?: boolean,\n gradient?: DesignPillToggleGradient,\n /** When false, hides labels and shows a tooltip on hover instead. Defaults to true. */\n showLabels?: boolean,\n className?: string,\n};\n\ntype SizeClass = {\n button: string,\n icon: string,\n};\n\nconst sizeClasses = new Map<DesignPillToggleSize, SizeClass>([\n [\"sm\", { button: \"px-3 py-1.5 text-xs\", icon: \"h-3.5 w-3.5\" }],\n [\"md\", { button: \"px-4 py-2 text-sm\", icon: \"h-4 w-4\" }],\n [\"lg\", { button: \"px-5 py-2.5 text-sm\", icon: \"h-4 w-4\" }],\n]);\n\nconst gradientClasses = new Map<DesignPillToggleGradient, string>([\n [\"blue\", \"ring-blue-500/20 dark:ring-blue-400/20\"],\n [\"cyan\", \"ring-cyan-500/20 dark:ring-cyan-400/20\"],\n [\"purple\", \"ring-purple-500/20 dark:ring-purple-400/20\"],\n [\"green\", \"ring-emerald-500/20 dark:ring-emerald-400/20\"],\n [\"orange\", \"ring-amber-500/20 dark:ring-amber-400/20\"],\n [\"default\", \"ring-black/[0.12] dark:ring-white/[0.06]\"],\n]);\n\nfunction getMapValueOrThrow<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, mapName: string) {\n const value = map.get(key);\n if (!value) {\n throw new Error(`Missing ${mapName} entry for key \"${String(key)}\"`);\n }\n return value;\n}\n\nexport function DesignPillToggle({\n options,\n selected,\n onSelect,\n size = \"md\",\n glassmorphic: glassmorphicProp,\n gradient = \"default\",\n showLabels = true,\n className,\n}: DesignPillToggleProps) {\n const glassmorphic = useGlassmorphicDefault(glassmorphicProp);\n const sizeClass = getMapValueOrThrow(sizeClasses, size, \"sizeClasses\");\n const activeRingClass = getMapValueOrThrow(gradientClasses, gradient, \"gradientClasses\");\n\n const [loadingOptionId, setLoadingOptionId] = useState<string | null>(null);\n\n const handleClick = (optionId: string) => {\n const result = onSelect(optionId);\n if (result && typeof (result as Promise<void>).then === \"function\") {\n setLoadingOptionId(optionId);\n runAsynchronouslyWithAlert(\n Promise.resolve(result).finally(() => setLoadingOptionId(null))\n );\n }\n };\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"inline-flex items-center gap-1 p-1 rounded-xl\",\n glassmorphic\n ? \"bg-foreground/[0.04] backdrop-blur-sm\"\n : \"bg-black/[0.08] dark:bg-white/[0.04]\",\n className\n )}\n >\n {options.map((option) => {\n const isActive = selected === option.id;\n const Icon = option.icon;\n\n const pill = (\n <button\n type=\"button\"\n key={option.id}\n onClick={() => handleClick(option.id)}\n disabled={loadingOptionId !== null}\n className={cn(\n \"relative flex items-center gap-2 font-medium rounded-lg transition-all duration-150 hover:transition-none\",\n sizeClass.button,\n isActive\n ? cn(\n \"bg-white dark:bg-background text-foreground shadow-sm ring-1\",\n glassmorphic\n ? \"ring-foreground/[0.06] dark:bg-[hsl(240,71%,70%)]/10 dark:text-[hsl(240,71%,90%)] dark:ring-[hsl(240,71%,70%)]/20\"\n : activeRingClass\n )\n : cn(\n \"text-muted-foreground hover:text-foreground\",\n glassmorphic\n ? \"hover:bg-background/50\"\n : \"hover:bg-black/[0.06] dark:hover:bg-white/[0.04]\"\n )\n )}\n >\n {loadingOptionId === option.id && (\n <Spinner\n size={12}\n className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none\"\n />\n )}\n <span className={cn(\n \"flex items-center gap-2\",\n loadingOptionId === option.id && \"invisible\"\n )}>\n {Icon && <Icon className={sizeClass.icon} />}\n {showLabels && option.label}\n </span>\n </button>\n );\n\n if (!showLabels) {\n return (\n <Tooltip key={option.id} delayDuration={0}>\n <TooltipTrigger asChild>\n {pill}\n </TooltipTrigger>\n <TooltipPortal>\n <TooltipContent side=\"top\">\n {option.label}\n </TooltipContent>\n </TooltipPortal>\n </Tooltip>\n );\n }\n\n return pill;\n })}\n </div>\n </TooltipProvider>\n );\n}\n"],"mappings":";;;;;;;;;;AAkCA,MAAM,cAAc,IAAI,IAAqC;CAC3D,CAAC,MAAM;EAAE,QAAQ;EAAuB,MAAM;EAAe,CAAC;CAC9D,CAAC,MAAM;EAAE,QAAQ;EAAqB,MAAM;EAAW,CAAC;CACxD,CAAC,MAAM;EAAE,QAAQ;EAAuB,MAAM;EAAW,CAAC;CAC3D,CAAC;AAEF,MAAM,kBAAkB,IAAI,IAAsC;CAChE,CAAC,QAAQ,yCAAyC;CAClD,CAAC,QAAQ,yCAAyC;CAClD,CAAC,UAAU,6CAA6C;CACxD,CAAC,SAAS,+CAA+C;CACzD,CAAC,UAAU,2CAA2C;CACtD,CAAC,WAAW,2CAA2C;CACxD,CAAC;AAEF,SAAS,mBAAiC,KAAwB,KAAW,SAAiB;CAC5F,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC1B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,WAAW,QAAQ,kBAAkB,OAAO,IAAI,CAAC,GAAG;AAEtE,QAAO;;AAGT,SAAgB,iBAAiB,EAC/B,SACA,UACA,UACA,OAAO,MACP,cAAc,kBACd,WAAW,WACX,aAAa,MACb,aACwB;CACxB,MAAM,eAAe,uBAAuB,iBAAiB;CAC7D,MAAM,YAAY,mBAAmB,aAAa,MAAM,cAAc;CACtE,MAAM,kBAAkB,mBAAmB,iBAAiB,UAAU,kBAAkB;CAExF,MAAM,CAAC,iBAAiB,sBAAsB,SAAwB,KAAK;CAE3E,MAAM,eAAe,aAAqB;EACxC,MAAM,SAAS,SAAS,SAAS;AACjC,MAAI,UAAU,OAAQ,OAAyB,SAAS,YAAY;AAClE,sBAAmB,SAAS;AAC5B,8BACE,QAAQ,QAAQ,OAAO,CAAC,cAAc,mBAAmB,KAAK,CAAC,CAChE;;;AAIL,QACE,oBAAC,6BACC,oBAAC;EACC,WAAW,GACT,iDACA,eACI,0CACA,wCACJ,UACD;YAEA,QAAQ,KAAK,WAAW;GACvB,MAAM,WAAW,aAAa,OAAO;GACrC,MAAM,OAAO,OAAO;GAEpB,MAAM,OACJ,qBAAC;IACC,MAAK;IAEL,eAAe,YAAY,OAAO,GAAG;IACrC,UAAU,oBAAoB;IAC9B,WAAW,GACT,6GACA,UAAU,QACV,WACI,GACA,gEACA,eACI,sHACA,gBACL,GACC,GACA,+CACA,eACI,2BACA,mDACL,CACJ;eAEA,oBAAoB,OAAO,MAC1B,oBAAC;KACC,MAAM;KACN,WAAU;MACV,EAEJ,qBAAC;KAAK,WAAW,GACf,2BACA,oBAAoB,OAAO,MAAM,YAClC;gBACE,QAAQ,oBAAC,QAAK,WAAW,UAAU,OAAQ,EAC3C,cAAc,OAAO;MACjB;MAjCF,OAAO,GAkCL;AAGX,OAAI,CAAC,WACH,QACE,qBAAC;IAAwB,eAAe;eACtC,oBAAC;KAAe;eACb;MACc,EACjB,oBAAC,2BACC,oBAAC;KAAe,MAAK;eAClB,OAAO;MACO,GACH;MARJ,OAAO,GASX;AAId,UAAO;IACP;GACE,GACU"}
1
+ {"version":3,"file":"pill-toggle.js","names":[],"sources":["../../../src/components/pill-toggle.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn, Spinner, Tooltip, TooltipContent, TooltipPortal, TooltipProvider, TooltipTrigger } from \"@hexclave/ui\";\nimport { runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { useGlassmorphicDefault } from \"./card\";\n\ntype DesignPillToggleSize = \"sm\" | \"md\" | \"lg\";\ntype DesignPillToggleGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nexport type DesignPillToggleOption = {\n id: string,\n label: string,\n icon?: React.ElementType,\n};\n\nexport type DesignPillToggleProps = {\n options: DesignPillToggleOption[],\n selected: string,\n onSelect: (id: string) => void | Promise<void>,\n size?: DesignPillToggleSize,\n glassmorphic?: boolean,\n gradient?: DesignPillToggleGradient,\n /** When false, hides labels and shows a tooltip on hover instead. Defaults to true. */\n showLabels?: boolean,\n className?: string,\n};\n\ntype SizeClass = {\n button: string,\n iconOnlyButton: string,\n icon: string,\n};\n\ntype SliderMetrics = {\n left: number,\n width: number,\n};\n\nconst sliderTransition = \"transform 200ms ease-out, width 200ms ease-out\";\n\nconst sizeClasses = new Map<DesignPillToggleSize, SizeClass>([\n [\"sm\", { button: \"px-3 py-1.5 text-xs\", iconOnlyButton: \"h-7 w-7 text-xs\", icon: \"h-3.5 w-3.5\" }],\n [\"md\", { button: \"px-4 py-2 text-sm\", iconOnlyButton: \"h-9 w-9 text-sm\", icon: \"h-4 w-4\" }],\n [\"lg\", { button: \"px-5 py-2.5 text-sm\", iconOnlyButton: \"h-10 w-10 text-sm\", icon: \"h-4 w-4\" }],\n]);\n\nconst gradientClasses = new Map<DesignPillToggleGradient, string>([\n [\"blue\", \"ring-blue-500/20 dark:ring-blue-400/20\"],\n [\"cyan\", \"ring-cyan-500/20 dark:ring-cyan-400/20\"],\n [\"purple\", \"ring-purple-500/20 dark:ring-purple-400/20\"],\n [\"green\", \"ring-emerald-500/20 dark:ring-emerald-400/20\"],\n [\"orange\", \"ring-amber-500/20 dark:ring-amber-400/20\"],\n [\"default\", \"ring-black/[0.12] dark:ring-white/[0.06]\"],\n]);\n\nfunction getMapValueOrThrow<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, mapName: string) {\n const value = map.get(key);\n if (!value) {\n throw new Error(`Missing ${mapName} entry for key \"${String(key)}\"`);\n }\n return value;\n}\n\nexport function DesignPillToggle({\n options,\n selected,\n onSelect,\n size = \"md\",\n glassmorphic: glassmorphicProp,\n gradient = \"default\",\n showLabels = true,\n className,\n}: DesignPillToggleProps) {\n const glassmorphic = useGlassmorphicDefault(glassmorphicProp);\n const sizeClass = getMapValueOrThrow(sizeClasses, size, \"sizeClasses\");\n const activeRingClass = getMapValueOrThrow(gradientClasses, gradient, \"gradientClasses\");\n\n const [loadingOptionId, setLoadingOptionId] = useState<string | null>(null);\n const [sliderMetrics, setSliderMetrics] = useState<SliderMetrics | null>(null);\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n const toggleRef = useRef<HTMLDivElement | null>(null);\n const optionRefs = useRef(new Map<string, HTMLButtonElement>());\n\n const handleClick = (optionId: string) => {\n const result = onSelect(optionId);\n if (result && typeof (result as Promise<void>).then === \"function\") {\n setLoadingOptionId(optionId);\n runAsynchronouslyWithAlert(\n Promise.resolve(result).finally(() => setLoadingOptionId(null))\n );\n }\n };\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const updatePrefersReducedMotion = () => setPrefersReducedMotion(mediaQuery.matches);\n\n updatePrefersReducedMotion();\n mediaQuery.addEventListener(\"change\", updatePrefersReducedMotion);\n\n return () => mediaQuery.removeEventListener(\"change\", updatePrefersReducedMotion);\n }, []);\n\n useEffect(() => {\n const toggle = toggleRef.current;\n const selectedButton = optionRefs.current.get(selected);\n\n if (!toggle || !selectedButton) {\n setSliderMetrics(null);\n return;\n }\n\n const updateSliderMetrics = () => {\n setSliderMetrics({\n left: selectedButton.offsetLeft,\n width: selectedButton.offsetWidth,\n });\n };\n\n updateSliderMetrics();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const resizeObserver = new ResizeObserver(updateSliderMetrics);\n resizeObserver.observe(toggle);\n resizeObserver.observe(selectedButton);\n\n return () => resizeObserver.disconnect();\n }, [options, selected]);\n\n const body = (\n <div\n ref={toggleRef}\n className={cn(\n \"relative inline-flex items-center gap-1 p-1 rounded-xl\",\n glassmorphic\n ? \"bg-foreground/[0.04] backdrop-blur-sm\"\n : \"bg-black/[0.08] dark:bg-white/[0.04]\",\n className\n )}\n >\n {sliderMetrics != null && (\n <div\n className={cn(\n \"pointer-events-none absolute inset-y-1 left-0 z-0 rounded-lg bg-background shadow-sm ring-1\",\n glassmorphic\n ? \"ring-foreground/[0.06] dark:bg-[hsl(240,71%,70%)]/10 dark:ring-[hsl(240,71%,70%)]/20\"\n : activeRingClass\n )}\n style={{\n transition: prefersReducedMotion ? undefined : sliderTransition,\n transform: `translateX(${sliderMetrics.left}px)`,\n width: sliderMetrics.width,\n }}\n />\n )}\n {options.map((option) => {\n const isActive = selected === option.id;\n const Icon = option.icon;\n\n const pill = (\n <button\n type=\"button\"\n key={option.id}\n ref={(element) => {\n if (element) {\n optionRefs.current.set(option.id, element);\n } else {\n optionRefs.current.delete(option.id);\n }\n }}\n onClick={() => handleClick(option.id)}\n disabled={loadingOptionId !== null}\n aria-label={showLabels ? undefined : option.label}\n className={cn(\n \"relative z-10 flex items-center gap-2 font-medium rounded-lg transition-all duration-150 hover:transition-none\",\n showLabels ? sizeClass.button : sizeClass.iconOnlyButton,\n !showLabels && \"justify-center p-0\",\n isActive\n ? cn(\"text-foreground\", glassmorphic && \"dark:text-[hsl(240,71%,90%)]\")\n : cn(\n \"text-muted-foreground hover:text-foreground\",\n glassmorphic\n ? \"hover:bg-background/50\"\n : \"hover:bg-black/[0.06] dark:hover:bg-white/[0.04]\"\n )\n )}\n >\n {loadingOptionId === option.id && (\n <Spinner\n size={12}\n className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none\"\n />\n )}\n <span className={cn(\n \"flex items-center gap-2\",\n loadingOptionId === option.id && \"invisible\"\n )}>\n {Icon && <Icon className={sizeClass.icon} />}\n {showLabels && option.label}\n </span>\n </button>\n );\n\n if (!showLabels) {\n return (\n <Tooltip key={option.id} delayDuration={0}>\n <TooltipTrigger asChild>\n {pill}\n </TooltipTrigger>\n <TooltipPortal>\n <TooltipContent side=\"top\">\n {option.label}\n </TooltipContent>\n </TooltipPortal>\n </Tooltip>\n );\n }\n\n return pill;\n })}\n </div>\n );\n\n // Tooltips require a TooltipProvider in scope. Wrap defensively so callers\n // outside an existing provider (e.g. inside a PageLayout actions slot) work.\n return showLabels ? body : <TooltipProvider>{body}</TooltipProvider>;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.d.ts","names":[],"sources":["../../../src/components/tabs.tsx"],"mappings":";;;;KAOK,cAAA;AAAA,KACA,kBAAA;AAAA,KAEO,qBAAA;EACV,EAAA;EACA,KAAA;EACA,KAAA;EACA,UAAA;EACA,IAAA,GAAO,KAAA,CAAM,aAAA,CAAc,KAAA,CAAM,QAAA,CAAS,aAAA;AAAA;AAAA,KAGhC,uBAAA,GAA0B,IAAA,CAAK,KAAA,CAAM,cAAA;EAC/C,UAAA,EAAY,qBAAA;EACZ,gBAAA;EACA,QAAA,GAAW,EAAA,oBAAsB,OAAA;EACjC,SAAA;EACA,IAAA,GAAO,cAAA;EACP,YAAA;EACA,QAAA,GAAW,kBAAA,EAVgB;EAY3B,QAAA,GAAW,SAAA;AAAA;AAAA,iBA8EG,kBAAA,CAAA;EACd,UAAA;EACA,gBAAA;EACA,QAAA;EACA,SAAA;EACA,IAAA;EACA,YAAA,EAAc,gBAAA;EACd,QAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,uBAAA,GAAuB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"tabs.d.ts","names":[],"sources":["../../../src/components/tabs.tsx"],"mappings":";;;;KAOK,cAAA;AAAA,KACA,kBAAA;AAAA,KAEO,qBAAA;EACV,EAAA;EACA,KAAA;EACA,KAAA;EACA,UAAA;EACA,IAAA,GAAO,KAAA,CAAM,aAAA,CAAc,KAAA,CAAM,QAAA,CAAS,aAAA;AAAA;AAAA,KAGhC,uBAAA,GAA0B,IAAA,CAAK,KAAA,CAAM,cAAA;EAC/C,UAAA,EAAY,qBAAA;EACZ,gBAAA;EACA,QAAA,GAAW,EAAA,oBAAsB,OAAA;EACjC,SAAA;EACA,IAAA,GAAO,cAAA;EACP,YAAA;EACA,QAAA,GAAW,kBAAA,EAVgB;EAY3B,QAAA,GAAW,SAAA;AAAA;AAAA,iBAqFG,kBAAA,CAAA;EACd,UAAA;EACA,gBAAA;EACA,QAAA;EACA,SAAA;EACA,IAAA;EACA,YAAA,EAAc,gBAAA;EACd,QAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,uBAAA,GAAuB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -2,11 +2,12 @@
2
2
 
3
3
  import { Spinner, cn } from "@hexclave/ui";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { useState } from "react";
5
+ import { useEffect, useRef, useState } from "react";
6
6
  import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
7
7
  import { useGlassmorphicDefault } from "./card.js";
8
8
 
9
9
  //#region src/components/tabs.tsx
10
+ const sliderTransition = "transform 200ms ease-out, width 200ms ease-out";
10
11
  const tabSizeClasses = new Map([["sm", {
11
12
  button: "px-3 py-2 text-xs",
12
13
  badge: "text-[10px] px-1.5 py-0.5"
@@ -56,6 +57,10 @@ function DesignCategoryTabs({ categories, selectedCategory, onSelect, showBadge
56
57
  const sizeClass = getMapValueOrThrow(tabSizeClasses, size, "tabSizeClasses");
57
58
  const gradientClass = getMapValueOrThrow(gradientClasses, gradient, "gradientClasses");
58
59
  const [loadingCategoryId, setLoadingCategoryId] = useState(null);
60
+ const [sliderMetrics, setSliderMetrics] = useState(null);
61
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
62
+ const tabListRef = useRef(null);
63
+ const tabButtonRefs = useRef(/* @__PURE__ */ new Map());
59
64
  const handleSelect = (categoryId) => {
60
65
  const result = onSelect(categoryId);
61
66
  if (result && typeof result.then === "function") {
@@ -63,25 +68,73 @@ function DesignCategoryTabs({ categories, selectedCategory, onSelect, showBadge
63
68
  runAsynchronouslyWithAlert(Promise.resolve(result).finally(() => setLoadingCategoryId(null)));
64
69
  }
65
70
  };
71
+ useEffect(() => {
72
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
73
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
74
+ const updatePrefersReducedMotion = () => setPrefersReducedMotion(mediaQuery.matches);
75
+ updatePrefersReducedMotion();
76
+ mediaQuery.addEventListener("change", updatePrefersReducedMotion);
77
+ return () => mediaQuery.removeEventListener("change", updatePrefersReducedMotion);
78
+ }, []);
79
+ useEffect(() => {
80
+ const tabList = tabListRef.current;
81
+ const selectedButton = tabButtonRefs.current.get(selectedCategory);
82
+ if (!tabList || !selectedButton) {
83
+ setSliderMetrics(null);
84
+ return;
85
+ }
86
+ const updateSliderMetrics = () => {
87
+ setSliderMetrics({
88
+ left: selectedButton.offsetLeft,
89
+ width: selectedButton.offsetWidth
90
+ });
91
+ };
92
+ updateSliderMetrics();
93
+ if (typeof ResizeObserver === "undefined") return;
94
+ const resizeObserver = new ResizeObserver(updateSliderMetrics);
95
+ resizeObserver.observe(tabList);
96
+ resizeObserver.observe(selectedButton);
97
+ return () => resizeObserver.disconnect();
98
+ }, [categories, selectedCategory]);
66
99
  return /* @__PURE__ */ jsxs("div", {
67
100
  className: cn("flex w-full min-w-0 items-center gap-2", glassmorphic ? "rounded-xl bg-zinc-100/90 p-1 ring-1 ring-black/[0.06] backdrop-blur-sm dark:bg-white/[0.04] dark:ring-white/[0.06]" : "border-b border-gray-300 dark:border-gray-800", className),
68
101
  ...props,
69
- children: [/* @__PURE__ */ jsx("div", {
70
- className: cn("flex min-h-0 min-w-0 items-center gap-1 overflow-x-auto flex-nowrap [&::-webkit-scrollbar]:hidden"),
71
- children: categories.map((category) => {
72
- const isActive = selectedCategory === category.id;
73
- const badgeValue = category.badgeCount ?? category.count;
74
- const shouldShowBadge = showBadge && badgeValue !== void 0;
75
- return /* @__PURE__ */ jsxs("button", {
76
- onClick: () => handleSelect(category.id),
77
- disabled: loadingCategoryId !== null,
78
- className: cn("font-medium transition-all duration-150 hover:transition-none relative flex flex-shrink-0 items-center justify-center gap-2 whitespace-nowrap", "hover:text-gray-900 dark:hover:text-gray-100", sizeClass.button, glassmorphic ? "rounded-lg" : "", isActive ? cn(gradientClass.activeText, glassmorphic && "bg-white shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]") : cn("text-gray-700 dark:text-gray-400", glassmorphic && "rounded-lg hover:bg-white/50 dark:hover:bg-white/[0.06]")),
79
- children: [
80
- loadingCategoryId === category.id && /* @__PURE__ */ jsx(Spinner, {
102
+ children: [/* @__PURE__ */ jsxs("div", {
103
+ ref: tabListRef,
104
+ className: cn("relative flex min-h-0 min-w-0 items-center gap-1 overflow-x-auto flex-nowrap [&::-webkit-scrollbar]:hidden"),
105
+ children: [
106
+ glassmorphic && sliderMetrics != null && /* @__PURE__ */ jsx("div", {
107
+ className: "pointer-events-none absolute inset-y-0 left-0 z-0 rounded-lg bg-background shadow-sm ring-1 ring-black/[0.12] motion-reduce:transition-none dark:ring-white/[0.06]",
108
+ style: {
109
+ transition: prefersReducedMotion ? void 0 : sliderTransition,
110
+ transform: `translateX(${sliderMetrics.left}px)`,
111
+ width: sliderMetrics.width
112
+ }
113
+ }),
114
+ !glassmorphic && sliderMetrics != null && /* @__PURE__ */ jsx("div", {
115
+ className: cn("pointer-events-none absolute bottom-0 left-0 h-0.5 motion-reduce:transition-none", gradientClass.underline),
116
+ style: {
117
+ transition: prefersReducedMotion ? void 0 : sliderTransition,
118
+ transform: `translateX(${sliderMetrics.left}px)`,
119
+ width: sliderMetrics.width
120
+ }
121
+ }),
122
+ categories.map((category) => {
123
+ const isActive = selectedCategory === category.id;
124
+ const badgeValue = category.badgeCount ?? category.count;
125
+ const shouldShowBadge = showBadge && badgeValue !== void 0;
126
+ return /* @__PURE__ */ jsxs("button", {
127
+ ref: (element) => {
128
+ if (element) tabButtonRefs.current.set(category.id, element);
129
+ else tabButtonRefs.current.delete(category.id);
130
+ },
131
+ onClick: () => handleSelect(category.id),
132
+ disabled: loadingCategoryId !== null,
133
+ className: cn("font-medium transition-all duration-150 hover:transition-none relative z-10 flex flex-shrink-0 items-center justify-center gap-2 whitespace-nowrap", "hover:text-gray-900 dark:hover:text-gray-100", sizeClass.button, glassmorphic ? "rounded-lg" : "", isActive ? gradientClass.activeText : cn("text-gray-700 dark:text-gray-400", glassmorphic && "rounded-lg hover:bg-white/50 dark:hover:bg-white/[0.06]")),
134
+ children: [loadingCategoryId === category.id && /* @__PURE__ */ jsx(Spinner, {
81
135
  size: 12,
82
136
  className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
83
- }),
84
- /* @__PURE__ */ jsxs("span", {
137
+ }), /* @__PURE__ */ jsxs("span", {
85
138
  className: cn("flex items-center gap-2", loadingCategoryId === category.id && "invisible"),
86
139
  children: [
87
140
  category.icon && /* @__PURE__ */ jsx(category.icon, {
@@ -94,11 +147,10 @@ function DesignCategoryTabs({ categories, selectedCategory, onSelect, showBadge
94
147
  children: badgeValue
95
148
  })
96
149
  ]
97
- }),
98
- !glassmorphic && isActive && /* @__PURE__ */ jsx("div", { className: cn("absolute bottom-0 left-0 right-0 h-0.5", gradientClass.underline) })
99
- ]
100
- }, category.id);
101
- })
150
+ })]
151
+ }, category.id);
152
+ })
153
+ ]
102
154
  }), trailing != null ? /* @__PURE__ */ jsx("div", {
103
155
  className: "flex shrink-0 items-center",
104
156
  children: trailing
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.js","names":[],"sources":["../../../src/components/tabs.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport { cn, Spinner } from \"@hexclave/ui\";\nimport { runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { useGlassmorphicDefault } from \"./card\";\n\ntype DesignTabsSize = \"sm\" | \"md\";\ntype DesignTabsGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nexport type DesignCategoryTabItem = {\n id: string,\n label: string,\n count?: number,\n badgeCount?: number,\n icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>,\n};\n\nexport type DesignCategoryTabsProps = Omit<React.ComponentProps<\"div\">, \"onSelect\"> & {\n categories: DesignCategoryTabItem[],\n selectedCategory: string,\n onSelect: (id: string) => void | Promise<void>,\n showBadge?: boolean,\n size?: DesignTabsSize,\n glassmorphic?: boolean,\n gradient?: DesignTabsGradient,\n /** Renders inside the tab bar after the tab buttons (not a tab). */\n trailing?: ReactNode,\n};\n\ntype TabSizeClass = {\n button: string,\n badge: string,\n};\n\ntype GradientClass = {\n activeText: string,\n activeBadge: string,\n underline: string,\n};\n\nconst tabSizeClasses = new Map<DesignTabsSize, TabSizeClass>([\n [\"sm\", { button: \"px-3 py-2 text-xs\", badge: \"text-[10px] px-1.5 py-0.5\" }],\n [\"md\", { button: \"px-4 py-3 text-sm\", badge: \"text-xs px-1.5 py-0.5\" }],\n]);\n\nconst gradientClasses = new Map<DesignTabsGradient, GradientClass>([\n [\n \"blue\",\n {\n activeText: \"text-blue-700 dark:text-blue-400\",\n activeBadge: \"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400\",\n underline: \"bg-blue-700 dark:bg-blue-400\",\n },\n ],\n [\n \"cyan\",\n {\n activeText: \"text-cyan-700 dark:text-cyan-300\",\n activeBadge: \"bg-cyan-100 dark:bg-cyan-900/30 text-cyan-700 dark:text-cyan-300\",\n underline: \"bg-cyan-600 dark:bg-cyan-400\",\n },\n ],\n [\n \"purple\",\n {\n activeText: \"text-purple-700 dark:text-purple-300\",\n activeBadge: \"bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300\",\n underline: \"bg-purple-600 dark:bg-purple-400\",\n },\n ],\n [\n \"green\",\n {\n activeText: \"text-emerald-700 dark:text-emerald-300\",\n activeBadge: \"bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300\",\n underline: \"bg-emerald-600 dark:bg-emerald-400\",\n },\n ],\n [\n \"orange\",\n {\n activeText: \"text-amber-700 dark:text-amber-300\",\n activeBadge: \"bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300\",\n underline: \"bg-amber-600 dark:bg-amber-400\",\n },\n ],\n [\n \"default\",\n {\n activeText: \"text-foreground\",\n activeBadge: \"bg-foreground/10 text-foreground\",\n underline: \"bg-foreground/80\",\n },\n ],\n]);\n\nfunction getMapValueOrThrow<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, mapName: string) {\n const value = map.get(key);\n if (!value) {\n throw new Error(`Missing ${mapName} entry for key \"${String(key)}\"`);\n }\n return value;\n}\n\nexport function DesignCategoryTabs({\n categories,\n selectedCategory,\n onSelect,\n showBadge = true,\n size = \"sm\",\n glassmorphic: glassmorphicProp,\n gradient = \"blue\",\n trailing,\n className,\n ...props\n}: DesignCategoryTabsProps) {\n const glassmorphic = useGlassmorphicDefault(glassmorphicProp);\n const sizeClass = getMapValueOrThrow(tabSizeClasses, size, \"tabSizeClasses\");\n const gradientClass = getMapValueOrThrow(gradientClasses, gradient, \"gradientClasses\");\n const [loadingCategoryId, setLoadingCategoryId] = useState<string | null>(null);\n\n const handleSelect = (categoryId: string) => {\n const result = onSelect(categoryId);\n if (result && typeof (result as Promise<void>).then === \"function\") {\n setLoadingCategoryId(categoryId);\n runAsynchronouslyWithAlert(\n Promise.resolve(result).finally(() => setLoadingCategoryId(null))\n );\n }\n };\n\n return (\n <div\n className={cn(\n \"flex w-full min-w-0 items-center gap-2\",\n glassmorphic\n ? \"rounded-xl bg-zinc-100/90 p-1 ring-1 ring-black/[0.06] backdrop-blur-sm dark:bg-white/[0.04] dark:ring-white/[0.06]\"\n : \"border-b border-gray-300 dark:border-gray-800\",\n className\n )}\n {...props}\n >\n <div\n className={cn(\n \"flex min-h-0 min-w-0 items-center gap-1 overflow-x-auto flex-nowrap [&::-webkit-scrollbar]:hidden\",\n )}\n >\n {categories.map((category) => {\n const isActive = selectedCategory === category.id;\n const badgeValue = category.badgeCount ?? category.count;\n const shouldShowBadge = showBadge && badgeValue !== undefined;\n\n return (\n <button\n key={category.id}\n onClick={() => handleSelect(category.id)}\n disabled={loadingCategoryId !== null}\n className={cn(\n \"font-medium transition-all duration-150 hover:transition-none relative flex flex-shrink-0 items-center justify-center gap-2 whitespace-nowrap\",\n \"hover:text-gray-900 dark:hover:text-gray-100\",\n sizeClass.button,\n glassmorphic ? \"rounded-lg\" : \"\",\n isActive\n ? cn(\n gradientClass.activeText,\n glassmorphic && \"bg-white shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]\"\n )\n : cn(\n \"text-gray-700 dark:text-gray-400\",\n glassmorphic && \"rounded-lg hover:bg-white/50 dark:hover:bg-white/[0.06]\",\n )\n )}\n >\n {loadingCategoryId === category.id && (\n <Spinner\n size={12}\n className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none\"\n />\n )}\n <span className={cn(\n \"flex items-center gap-2\",\n loadingCategoryId === category.id && \"invisible\"\n )}>\n {category.icon && (\n <category.icon className=\"h-4 w-4 shrink-0\" aria-hidden />\n )}\n {category.label}\n {shouldShowBadge && (\n <span\n className={cn(\n \"rounded-full\",\n sizeClass.badge,\n isActive\n ? gradientClass.activeBadge\n : \"bg-gray-200 dark:bg-gray-800 text-gray-600 dark:text-gray-400\"\n )}\n >\n {badgeValue}\n </span>\n )}\n </span>\n {!glassmorphic && isActive && (\n <div className={cn(\"absolute bottom-0 left-0 right-0 h-0.5\", gradientClass.underline)} />\n )}\n </button>\n );\n })}\n </div>\n {trailing != null ? (\n <div className=\"flex shrink-0 items-center\">\n {trailing}\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAyCA,MAAM,iBAAiB,IAAI,IAAkC,CAC3D,CAAC,MAAM;CAAE,QAAQ;CAAqB,OAAO;CAA6B,CAAC,EAC3E,CAAC,MAAM;CAAE,QAAQ;CAAqB,OAAO;CAAyB,CAAC,CACxE,CAAC;AAEF,MAAM,kBAAkB,IAAI,IAAuC;CACjE,CACE,QACA;EACE,YAAY;EACZ,aAAa;EACb,WAAW;EACZ,CACF;CACD,CACE,QACA;EACE,YAAY;EACZ,aAAa;EACb,WAAW;EACZ,CACF;CACD,CACE,UACA;EACE,YAAY;EACZ,aAAa;EACb,WAAW;EACZ,CACF;CACD,CACE,SACA;EACE,YAAY;EACZ,aAAa;EACb,WAAW;EACZ,CACF;CACD,CACE,UACA;EACE,YAAY;EACZ,aAAa;EACb,WAAW;EACZ,CACF;CACD,CACE,WACA;EACE,YAAY;EACZ,aAAa;EACb,WAAW;EACZ,CACF;CACF,CAAC;AAEF,SAAS,mBAAiC,KAAwB,KAAW,SAAiB;CAC5F,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC1B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,WAAW,QAAQ,kBAAkB,OAAO,IAAI,CAAC,GAAG;AAEtE,QAAO;;AAGT,SAAgB,mBAAmB,EACjC,YACA,kBACA,UACA,YAAY,MACZ,OAAO,MACP,cAAc,kBACd,WAAW,QACX,UACA,WACA,GAAG,SACuB;CAC1B,MAAM,eAAe,uBAAuB,iBAAiB;CAC7D,MAAM,YAAY,mBAAmB,gBAAgB,MAAM,iBAAiB;CAC5E,MAAM,gBAAgB,mBAAmB,iBAAiB,UAAU,kBAAkB;CACtF,MAAM,CAAC,mBAAmB,wBAAwB,SAAwB,KAAK;CAE/E,MAAM,gBAAgB,eAAuB;EAC3C,MAAM,SAAS,SAAS,WAAW;AACnC,MAAI,UAAU,OAAQ,OAAyB,SAAS,YAAY;AAClE,wBAAqB,WAAW;AAChC,8BACE,QAAQ,QAAQ,OAAO,CAAC,cAAc,qBAAqB,KAAK,CAAC,CAClE;;;AAIL,QACE,qBAAC;EACC,WAAW,GACT,0CACA,eACI,wHACA,iDACJ,UACD;EACD,GAAI;aAEJ,oBAAC;GACC,WAAW,GACT,oGACD;aAEA,WAAW,KAAK,aAAa;IAC5B,MAAM,WAAW,qBAAqB,SAAS;IAC/C,MAAM,aAAa,SAAS,cAAc,SAAS;IACnD,MAAM,kBAAkB,aAAa,eAAe;AAEpD,WACE,qBAAC;KAEC,eAAe,aAAa,SAAS,GAAG;KACxC,UAAU,sBAAsB;KAChC,WAAW,GACT,iJACA,gDACA,UAAU,QACV,eAAe,eAAe,IAC9B,WACI,GACA,cAAc,YACd,gBAAgB,wFACjB,GACC,GACA,oCACA,gBAAgB,0DACjB,CACJ;;MAEA,sBAAsB,SAAS,MAC9B,oBAAC;OACC,MAAM;OACN,WAAU;QACV;MAEJ,qBAAC;OAAK,WAAW,GACf,2BACA,sBAAsB,SAAS,MAAM,YACtC;;QACE,SAAS,QACR,oBAAC,SAAS;SAAK,WAAU;SAAmB;UAAc;QAE3D,SAAS;QACT,mBACC,oBAAC;SACC,WAAW,GACT,gBACA,UAAU,OACV,WACI,cAAc,cACd,gEACL;mBAEA;UACI;;QAEJ;MACN,CAAC,gBAAgB,YAChB,oBAAC,SAAI,WAAW,GAAG,0CAA0C,cAAc,UAAU,GAAI;;OAhDtF,SAAS,GAkDP;KAEX;IACE,EACL,YAAY,OACX,oBAAC;GAAI,WAAU;aACZ;IACG,GACJ;GACA"}
1
+ {"version":3,"file":"tabs.js","names":[],"sources":["../../../src/components/tabs.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn, Spinner } from \"@hexclave/ui\";\nimport { runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { useGlassmorphicDefault } from \"./card\";\n\ntype DesignTabsSize = \"sm\" | \"md\";\ntype DesignTabsGradient = \"blue\" | \"cyan\" | \"purple\" | \"green\" | \"orange\" | \"default\";\n\nexport type DesignCategoryTabItem = {\n id: string,\n label: string,\n count?: number,\n badgeCount?: number,\n icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>,\n};\n\nexport type DesignCategoryTabsProps = Omit<React.ComponentProps<\"div\">, \"onSelect\"> & {\n categories: DesignCategoryTabItem[],\n selectedCategory: string,\n onSelect: (id: string) => void | Promise<void>,\n showBadge?: boolean,\n size?: DesignTabsSize,\n glassmorphic?: boolean,\n gradient?: DesignTabsGradient,\n /** Renders inside the tab bar after the tab buttons (not a tab). */\n trailing?: ReactNode,\n};\n\ntype TabSizeClass = {\n button: string,\n badge: string,\n};\n\ntype GradientClass = {\n activeText: string,\n activeBadge: string,\n underline: string,\n};\n\ntype SliderMetrics = {\n left: number,\n width: number,\n};\n\nconst sliderTransition = \"transform 200ms ease-out, width 200ms ease-out\";\n\nconst tabSizeClasses = new Map<DesignTabsSize, TabSizeClass>([\n [\"sm\", { button: \"px-3 py-2 text-xs\", badge: \"text-[10px] px-1.5 py-0.5\" }],\n [\"md\", { button: \"px-4 py-3 text-sm\", badge: \"text-xs px-1.5 py-0.5\" }],\n]);\n\nconst gradientClasses = new Map<DesignTabsGradient, GradientClass>([\n [\n \"blue\",\n {\n activeText: \"text-blue-700 dark:text-blue-400\",\n activeBadge: \"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400\",\n underline: \"bg-blue-700 dark:bg-blue-400\",\n },\n ],\n [\n \"cyan\",\n {\n activeText: \"text-cyan-700 dark:text-cyan-300\",\n activeBadge: \"bg-cyan-100 dark:bg-cyan-900/30 text-cyan-700 dark:text-cyan-300\",\n underline: \"bg-cyan-600 dark:bg-cyan-400\",\n },\n ],\n [\n \"purple\",\n {\n activeText: \"text-purple-700 dark:text-purple-300\",\n activeBadge: \"bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300\",\n underline: \"bg-purple-600 dark:bg-purple-400\",\n },\n ],\n [\n \"green\",\n {\n activeText: \"text-emerald-700 dark:text-emerald-300\",\n activeBadge: \"bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300\",\n underline: \"bg-emerald-600 dark:bg-emerald-400\",\n },\n ],\n [\n \"orange\",\n {\n activeText: \"text-amber-700 dark:text-amber-300\",\n activeBadge: \"bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300\",\n underline: \"bg-amber-600 dark:bg-amber-400\",\n },\n ],\n [\n \"default\",\n {\n activeText: \"text-foreground\",\n activeBadge: \"bg-foreground/10 text-foreground\",\n underline: \"bg-foreground/80\",\n },\n ],\n]);\n\nfunction getMapValueOrThrow<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, mapName: string) {\n const value = map.get(key);\n if (!value) {\n throw new Error(`Missing ${mapName} entry for key \"${String(key)}\"`);\n }\n return value;\n}\n\nexport function DesignCategoryTabs({\n categories,\n selectedCategory,\n onSelect,\n showBadge = true,\n size = \"sm\",\n glassmorphic: glassmorphicProp,\n gradient = \"blue\",\n trailing,\n className,\n ...props\n}: DesignCategoryTabsProps) {\n const glassmorphic = useGlassmorphicDefault(glassmorphicProp);\n const sizeClass = getMapValueOrThrow(tabSizeClasses, size, \"tabSizeClasses\");\n const gradientClass = getMapValueOrThrow(gradientClasses, gradient, \"gradientClasses\");\n const [loadingCategoryId, setLoadingCategoryId] = useState<string | null>(null);\n const [sliderMetrics, setSliderMetrics] = useState<SliderMetrics | null>(null);\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n const tabListRef = useRef<HTMLDivElement | null>(null);\n const tabButtonRefs = useRef(new Map<string, HTMLButtonElement>());\n\n const handleSelect = (categoryId: string) => {\n const result = onSelect(categoryId);\n if (result && typeof (result as Promise<void>).then === \"function\") {\n setLoadingCategoryId(categoryId);\n runAsynchronouslyWithAlert(\n Promise.resolve(result).finally(() => setLoadingCategoryId(null))\n );\n }\n };\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const updatePrefersReducedMotion = () => setPrefersReducedMotion(mediaQuery.matches);\n\n updatePrefersReducedMotion();\n mediaQuery.addEventListener(\"change\", updatePrefersReducedMotion);\n\n return () => mediaQuery.removeEventListener(\"change\", updatePrefersReducedMotion);\n }, []);\n\n useEffect(() => {\n const tabList = tabListRef.current;\n const selectedButton = tabButtonRefs.current.get(selectedCategory);\n\n if (!tabList || !selectedButton) {\n setSliderMetrics(null);\n return;\n }\n\n const updateSliderMetrics = () => {\n setSliderMetrics({\n left: selectedButton.offsetLeft,\n width: selectedButton.offsetWidth,\n });\n };\n\n updateSliderMetrics();\n\n if (typeof ResizeObserver === \"undefined\") return;\n const resizeObserver = new ResizeObserver(updateSliderMetrics);\n resizeObserver.observe(tabList);\n resizeObserver.observe(selectedButton);\n\n return () => resizeObserver.disconnect();\n }, [categories, selectedCategory]);\n\n return (\n <div\n className={cn(\n \"flex w-full min-w-0 items-center gap-2\",\n glassmorphic\n ? \"rounded-xl bg-zinc-100/90 p-1 ring-1 ring-black/[0.06] backdrop-blur-sm dark:bg-white/[0.04] dark:ring-white/[0.06]\"\n : \"border-b border-gray-300 dark:border-gray-800\",\n className\n )}\n {...props}\n >\n <div\n ref={tabListRef}\n className={cn(\n \"relative flex min-h-0 min-w-0 items-center gap-1 overflow-x-auto flex-nowrap [&::-webkit-scrollbar]:hidden\",\n )}\n >\n {glassmorphic && sliderMetrics != null && (\n <div\n className=\"pointer-events-none absolute inset-y-0 left-0 z-0 rounded-lg bg-background shadow-sm ring-1 ring-black/[0.12] motion-reduce:transition-none dark:ring-white/[0.06]\"\n style={{\n transition: prefersReducedMotion ? undefined : sliderTransition,\n transform: `translateX(${sliderMetrics.left}px)`,\n width: sliderMetrics.width,\n }}\n />\n )}\n {!glassmorphic && sliderMetrics != null && (\n <div\n className={cn(\n \"pointer-events-none absolute bottom-0 left-0 h-0.5 motion-reduce:transition-none\",\n gradientClass.underline\n )}\n style={{\n transition: prefersReducedMotion ? undefined : sliderTransition,\n transform: `translateX(${sliderMetrics.left}px)`,\n width: sliderMetrics.width,\n }}\n />\n )}\n {categories.map((category) => {\n const isActive = selectedCategory === category.id;\n const badgeValue = category.badgeCount ?? category.count;\n const shouldShowBadge = showBadge && badgeValue !== undefined;\n\n return (\n <button\n key={category.id}\n ref={(element) => {\n if (element) {\n tabButtonRefs.current.set(category.id, element);\n } else {\n tabButtonRefs.current.delete(category.id);\n }\n }}\n onClick={() => handleSelect(category.id)}\n disabled={loadingCategoryId !== null}\n className={cn(\n \"font-medium transition-all duration-150 hover:transition-none relative z-10 flex flex-shrink-0 items-center justify-center gap-2 whitespace-nowrap\",\n \"hover:text-gray-900 dark:hover:text-gray-100\",\n sizeClass.button,\n glassmorphic ? \"rounded-lg\" : \"\",\n isActive\n ? gradientClass.activeText\n : cn(\n \"text-gray-700 dark:text-gray-400\",\n glassmorphic && \"rounded-lg hover:bg-white/50 dark:hover:bg-white/[0.06]\",\n )\n )}\n >\n {loadingCategoryId === category.id && (\n <Spinner\n size={12}\n className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none\"\n />\n )}\n <span className={cn(\n \"flex items-center gap-2\",\n loadingCategoryId === category.id && \"invisible\"\n )}>\n {category.icon && (\n <category.icon className=\"h-4 w-4 shrink-0\" aria-hidden />\n )}\n {category.label}\n {shouldShowBadge && (\n <span\n className={cn(\n \"rounded-full\",\n sizeClass.badge,\n isActive\n ? gradientClass.activeBadge\n : \"bg-gray-200 dark:bg-gray-800 text-gray-600 dark:text-gray-400\"\n )}\n >\n {badgeValue}\n </span>\n )}\n </span>\n </button>\n );\n })}\n </div>\n {trailing != null ? (\n <div className=\"flex shrink-0 items-center\">\n {trailing}\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexclave/dashboard-ui-components",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "repository": "https://github.com/hexclave/hexclave",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -50,8 +50,8 @@
50
50
  "@tanstack/react-table": "^8.21.3",
51
51
  "@tanstack/react-virtual": "^3.13.0",
52
52
  "class-variance-authority": "^0.7.0",
53
- "@hexclave/shared": "1.0.9",
54
- "@hexclave/ui": "1.0.9"
53
+ "@hexclave/ui": "1.0.11",
54
+ "@hexclave/shared": "1.0.11"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/react": "^19.0.0",