@spear-ai/spectral 1.6.9 → 1.6.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.
@@ -101,24 +101,28 @@ const N = k(null), A = (y = "Tabs") => {
101
101
  D.div,
102
102
  {
103
103
  className: x(
104
- "pointer-events-none absolute select-none",
104
+ "pointer-events-none absolute top-0 left-0 will-change-transform select-none",
105
105
  t.orientation === "horizontal" && !t.isEnclosed && ["bottom-[1px] after:absolute after:bottom-0 after:left-0 after:w-full after:content-['']", "after:border-tabs-indicator z-10 after:rounded-t-[0.3rem] after:border-b-[0.3rem]"],
106
106
  t.orientation === "vertical" && !t.isEnclosed && ["right-[1px]", "after:absolute after:top-0 after:right-0 after:h-full after:w-1 after:content-['']", "after:border-tabs-indicator z-10 after:rounded-l-[0.3rem] after:border-r-[0.3rem]"],
107
107
  t.orientation === "horizontal" && t.isEnclosed && ["bg-tabs-enclosed-indicator z-0 rounded-lg shadow-lg"],
108
- t.orientation === "vertical" && t.isEnclosed && ["bg-tabs-enclosed-indicator right-1 left-1 z-0 rounded-lg shadow-lg"]
108
+ t.orientation === "vertical" && t.isEnclosed && ["bg-tabs-enclosed-indicator z-0 rounded-lg shadow-lg"]
109
109
  ),
110
110
  initial: !1,
111
111
  animate: {
112
- left: t.left,
113
- top: t.isEnclosed ? t.top + t.height * 0.1 : t.top,
112
+ // Use x/y (translateX/translateY) for position - compositor-friendly, no reflows
113
+ x: t.left,
114
+ y: t.isEnclosed ? t.top + t.height * 0.1 : t.top,
115
+ // Width/height required for correct rendering: scaleX/Y would distort
116
+ // border-radius on enclosed variant and border widths on default variant
114
117
  width: t.width,
115
118
  height: t.isEnclosed ? t.orientation === "vertical" ? 40 : t.height * 0.8 : t.height
116
119
  },
117
120
  transition: I ? { duration: 0 } : {
118
- type: "spring",
119
- stiffness: 500,
120
- damping: 40,
121
- mass: 1
121
+ // Separate transition configs: spring for transforms, tween for layout
122
+ x: { type: "spring", stiffness: 500, damping: 40, mass: 1 },
123
+ y: { type: "spring", stiffness: 500, damping: 40, mass: 1 },
124
+ width: { type: "spring", stiffness: 500, damping: 40, mass: 1 },
125
+ height: { type: "spring", stiffness: 500, damping: 40, mass: 1 }
122
126
  },
123
127
  "aria-hidden": "true"
124
128
  }
package/dist/Tabs.d.ts CHANGED
@@ -8,7 +8,7 @@ export interface TabValue {
8
8
  'aria-label'?: string | undefined;
9
9
  'aria-describedby'?: string | undefined;
10
10
  }
11
- export interface TabsProps extends Omit<TabsBaseProps, 'children' | 'defaultValue' | 'onError' | 'disabled'> {
11
+ export interface TabsProps extends Omit<TabsBaseProps, 'children' | 'defaultValue' | 'onError' | 'disabled' | 'value'> {
12
12
  activationMode?: 'automatic' | 'manual' | undefined;
13
13
  ariaLabel?: string | undefined;
14
14
  className?: string | undefined;
@@ -17,12 +17,23 @@ export interface TabsProps extends Omit<TabsBaseProps, 'children' | 'defaultValu
17
17
  onBeforeChange?: (newValue: string, oldValue: string) => boolean | Promise<boolean> | undefined;
18
18
  onError?: ((error: Error) => void) | undefined;
19
19
  loading?: boolean | undefined;
20
+ /**
21
+ * Initial tab to open (uncontrolled mode).
22
+ * Mutually exclusive with `value` - use one or the other.
23
+ */
20
24
  openOnLoad?: string | undefined;
21
25
  rightSlot?: ReactNode | undefined;
22
26
  tabValues: TabValue[] | undefined;
27
+ /**
28
+ * Controlled value - the currently active tab key.
29
+ * When provided, the component operates in controlled mode and the parent
30
+ * must manage the active tab state via `onValueChange`.
31
+ * Mutually exclusive with `openOnLoad` - use one or the other.
32
+ */
33
+ value?: string | undefined;
23
34
  variant?: 'enclosed' | 'default';
24
35
  }
25
- export declare const Tabs: ({ activationMode, ariaLabel, className, hideContent, loading, onBeforeChange, onError, onValueChange, openOnLoad, orientation, rightSlot, tabValues, variant, ...props }: TabsProps) => import("react/jsx-runtime").JSX.Element;
36
+ export declare const Tabs: ({ activationMode, ariaLabel, className, hideContent, loading, onBeforeChange, onError, onValueChange, openOnLoad, orientation, rightSlot, tabValues, value: controlledValue, variant, ...props }: TabsProps) => import("react/jsx-runtime").JSX.Element;
26
37
  export { TabsContent, TabsContentContainer, TabsList, TabsBase as TabsRoot, TabsTrigger };
27
38
  export type { TabsBaseProps, TabsContentContainerProps, TabsContentProps, TabsListProps, TabsTriggerProps } from './TabsBase';
28
39
  export type TabKeys<T extends TabValue[]> = T[number]['key'];
package/dist/Tabs.js CHANGED
@@ -1,80 +1,110 @@
1
1
  import "./styles/main.css";
2
- import { jsx as l, jsxs as y } from "react/jsx-runtime";
3
- import { TabsBase as z, TabsList as k, TabsTrigger as j, TabsContentContainer as C, TabsContent as V } from "./Tabs/TabsBase.js";
4
- import { validateTabValues as E, getTabKeys as K } from "./Tabs/tabsUtils.js";
2
+ import { jsx as l, jsxs as N } from "react/jsx-runtime";
3
+ import { TabsBase as j, TabsList as E, TabsTrigger as K, TabsContentContainer as $, TabsContent as A } from "./Tabs/TabsBase.js";
4
+ import { validateTabValues as B, getTabKeys as I } from "./Tabs/tabsUtils.js";
5
5
  import { cn as r } from "./utils/twUtils.js";
6
6
  import "react";
7
- const A = ({ tabCount: d, orientation: s, hideContent: n }) => /* @__PURE__ */ y("div", { className: "relative flex w-full flex-col", role: "status", children: [
8
- /* @__PURE__ */ l("div", { className: r("inline-flex w-full items-center justify-start", s === "horizontal" && "border-b border-neutral-600", s === "vertical" && "flex-col items-stretch border-r border-neutral-600"), children: Array.from({ length: Math.min(d, 5) }, (b, u) => /* @__PURE__ */ l("div", { className: r("animate-pulse rounded bg-neutral-600", s === "horizontal" ? "mx-2 my-2 h-6 w-16" : "mx-2 my-1 h-6 w-full"), "aria-hidden": "true" }, u)) }),
9
- !n && /* @__PURE__ */ l("div", { className: "mt-4 h-32 w-full animate-pulse rounded bg-neutral-600", "aria-hidden": "true" }),
7
+ const L = ({ tabCount: f, orientation: s, hideContent: v }) => /* @__PURE__ */ N("div", { className: "relative flex w-full flex-col", role: "status", children: [
8
+ /* @__PURE__ */ l("div", { className: r("inline-flex w-full items-center justify-start", s === "horizontal" && "border-b border-neutral-600", s === "vertical" && "flex-col items-stretch border-r border-neutral-600"), children: Array.from({ length: Math.min(f, 5) }, (h, x) => /* @__PURE__ */ l("div", { className: r("animate-pulse rounded bg-neutral-600", s === "horizontal" ? "mx-2 my-2 h-6 w-16" : "mx-2 my-1 h-6 w-full"), "aria-hidden": "true" }, x)) }),
9
+ !v && /* @__PURE__ */ l("div", { className: "mt-4 h-32 w-full animate-pulse rounded bg-neutral-600", "aria-hidden": "true" }),
10
10
  /* @__PURE__ */ l("span", { className: "sr-only", children: "Loading tabs…" })
11
- ] }), S = ({ activationMode: d = "automatic", ariaLabel: s, className: n, hideContent: b = !1, loading: u = !1, onBeforeChange: x, onError: f, onValueChange: m, openOnLoad: i, orientation: a = "horizontal", rightSlot: g, tabValues: p = [], variant: t = "default", ...w }) => {
12
- const c = p.filter((e) => e.key !== void 0);
13
- if (!E(c)) {
11
+ ] }), _ = ({
12
+ activationMode: f = "automatic",
13
+ ariaLabel: s,
14
+ className: v,
15
+ hideContent: h = !1,
16
+ loading: x = !1,
17
+ onBeforeChange: y,
18
+ onError: m,
19
+ onValueChange: g,
20
+ openOnLoad: o,
21
+ orientation: a = "horizontal",
22
+ rightSlot: T,
23
+ tabValues: w = [],
24
+ value: d,
25
+ variant: t = "default",
26
+ ...k
27
+ }) => {
28
+ const n = w.filter((e) => e.key !== void 0), b = d !== void 0;
29
+ if (!B(n)) {
14
30
  const e = new Error("Invalid tabValues provided to Tabs component");
15
- return f?.(e), /* @__PURE__ */ l("div", { className: "relative flex w-full flex-col p-4 text-center text-gray-500", role: "alert", "aria-live": "polite", children: /* @__PURE__ */ l("p", { children: "No valid tabs provided" }) });
31
+ return m?.(e), /* @__PURE__ */ l("div", { className: "relative flex w-full flex-col p-4 text-center text-gray-500", role: "alert", "aria-live": "polite", children: /* @__PURE__ */ l("p", { children: "No valid tabs provided" }) });
16
32
  }
17
- if (u)
18
- return /* @__PURE__ */ l(A, { tabCount: p.length || 3, orientation: a, hideContent: b });
19
- const h = K(c);
20
- let o = i ?? h[0];
21
- if (i && !h.includes(i) && (console.warn(`Tabs: openOnLoad value '${i}' is not a valid tab key`), o = h[0]), !o) {
33
+ if (x)
34
+ return /* @__PURE__ */ l(L, { tabCount: w.length || 3, orientation: a, hideContent: h });
35
+ const i = I(n);
36
+ let u;
37
+ b && (i.includes(d) ? u = d : (console.warn(`Tabs: value '${d}' is not a valid tab key, falling back to first tab`), u = i[0]));
38
+ let c;
39
+ if (!b && (c = o ?? i[0], o && !i.includes(o) && (console.warn(`Tabs: openOnLoad value '${o}' is not a valid tab key`), c = i[0]), !c)) {
22
40
  const e = new Error("No valid default tab available");
23
- return f?.(e), /* @__PURE__ */ l("div", { className: "text-text-secondary relative flex w-full flex-col p-4 text-center", role: "alert", "aria-live": "polite", children: /* @__PURE__ */ l("p", { children: "Unable to render tabs" }) });
41
+ return m?.(e), /* @__PURE__ */ l("div", { className: "text-text-secondary relative flex w-full flex-col p-4 text-center", role: "alert", "aria-live": "polite", children: /* @__PURE__ */ l("p", { children: "Unable to render tabs" }) });
24
42
  }
25
- const N = async (e) => {
26
- if (x && m) {
27
- const T = o;
43
+ const z = async (e) => {
44
+ if (y && g) {
45
+ const C = b ? u : c;
28
46
  try {
29
- if (await x(e, T) === !1) return;
30
- } catch (v) {
31
- f?.(v instanceof Error ? v : new Error("Before change callback failed"));
47
+ if (await y(e, C) === !1) return;
48
+ } catch (p) {
49
+ m?.(p instanceof Error ? p : new Error("Before change callback failed"));
32
50
  return;
33
51
  }
34
52
  }
35
- m?.(e);
53
+ g?.(e);
36
54
  };
37
- return /* @__PURE__ */ l("div", { className: "relative flex w-full flex-col", children: /* @__PURE__ */ y(z, { "aria-label": s, className: r("relative w-full data-[orientation=vertical]:flex", t, n), defaultValue: o, orientation: a, activationMode: d, onValueChange: N, ...w, children: [
38
- /* @__PURE__ */ l(
39
- k,
40
- {
41
- className: r(
42
- "inline-flex w-full items-center justify-between",
43
- a === "horizontal" && t === "default" && ["border-tabs-border border-b"],
44
- a === "vertical" && t === "default" && ["border-tabs-border relative m-0 h-auto w-auto flex-col items-stretch border-r py-1"],
45
- a === "horizontal" && t === "enclosed" && ["bg-tabs-group-bg relative rounded-lg border-0 p-1"],
46
- a === "vertical" && t === "enclosed" && ["bg-tabs-group-bg w-auto flex-col rounded-lg border-0 p-1"]
47
- ),
48
- rightSlot: g,
49
- children: c.map((e) => /* @__PURE__ */ l(
50
- j,
55
+ return /* @__PURE__ */ l("div", { className: "relative flex w-full flex-col", children: /* @__PURE__ */ N(
56
+ j,
57
+ {
58
+ "aria-label": s,
59
+ className: r("relative w-full data-[orientation=vertical]:flex", t, v),
60
+ ...b ? { value: u } : { defaultValue: c },
61
+ orientation: a,
62
+ activationMode: f,
63
+ onValueChange: z,
64
+ ...k,
65
+ children: [
66
+ /* @__PURE__ */ l(
67
+ E,
51
68
  {
52
- value: e.key,
53
- disabled: e.disabled,
54
- "aria-label": e["aria-label"],
55
- "aria-describedby": e["aria-describedby"],
56
69
  className: r(
57
- "relative font-semibold whitespace-nowrap transition-all duration-500 ease-in-out focus:outline-none focus-visible:outline-none",
58
- a === "horizontal" && t === "default" && ["text-text-primary hover:text-accent hover:bg-tabs-bg--hover px-4 py-2"],
59
- a === "vertical" && t === "default" && ["text-text-primary data-[state=active]:text-accent hover:text-accent hover:bg-tabs-bg--hover flex h-10 items-center px-4 py-2"],
60
- a === "horizontal" && t === "enclosed" && ["text-text-primary data-[state=active]:text-accent hover:text-accent hover:bg-tabs-bg--hover z-[1] inline-flex items-center justify-center px-4 py-2"],
61
- a === "vertical" && t === "enclosed" && ["text-text-primary data-[state=active]:text-accent hover:text-accent hover:bg-tabs-bg--hover z-[1] px-4 py-2"],
62
- "disabled:hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent"
70
+ "inline-flex w-full items-center justify-between",
71
+ a === "horizontal" && t === "default" && ["border-tabs-border border-b"],
72
+ a === "vertical" && t === "default" && ["border-tabs-border relative m-0 h-auto w-auto flex-col items-stretch border-r py-1"],
73
+ a === "horizontal" && t === "enclosed" && ["bg-tabs-group-bg relative rounded-lg border-0 p-1"],
74
+ a === "vertical" && t === "enclosed" && ["bg-tabs-group-bg w-auto flex-col rounded-lg border-0 p-1"]
63
75
  ),
64
- children: e.label
65
- },
66
- e.key
67
- ))
68
- }
69
- ),
70
- !b && /* @__PURE__ */ l(C, { className: r(a === "vertical" && "flex-1"), children: c.map((e) => /* @__PURE__ */ l(V, { value: e.key, className: r("focus-visible:outline-accent text-text-primary focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2", a === "horizontal" && "pb-4", a === "vertical" && "px-4 py-2"), children: e.children }, e.key)) })
71
- ] }) });
76
+ rightSlot: T,
77
+ children: n.map((e) => /* @__PURE__ */ l(
78
+ K,
79
+ {
80
+ value: e.key,
81
+ disabled: e.disabled,
82
+ "aria-label": e["aria-label"],
83
+ "aria-describedby": e["aria-describedby"],
84
+ className: r(
85
+ "relative font-semibold whitespace-nowrap transition-all duration-500 ease-in-out focus:outline-none focus-visible:outline-none",
86
+ a === "horizontal" && t === "default" && ["text-text-primary hover:text-accent hover:bg-tabs-bg--hover px-4 py-2"],
87
+ a === "vertical" && t === "default" && ["text-text-primary data-[state=active]:text-accent hover:text-accent hover:bg-tabs-bg--hover flex h-10 items-center px-4 py-2"],
88
+ a === "horizontal" && t === "enclosed" && ["text-text-primary data-[state=active]:text-accent hover:text-accent hover:bg-tabs-bg--hover z-[1] inline-flex items-center justify-center px-4 py-2"],
89
+ a === "vertical" && t === "enclosed" && ["text-text-primary data-[state=active]:text-accent hover:text-accent hover:bg-tabs-bg--hover z-[1] px-4 py-2"],
90
+ "disabled:hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent"
91
+ ),
92
+ children: e.label
93
+ },
94
+ e.key
95
+ ))
96
+ }
97
+ ),
98
+ !h && /* @__PURE__ */ l($, { className: r(a === "vertical" && "flex-1"), children: n.map((e) => /* @__PURE__ */ l(A, { value: e.key, className: r("focus-visible:outline-accent text-text-primary focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2", a === "horizontal" && "pb-4", a === "vertical" && "px-4 py-2"), children: e.children }, e.key)) })
99
+ ]
100
+ }
101
+ ) });
72
102
  };
73
103
  export {
74
- S as Tabs,
75
- V as TabsContent,
76
- C as TabsContentContainer,
77
- k as TabsList,
78
- z as TabsRoot,
79
- j as TabsTrigger
104
+ _ as Tabs,
105
+ A as TabsContent,
106
+ $ as TabsContentContainer,
107
+ E as TabsList,
108
+ j as TabsRoot,
109
+ K as TabsTrigger
80
110
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spear-ai/spectral",
3
- "version": "1.6.9",
3
+ "version": "1.6.11",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "repository": {