@geomak/ui 5.9.0 → 6.0.0

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.cjs CHANGED
@@ -918,7 +918,8 @@ function Tooltip({
918
918
  title,
919
919
  placement = "top",
920
920
  delayDuration = 300,
921
- sideOffset = 8
921
+ sideOffset = 8,
922
+ className = ""
922
923
  }) {
923
924
  return /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive__namespace.Provider, { delayDuration, children: /* @__PURE__ */ jsxRuntime.jsxs(TooltipPrimitive__namespace.Root, { children: [
924
925
  /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex", children }) }),
@@ -938,8 +939,9 @@ function Tooltip({
938
939
  // Out animation (always the same — just fade)
939
940
  "data-[state=closed]:animate-tooltip-out",
940
941
  // In animation — direction-aware
941
- ANIMATION[placement]
942
- ].join(" "),
942
+ ANIMATION[placement],
943
+ className
944
+ ].filter(Boolean).join(" "),
943
945
  children: [
944
946
  title,
945
947
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -956,131 +958,225 @@ function Tooltip({
956
958
  ] }) });
957
959
  }
958
960
  var TooltipProvider = TooltipPrimitive__namespace.Provider;
961
+ var TabsContext = React8.createContext(null);
962
+ function useTabsContext() {
963
+ const ctx = React8.useContext(TabsContext);
964
+ if (!ctx) throw new Error("Tabs.List / Tabs.Trigger / Tabs.Panel must be rendered inside <Tabs>.");
965
+ return ctx;
966
+ }
967
+ var SIZE = {
968
+ sm: { trigger: "h-8 text-xs px-2.5", icon: "h-3.5 w-3.5", add: "h-8 w-8" },
969
+ md: { trigger: "h-10 text-sm px-3", icon: "h-4 w-4", add: "h-10 w-9" },
970
+ lg: { trigger: "h-12 text-sm px-4", icon: "h-[18px] w-[18px]", add: "h-12 w-10" }
971
+ };
972
+ var MARKER_TRANSITION = { duration: 0.26, ease: [0.16, 1, 0.3, 1] };
959
973
  function Tabs({
960
- tabs = [],
961
- onTabChange,
962
- onTabClose,
963
- isLazy,
964
- tabsClosable = true,
965
- defaultActiveTab
974
+ value,
975
+ defaultValue,
976
+ onValueChange,
977
+ variant = "underline",
978
+ size = "md",
979
+ orientation = "horizontal",
980
+ className = "",
981
+ style,
982
+ children
966
983
  }) {
967
- const [value, setValue] = React8.useState(() => defaultActiveTab ?? tabs[0]?.key ?? "");
968
- React8.useEffect(() => {
969
- if (defaultActiveTab) setValue(defaultActiveTab);
970
- }, [defaultActiveTab]);
971
- React8.useEffect(() => {
972
- if (tabs.length === 0) {
973
- setValue("");
974
- return;
975
- }
976
- const exists = tabs.find((t) => t.key === value);
977
- if (!exists) {
978
- setValue(tabs[tabs.length - 1].key);
979
- }
980
- }, [tabs, value]);
981
- const handleValueChange = (newValue) => {
982
- const prev = tabs.find((t) => t.key === value);
983
- const next = tabs.find((t) => t.key === newValue);
984
- onTabChange?.(prev, next);
985
- setValue(newValue);
986
- };
987
- const toPreviousTab = () => {
988
- const idx = tabs.findIndex((t) => t.key === value);
989
- if (idx > 0) handleValueChange(tabs[idx - 1].key);
990
- };
991
- const toNextTab = () => {
992
- const idx = tabs.findIndex((t) => t.key === value);
993
- if (idx < tabs.length - 1) handleValueChange(tabs[idx + 1].key);
984
+ const isControlled = value !== void 0;
985
+ const [internal, setInternal] = React8.useState(defaultValue);
986
+ const current = isControlled ? value : internal;
987
+ const reduced = !!framerMotion.useReducedMotion();
988
+ const indicatorId = React8.useId();
989
+ const handle = (next) => {
990
+ if (!isControlled) setInternal(next);
991
+ onValueChange?.(next);
994
992
  };
995
- if (tabs.length === 0) return null;
996
- return /* @__PURE__ */ jsxRuntime.jsxs(
993
+ return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: { value: current, variant, size, orientation, indicatorId, reduced }, children: /* @__PURE__ */ jsxRuntime.jsx(
997
994
  TabsPrimitive__namespace.Root,
995
+ {
996
+ value: current,
997
+ onValueChange: handle,
998
+ orientation,
999
+ className: [
1000
+ "flex min-w-0",
1001
+ orientation === "vertical" ? "flex-row gap-4" : "flex-col gap-3",
1002
+ className
1003
+ ].filter(Boolean).join(" "),
1004
+ style,
1005
+ children
1006
+ }
1007
+ ) });
1008
+ }
1009
+ function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
1010
+ const { variant, orientation, reduced } = useTabsContext();
1011
+ const horizontal = orientation === "horizontal";
1012
+ const scrollRef = React8.useRef(null);
1013
+ const [edges, setEdges] = React8.useState({ start: false, end: false });
1014
+ const scrollable = variant !== "segmented";
1015
+ React8.useLayoutEffect(() => {
1016
+ const el = scrollRef.current;
1017
+ if (!el || !scrollable) return;
1018
+ const update = () => {
1019
+ if (horizontal) {
1020
+ setEdges({
1021
+ start: el.scrollLeft > 1,
1022
+ end: el.scrollLeft + el.clientWidth < el.scrollWidth - 1
1023
+ });
1024
+ } else {
1025
+ setEdges({
1026
+ start: el.scrollTop > 1,
1027
+ end: el.scrollTop + el.clientHeight < el.scrollHeight - 1
1028
+ });
1029
+ }
1030
+ };
1031
+ update();
1032
+ el.addEventListener("scroll", update, { passive: true });
1033
+ const ro = new ResizeObserver(update);
1034
+ ro.observe(el);
1035
+ return () => {
1036
+ el.removeEventListener("scroll", update);
1037
+ ro.disconnect();
1038
+ };
1039
+ }, [horizontal, scrollable, children]);
1040
+ const nudge = React8.useCallback((dir) => {
1041
+ const el = scrollRef.current;
1042
+ if (!el) return;
1043
+ const amount = (horizontal ? el.clientWidth : el.clientHeight) * 0.7 * dir;
1044
+ el.scrollBy({ [horizontal ? "left" : "top"]: amount, behavior: reduced ? "auto" : "smooth" });
1045
+ }, [horizontal, reduced]);
1046
+ const maskStyle = scrollable && (edges.start || edges.end) ? (() => {
1047
+ const dir = horizontal ? "to right" : "to bottom";
1048
+ const a = edges.start ? "transparent, black 36px" : "black";
1049
+ const b = edges.end ? "black calc(100% - 36px), transparent" : "black";
1050
+ const img = `linear-gradient(${dir}, ${a}, ${b})`;
1051
+ return { maskImage: img, WebkitMaskImage: img };
1052
+ })() : {};
1053
+ const trackClass = (() => {
1054
+ if (variant === "segmented") {
1055
+ return horizontal ? "inline-flex items-center gap-1 rounded-lg border border-border bg-surface-raised p-1 w-fit" : "inline-flex flex-col items-stretch gap-1 rounded-lg border border-border bg-surface-raised p-1 w-fit";
1056
+ }
1057
+ const hairline = horizontal ? "border-b border-border" : "border-r border-border";
1058
+ const align = variant === "enclosed" && horizontal ? "items-end" : "items-stretch";
1059
+ return `flex ${horizontal ? "flex-row" : "flex-col"} ${align} gap-1 ${hairline}`;
1060
+ })();
1061
+ const scrollClass = scrollable ? horizontal ? "overflow-x-auto overflow-y-hidden hidden-scrollbar" : "overflow-y-auto overflow-x-hidden hidden-scrollbar" : "";
1062
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["relative flex min-w-0", horizontal ? "flex-row items-stretch" : "flex-col items-stretch", className].filter(Boolean).join(" "), children: [
1063
+ scrollable && edges.start && /* @__PURE__ */ jsxRuntime.jsx(Chevron, { side: "start", orientation, onClick: () => nudge(-1) }),
1064
+ /* @__PURE__ */ jsxRuntime.jsx(
1065
+ TabsPrimitive__namespace.List,
1066
+ {
1067
+ ref: scrollRef,
1068
+ "aria-label": ariaLabel,
1069
+ className: [scrollClass, trackClass, "min-w-0 flex-1"].filter(Boolean).join(" "),
1070
+ style: maskStyle,
1071
+ children
1072
+ }
1073
+ ),
1074
+ scrollable && edges.end && /* @__PURE__ */ jsxRuntime.jsx(Chevron, { side: "end", orientation, onClick: () => nudge(1) })
1075
+ ] });
1076
+ }
1077
+ function Chevron({ side, orientation, onClick }) {
1078
+ const horizontal = orientation === "horizontal";
1079
+ const rotate = horizontal ? side === "start" ? "rotate-180" : "" : side === "start" ? "-rotate-90" : "rotate-90";
1080
+ const pos = horizontal ? side === "start" ? "left-0 top-1/2 -translate-y-1/2" : "right-0 top-1/2 -translate-y-1/2" : side === "start" ? "top-0 left-1/2 -translate-x-1/2" : "bottom-0 left-1/2 -translate-x-1/2";
1081
+ return /* @__PURE__ */ jsxRuntime.jsx(
1082
+ "button",
1083
+ {
1084
+ type: "button",
1085
+ "aria-label": side === "start" ? "Scroll tabs backward" : "Scroll tabs forward",
1086
+ onClick,
1087
+ className: `absolute z-20 ${pos} flex h-7 w-7 items-center justify-center rounded-full border border-border bg-surface text-foreground-secondary shadow-sm hover:text-foreground hover:bg-surface-raised transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent`,
1088
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: `h-4 w-4 ${rotate}`, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) })
1089
+ }
1090
+ );
1091
+ }
1092
+ function TabsTrigger({ value, icon, badge, closeable, onClose, disabled, className = "", children }) {
1093
+ const { value: active, variant, size, orientation, indicatorId, reduced } = useTabsContext();
1094
+ const isActive = active === value;
1095
+ const horizontal = orientation === "horizontal";
1096
+ const sz = SIZE[size];
1097
+ const base = "group/trigger relative inline-flex items-center justify-center whitespace-nowrap font-medium select-none transition-colors duration-150 focus:outline-none disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0";
1098
+ const variantCls = variant === "segmented" ? `rounded-md ${isActive ? "text-accent" : "text-foreground-secondary hover:text-foreground"} focus-visible:text-accent` : variant === "enclosed" ? `${horizontal ? "rounded-t-md border border-b-0 -mb-px" : "rounded-l-md border border-r-0 -mr-px"} ${isActive ? "bg-surface border-border text-foreground" : "border-transparent text-foreground-secondary hover:text-foreground hover:bg-surface-raised"} focus-visible:text-accent` : `${isActive ? "text-accent" : "text-foreground-secondary hover:text-foreground"} focus-visible:text-accent`;
1099
+ const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
1100
+ TabsPrimitive__namespace.Trigger,
998
1101
  {
999
1102
  value,
1000
- onValueChange: handleValueChange,
1001
- className: "h-full max-w-full flex flex-col gap-2",
1103
+ disabled,
1104
+ className: [base, sz.trigger, closeable ? "pr-8" : "", variantCls, className].filter(Boolean).join(" "),
1002
1105
  children: [
1003
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-surface border border-border rounded-lg flex items-center justify-between flex-shrink-0 w-full p-1 overflow-hidden", children: [
1004
- /* @__PURE__ */ jsxRuntime.jsx(
1005
- "button",
1006
- {
1007
- type: "button",
1008
- onClick: toPreviousTab,
1009
- "aria-label": "Previous tab",
1010
- className: "cursor-pointer rounded-lg transition-colors duration-150 hover:bg-surface-raised text-foreground-secondary hover:text-foreground rotate-180 flex-shrink-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
1011
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-6 w-6", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) })
1012
- }
1013
- ),
1014
- /* @__PURE__ */ jsxRuntime.jsx(
1015
- TabsPrimitive__namespace.List,
1016
- {
1017
- "aria-label": "Tabs",
1018
- className: "flex-1 flex items-center gap-1 overflow-x-auto overflow-y-hidden rounded-lg scroll-smooth snap-x snap-mandatory hidden-scrollbar",
1019
- children: tabs.map((tab) => (
1020
- // Trigger + close button are SIBLINGS, not nested.
1021
- // Nesting a clickable element inside <button> is invalid
1022
- // HTML and breaks keyboard activation of the inner one.
1023
- // The wrapper carries `group` so the close button can
1024
- // react to the trigger's `data-state=active` for styling.
1025
- /* @__PURE__ */ jsxRuntime.jsxs(
1026
- "div",
1027
- {
1028
- className: "snap-start snap-always relative flex items-center flex-1 min-w-[120px] max-w-[220px] flex-shrink-0 group",
1029
- children: [
1030
- /* @__PURE__ */ jsxRuntime.jsx(
1031
- TabsPrimitive__namespace.Trigger,
1032
- {
1033
- value: tab.key,
1034
- className: `w-full ${tabsClosable ? "pr-8" : "pr-3"} pl-3 py-2 rounded-3xl cursor-pointer transition-all duration-200 select-none h-10 text-left
1035
- text-foreground-secondary bg-surface-raised
1036
- hover:bg-surface hover:text-foreground
1037
- data-[state=active]:bg-accent data-[state=active]:text-accent-foreground
1038
- focus:outline-none focus-visible:ring-2 focus-visible:ring-accent`,
1039
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm block", children: tab.title })
1040
- }
1041
- ),
1042
- tabsClosable && /* @__PURE__ */ jsxRuntime.jsx(
1043
- "button",
1044
- {
1045
- type: "button",
1046
- "aria-label": `Close ${tab.title}`,
1047
- onClick: (e) => {
1048
- e.stopPropagation();
1049
- onTabClose?.(tab.key);
1050
- },
1051
- className: "absolute right-1.5 top-1/2 -translate-y-1/2 rounded p-0.5 text-foreground-secondary group-data-[state=active]:text-accent-foreground hover:bg-black/10 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
1052
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1053
- }
1054
- )
1055
- ]
1056
- },
1057
- tab.key
1058
- )
1059
- ))
1060
- }
1061
- ),
1062
- /* @__PURE__ */ jsxRuntime.jsx(
1063
- "button",
1064
- {
1065
- type: "button",
1066
- onClick: toNextTab,
1067
- "aria-label": "Next tab",
1068
- className: "cursor-pointer rounded-lg transition-colors duration-150 hover:bg-surface-raised text-foreground-secondary hover:text-foreground flex-shrink-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
1069
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-6 w-6", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) })
1070
- }
1071
- )
1106
+ variant === "segmented" && isActive && /* @__PURE__ */ jsxRuntime.jsx(
1107
+ framerMotion.motion.span,
1108
+ {
1109
+ layoutId: `${indicatorId}-seg`,
1110
+ className: "absolute inset-0 rounded-md bg-surface shadow-sm",
1111
+ transition: reduced ? { duration: 0 } : MARKER_TRANSITION,
1112
+ "aria-hidden": "true"
1113
+ }
1114
+ ),
1115
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative z-[1] inline-flex items-center gap-2 min-w-0", children: [
1116
+ icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `flex-shrink-0 inline-flex items-center justify-center ${sz.icon}`, children: icon }),
1117
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children }),
1118
+ badge != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-border px-1 text-[11px] font-semibold leading-none text-foreground-secondary group-data-[state=active]/trigger:bg-accent group-data-[state=active]/trigger:text-accent-foreground transition-colors", children: badge })
1072
1119
  ] }),
1073
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 rounded-lg w-full flex-1 min-h-0 bg-surface border border-border overflow-hidden", children: isLazy ? (
1074
- // Mount only the active content
1075
- tabs.filter((t) => t.key === value).map((t) => /* @__PURE__ */ jsxRuntime.jsx(TabsPrimitive__namespace.Content, { value: t.key, className: "w-full h-full focus:outline-none", children: t.content }, t.key))
1076
- ) : (
1077
- // Pre-mount all, hide non-active via Radix
1078
- tabs.map((t) => /* @__PURE__ */ jsxRuntime.jsx(TabsPrimitive__namespace.Content, { value: t.key, className: "w-full h-full focus:outline-none", forceMount: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full ${t.key === value ? "block" : "hidden"}`, children: t.content }) }, t.key))
1079
- ) })
1120
+ variant === "underline" && isActive && /* @__PURE__ */ jsxRuntime.jsx(
1121
+ framerMotion.motion.span,
1122
+ {
1123
+ layoutId: `${indicatorId}-line`,
1124
+ className: horizontal ? "absolute left-2 right-2 bottom-0 h-0.5 rounded-full bg-accent" : "absolute top-1.5 bottom-1.5 right-0 w-0.5 rounded-full bg-accent",
1125
+ transition: reduced ? { duration: 0 } : MARKER_TRANSITION,
1126
+ "aria-hidden": "true"
1127
+ }
1128
+ )
1080
1129
  ]
1081
1130
  }
1082
1131
  );
1132
+ if (!closeable) return trigger;
1133
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex items-center flex-shrink-0", children: [
1134
+ trigger,
1135
+ /* @__PURE__ */ jsxRuntime.jsx(
1136
+ "button",
1137
+ {
1138
+ type: "button",
1139
+ "aria-label": "Close tab",
1140
+ onClick: (e) => {
1141
+ e.stopPropagation();
1142
+ onClose?.();
1143
+ },
1144
+ className: "absolute right-1.5 top-1/2 z-[2] -translate-y-1/2 inline-flex h-5 w-5 items-center justify-center rounded text-foreground-muted hover:text-status-error hover:bg-surface-raised transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-accent",
1145
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
1146
+ }
1147
+ )
1148
+ ] });
1149
+ }
1150
+ function TabsAdd({ onClick, "aria-label": ariaLabel = "Add tab", className = "" }) {
1151
+ const { size } = useTabsContext();
1152
+ return /* @__PURE__ */ jsxRuntime.jsx(
1153
+ "button",
1154
+ {
1155
+ type: "button",
1156
+ onClick,
1157
+ "aria-label": ariaLabel,
1158
+ className: `flex-shrink-0 inline-flex items-center justify-center rounded-md text-foreground-muted hover:text-foreground hover:bg-surface-raised transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${SIZE[size].add} ${className}`.trim(),
1159
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 5v14M5 12h14" }) })
1160
+ }
1161
+ );
1083
1162
  }
1163
+ function TabsPanel({ value, keepMounted, className = "", style, children }) {
1164
+ return /* @__PURE__ */ jsxRuntime.jsx(
1165
+ TabsPrimitive__namespace.Content,
1166
+ {
1167
+ value,
1168
+ forceMount: keepMounted || void 0,
1169
+ className: ["min-w-0 flex-1 focus:outline-none data-[state=inactive]:hidden", className].filter(Boolean).join(" "),
1170
+ style,
1171
+ children
1172
+ }
1173
+ );
1174
+ }
1175
+ Tabs.List = TabsList;
1176
+ Tabs.Trigger = TabsTrigger;
1177
+ Tabs.Panel = TabsPanel;
1178
+ Tabs.Add = TabsAdd;
1179
+ var Tabs_default = Tabs;
1084
1180
  var isParent = (item) => Boolean(item.children && item.children.length > 0);
1085
1181
  function TreeNodeItem({
1086
1182
  item,
@@ -1172,9 +1268,11 @@ function Tree({
1172
1268
  nodes,
1173
1269
  onNodeClick,
1174
1270
  defaultExpandAll = false,
1175
- defaultExpandedKeys = []
1271
+ defaultExpandedKeys = [],
1272
+ className = "",
1273
+ style
1176
1274
  }) {
1177
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-1 w-full", children: nodes.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
1275
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `p-1 w-full ${className}`.trim(), style, children: nodes.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
1178
1276
  TreeNodeItem,
1179
1277
  {
1180
1278
  item,
@@ -1441,7 +1539,8 @@ function LoadingSpinner({
1441
1539
  inline = false,
1442
1540
  spinnerColor,
1443
1541
  textColor,
1444
- backdropOpacity = 0.8
1542
+ backdropOpacity = 0.8,
1543
+ className = ""
1445
1544
  }) {
1446
1545
  const reduced = framerMotion.useReducedMotion();
1447
1546
  const letters = prompt ? Array.from(prompt) : [];
@@ -1476,7 +1575,7 @@ function LoadingSpinner({
1476
1575
  role: "status",
1477
1576
  "aria-live": "polite",
1478
1577
  "aria-label": prompt ?? "Loading",
1479
- className: "flex flex-col items-center justify-center gap-3",
1578
+ className: `flex flex-col items-center justify-center gap-3 ${className}`.trim(),
1480
1579
  children: content
1481
1580
  }
1482
1581
  );
@@ -1487,7 +1586,7 @@ function LoadingSpinner({
1487
1586
  role: "status",
1488
1587
  "aria-live": "polite",
1489
1588
  "aria-label": prompt ?? "Loading",
1490
- className: "fixed inset-0 z-[8000000] flex flex-col items-center justify-center gap-6 bg-background backdrop-blur-sm",
1589
+ className: `fixed inset-0 z-[8000000] flex flex-col items-center justify-center gap-6 bg-background backdrop-blur-sm ${className}`.trim(),
1491
1590
  style: { opacity: backdropOpacity },
1492
1591
  children: content
1493
1592
  }
@@ -1596,7 +1695,8 @@ function ScalableContainer({
1596
1695
  assignClassOnClick,
1597
1696
  expandIcon,
1598
1697
  collapseIcon,
1599
- togglePosition = "top-right"
1698
+ togglePosition = "top-right",
1699
+ className = ""
1600
1700
  }) {
1601
1701
  const containerRef = React8.useRef(null);
1602
1702
  const [internalScaled, setInternalScaled] = React8.useState(false);
@@ -1631,8 +1731,9 @@ function ScalableContainer({
1631
1731
  // OS-window aesthetic: subtle elevation at rest, lifted shadow
1632
1732
  // when expanded. No background colour change.
1633
1733
  isScaled ? "shadow-2xl" : "shadow-md",
1634
- "transition-shadow duration-300"
1635
- ].join(" "),
1734
+ "transition-shadow duration-300",
1735
+ className
1736
+ ].filter(Boolean).join(" "),
1636
1737
  children: [
1637
1738
  /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { placement: "bottom", title: isScaled ? "Collapse" : "Expand", children: /* @__PURE__ */ jsxRuntime.jsx(
1638
1739
  "button",
@@ -1684,12 +1785,13 @@ function OpaqueGridCard({
1684
1785
  item,
1685
1786
  isRight = false,
1686
1787
  buttonText = "Open Application",
1687
- onOpen
1788
+ onOpen,
1789
+ className = ""
1688
1790
  }) {
1689
1791
  return /* @__PURE__ */ jsxRuntime.jsxs(
1690
1792
  "div",
1691
1793
  {
1692
- className: `flex flex-col w-[200px] h-[250px] rounded-lg items-center p-2 ${!isRight ? "opaque-carousel-card-left dark:opaque-carousel-card-dark-left" : "opaque-carousel-card-right dark:opaque-carousel-card-dark-right"}`,
1794
+ className: `flex flex-col w-[200px] h-[250px] rounded-lg items-center p-2 ${!isRight ? "opaque-carousel-card-left dark:opaque-carousel-card-dark-left" : "opaque-carousel-card-right dark:opaque-carousel-card-dark-right"} ${className}`.trim(),
1693
1795
  children: [
1694
1796
  /* @__PURE__ */ jsxRuntime.jsx(
1695
1797
  "div",
@@ -1731,10 +1833,10 @@ function OpaqueGridCard({
1731
1833
  }
1732
1834
  );
1733
1835
  }
1734
- function CatalogGrid({ items, buttonText, onOpen }) {
1735
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(GridCard, { item, buttonText, onOpen }, item.key)) });
1836
+ function CatalogGrid({ items, buttonText, onOpen, className = "" }) {
1837
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex flex-wrap gap-2 ${className}`.trim(), children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(GridCard, { item, buttonText, onOpen }, item.key)) });
1736
1838
  }
1737
- function CatalogCarousel({ items, buttonText, onOpen }) {
1839
+ function CatalogCarousel({ items, buttonText, onOpen, className = "" }) {
1738
1840
  const [activeIndex, setActiveIndex] = React8.useState(0);
1739
1841
  const [indexPool, setIndexPool] = React8.useState([]);
1740
1842
  const cardRefs = React8.useRef([]);
@@ -1771,7 +1873,7 @@ function CatalogCarousel({ items, buttonText, onOpen }) {
1771
1873
  }, [activeIndex, getIndexes, items.length]);
1772
1874
  const nextApp = () => setActiveIndex((prev) => prev + 1 === items.length ? 0 : prev + 1);
1773
1875
  const previousApp = () => setActiveIndex((prev) => prev - 1 === -1 ? items.length - 1 : prev - 1);
1774
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-full h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-10", children: [
1876
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center justify-center w-full h-full ${className}`.trim(), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-10", children: [
1775
1877
  /* @__PURE__ */ jsxRuntime.jsx(
1776
1878
  "button",
1777
1879
  {
@@ -1821,16 +1923,16 @@ function CatalogCarousel({ items, buttonText, onOpen }) {
1821
1923
  )
1822
1924
  ] }) });
1823
1925
  }
1824
- function Catalog({ display: display2 = "grid", items = [], buttonText, onOpen }) {
1825
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full", children: display2 === "grid" ? /* @__PURE__ */ jsxRuntime.jsx(CatalogGrid, { items, buttonText, onOpen }) : /* @__PURE__ */ jsxRuntime.jsx(CatalogCarousel, { items, buttonText, onOpen }) });
1926
+ function Catalog({ display: display2 = "grid", items = [], buttonText, onOpen, className = "" }) {
1927
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full ${className}`.trim(), children: display2 === "grid" ? /* @__PURE__ */ jsxRuntime.jsx(CatalogGrid, { items, buttonText, onOpen }) : /* @__PURE__ */ jsxRuntime.jsx(CatalogCarousel, { items, buttonText, onOpen }) });
1826
1928
  }
1827
- function ContextMenu({ items, children }) {
1929
+ function ContextMenu({ items, children, className = "" }) {
1828
1930
  return /* @__PURE__ */ jsxRuntime.jsxs(ContextMenuPrimitive__namespace.Root, { children: [
1829
1931
  /* @__PURE__ */ jsxRuntime.jsx(ContextMenuPrimitive__namespace.Trigger, { asChild: true, children }),
1830
1932
  /* @__PURE__ */ jsxRuntime.jsx(ContextMenuPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
1831
1933
  ContextMenuPrimitive__namespace.Content,
1832
1934
  {
1833
- className: CONTENT_CLASSNAME,
1935
+ className: `${CONTENT_CLASSNAME} ${className}`.trim(),
1834
1936
  collisionPadding: 8,
1835
1937
  children: items.map((item) => renderItem(item))
1836
1938
  }
@@ -3015,9 +3117,9 @@ function TableSkeletonBody({
3015
3117
  i
3016
3118
  )) });
3017
3119
  }
3018
- function ThemeSwitch({ checked, onChange, label = "Toggle dark mode" }) {
3120
+ function ThemeSwitch({ checked, onChange, label = "Toggle dark mode", className = "" }) {
3019
3121
  const id = React8.useId();
3020
- return /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "flex items-center gap-2 cursor-pointer select-none", children: /* @__PURE__ */ jsxRuntime.jsx(
3122
+ return /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: `flex items-center gap-2 cursor-pointer select-none ${className}`.trim(), children: /* @__PURE__ */ jsxRuntime.jsx(
3021
3123
  SwitchPrimitive__namespace.Root,
3022
3124
  {
3023
3125
  id,
@@ -4639,7 +4741,8 @@ function DatePicker({
4639
4741
  format = defaultFormat,
4640
4742
  weekStartsOn = 0,
4641
4743
  clearable = true,
4642
- size = "md"
4744
+ size = "md",
4745
+ className = ""
4643
4746
  }) {
4644
4747
  const errorId = React8.useId();
4645
4748
  const hasError = errorMessage != null;
@@ -4720,7 +4823,7 @@ function DatePicker({
4720
4823
  }
4721
4824
  };
4722
4825
  const displayValue = value ? format(value) : "";
4723
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
4826
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col gap-1 ${className}`.trim(), children: [
4724
4827
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1.5" : "flex-row items-start gap-3"}`, children: [
4725
4828
  /* @__PURE__ */ jsxRuntime.jsx(
4726
4829
  FieldLabel,
@@ -5004,7 +5107,7 @@ function TextArea({
5004
5107
  }
5005
5108
  );
5006
5109
  }
5007
- var SIZE = {
5110
+ var SIZE2 = {
5008
5111
  sm: { h: "h-control-sm", text: "text-xs", pad: "px-2.5" },
5009
5112
  md: { h: "h-control-md", text: "text-sm", pad: "px-3.5" },
5010
5113
  lg: { h: "h-control-lg", text: "text-sm", pad: "px-4" }
@@ -5026,7 +5129,7 @@ function SegmentedControl({
5026
5129
  errorMessage,
5027
5130
  "aria-label": ariaLabel
5028
5131
  }) {
5029
- const sz = SIZE[size];
5132
+ const sz = SIZE2[size];
5030
5133
  const groupId = React8.useId();
5031
5134
  const errorId = React8.useId();
5032
5135
  const hasError = errorMessage != null;
@@ -6695,7 +6798,7 @@ exports.SkeletonText = SkeletonText;
6695
6798
  exports.Slider = Slider;
6696
6799
  exports.Switch = Switch;
6697
6800
  exports.Table = Table;
6698
- exports.Tabs = Tabs;
6801
+ exports.Tabs = Tabs_default;
6699
6802
  exports.TagsInput = TagsInput;
6700
6803
  exports.Temporal = DatePicker;
6701
6804
  exports.TextArea = TextArea;