@spear-ai/spectral 1.16.9 → 1.17.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.
@@ -0,0 +1,331 @@
1
+ 'use client';
2
+ import { ChevronDownIcon } from "./Icons/ChevronDownIcon.js";
3
+ import { cn } from "./utils/twUtils.js";
4
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip.js";
5
+ import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { createPortal } from "react-dom";
8
+
9
+ //#region src/components/RadialMenu/RadialMenu.tsx
10
+ const BOUNCE_EASE = "motion-safe:ease-[cubic-bezier(0.34,1.56,0.64,1)]";
11
+ const LABEL_SHADOW = "[text-shadow:0_0_5px_var(--color-text-inverted),0_0_5px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_22px_var(--color-text-inverted)]";
12
+ const CLOSE_MS = 260;
13
+ const LEVEL_MS = 200;
14
+ const LABEL_OFFSET = 30;
15
+ const PRIMARY_RADIUS = 70;
16
+ const SUB_RADIUS = 96;
17
+ const ITEM_REACH = 44;
18
+ const VIEWPORT_MARGIN = 16;
19
+ const clamp = (value, min, max) => min > max ? (min + max) / 2 : Math.min(Math.max(value, min), max);
20
+ const runAfterPaint = (callback) => {
21
+ let inner = 0;
22
+ const outer = requestAnimationFrame(() => {
23
+ inner = requestAnimationFrame(callback);
24
+ });
25
+ return () => {
26
+ cancelAnimationFrame(outer);
27
+ cancelAnimationFrame(inner);
28
+ };
29
+ };
30
+ const positionForIndex = (index, count, radius) => {
31
+ const angle = -Math.PI / 2 + index * 2 * Math.PI / count;
32
+ return {
33
+ x: Math.cos(angle) * radius,
34
+ y: Math.sin(angle) * radius
35
+ };
36
+ };
37
+ const SUB_ARC_CENTER = -Math.PI / 2;
38
+ const SUB_ARC_SPAN = Math.PI * .7;
39
+ const positionOnArc = (index, count, radius) => {
40
+ const angle = count <= 1 ? SUB_ARC_CENTER : SUB_ARC_CENTER - SUB_ARC_SPAN / 2 + index / (count - 1) * SUB_ARC_SPAN;
41
+ return {
42
+ x: Math.cos(angle) * radius,
43
+ y: Math.sin(angle) * radius
44
+ };
45
+ };
46
+ const RadialMenu = ({ ariaLabel, backLabel = "Back", className, dataTestId, items, onClose, position, ref }) => {
47
+ const resolvedTestId = dataTestId ?? "spectral-radial-menu";
48
+ const containerRef = useRef(null);
49
+ const levelTimersRef = useRef([]);
50
+ const [activeSubmenuId, setActiveSubmenuId] = useState(null);
51
+ const [highlightIndex, setHighlightIndex] = useState(-1);
52
+ const [renderCenter, setRenderCenter] = useState(position);
53
+ const [expanded, setExpanded] = useState(false);
54
+ const [spreadOrigin, setSpreadOrigin] = useState({
55
+ x: 0,
56
+ y: 0
57
+ });
58
+ const [backSkin, setBackSkin] = useState(null);
59
+ const [returningId, setReturningId] = useState(null);
60
+ const clearLevelTimers = useCallback(() => {
61
+ levelTimersRef.current.forEach((id) => clearTimeout(id));
62
+ levelTimersRef.current = [];
63
+ }, []);
64
+ useEffect(() => {
65
+ clearLevelTimers();
66
+ if (position !== null) {
67
+ setRenderCenter(position);
68
+ setActiveSubmenuId(null);
69
+ setHighlightIndex(-1);
70
+ setSpreadOrigin({
71
+ x: 0,
72
+ y: 0
73
+ });
74
+ setBackSkin(null);
75
+ setReturningId(null);
76
+ setExpanded(false);
77
+ return runAfterPaint(() => {
78
+ setExpanded(true);
79
+ containerRef.current?.focus();
80
+ });
81
+ }
82
+ setExpanded(false);
83
+ setSpreadOrigin({
84
+ x: 0,
85
+ y: 0
86
+ });
87
+ const timer = setTimeout(() => {
88
+ setRenderCenter(null);
89
+ setBackSkin(null);
90
+ }, CLOSE_MS);
91
+ return () => {
92
+ clearTimeout(timer);
93
+ };
94
+ }, [clearLevelTimers, position]);
95
+ useEffect(() => {
96
+ if (position === null) return () => {};
97
+ window.addEventListener("scroll", onClose, true);
98
+ window.addEventListener("resize", onClose);
99
+ return () => {
100
+ window.removeEventListener("scroll", onClose, true);
101
+ window.removeEventListener("resize", onClose);
102
+ };
103
+ }, [position, onClose]);
104
+ const renderCenterRef = useRef(renderCenter);
105
+ renderCenterRef.current = renderCenter;
106
+ useEffect(() => {
107
+ if (renderCenterRef.current === null) return () => {};
108
+ return runAfterPaint(() => {
109
+ setExpanded(true);
110
+ });
111
+ }, [activeSubmenuId]);
112
+ useEffect(() => clearLevelTimers, [clearLevelTimers]);
113
+ const activeItems = useMemo(() => {
114
+ if (activeSubmenuId === null) return items;
115
+ return items.find((item) => item.id === activeSubmenuId)?.items ?? items;
116
+ }, [activeSubmenuId, items]);
117
+ const activate = useCallback((item) => {
118
+ if (item.disabled === true) return;
119
+ if (item.items && item.items.length > 0) {
120
+ clearLevelTimers();
121
+ const offset = positionForIndex(items.findIndex((candidate) => candidate.id === item.id), items.length, PRIMARY_RADIUS);
122
+ setSpreadOrigin(offset);
123
+ setReturningId(null);
124
+ setBackSkin({
125
+ item: {
126
+ ...item,
127
+ description: void 0,
128
+ icon: /* @__PURE__ */ jsx(ChevronDownIcon, {
129
+ className: "rotate-90",
130
+ size: 20
131
+ }),
132
+ id: `${item.id}__back`,
133
+ items: void 0,
134
+ label: backLabel
135
+ },
136
+ offset
137
+ });
138
+ setActiveSubmenuId(item.id);
139
+ setHighlightIndex(-1);
140
+ setExpanded(false);
141
+ return;
142
+ }
143
+ item.onSelect?.();
144
+ onClose();
145
+ }, [
146
+ backLabel,
147
+ clearLevelTimers,
148
+ items,
149
+ onClose
150
+ ]);
151
+ const goBack = useCallback(() => {
152
+ const returningSubmenuId = activeSubmenuId;
153
+ const index = items.findIndex((candidate) => candidate.id === returningSubmenuId);
154
+ setSpreadOrigin(index >= 0 ? positionForIndex(index, items.length, PRIMARY_RADIUS) : {
155
+ x: 0,
156
+ y: 0
157
+ });
158
+ setExpanded(false);
159
+ setHighlightIndex(-1);
160
+ clearLevelTimers();
161
+ levelTimersRef.current = [window.setTimeout(() => {
162
+ if (renderCenterRef.current === null) return;
163
+ setActiveSubmenuId(null);
164
+ setBackSkin(null);
165
+ setReturningId(returningSubmenuId);
166
+ }, LEVEL_MS), window.setTimeout(() => {
167
+ if (renderCenterRef.current !== null) setReturningId(null);
168
+ }, LEVEL_MS + 350)];
169
+ }, [
170
+ activeSubmenuId,
171
+ clearLevelTimers,
172
+ items
173
+ ]);
174
+ const handleKeyDown = useCallback((event) => {
175
+ const count = activeItems.length;
176
+ if (count === 0) return;
177
+ switch (event.key) {
178
+ case "ArrowRight":
179
+ case "ArrowDown":
180
+ event.preventDefault();
181
+ setHighlightIndex((index) => index < 0 ? 0 : (index + 1) % count);
182
+ break;
183
+ case "ArrowLeft":
184
+ case "ArrowUp":
185
+ event.preventDefault();
186
+ setHighlightIndex((index) => index < 0 ? count - 1 : (index - 1 + count) % count);
187
+ break;
188
+ case "Enter":
189
+ case " ": {
190
+ event.preventDefault();
191
+ const item = activeItems[highlightIndex];
192
+ if (item) activate(item);
193
+ break;
194
+ }
195
+ case "Escape":
196
+ event.preventDefault();
197
+ if (activeSubmenuId === null) onClose();
198
+ else goBack();
199
+ break;
200
+ case "Tab":
201
+ event.preventDefault();
202
+ break;
203
+ default: break;
204
+ }
205
+ }, [
206
+ activate,
207
+ activeItems,
208
+ activeSubmenuId,
209
+ goBack,
210
+ highlightIndex,
211
+ onClose
212
+ ]);
213
+ if (renderCenter === null || typeof document === "undefined") return null;
214
+ const inSubmenu = activeSubmenuId !== null;
215
+ const parentIndex = inSubmenu ? items.findIndex((item) => item.id === activeSubmenuId) : -1;
216
+ const base = (parentIndex >= 0 ? items[parentIndex] ?? null : null) === null ? {
217
+ x: 0,
218
+ y: 0
219
+ } : positionForIndex(parentIndex, items.length, PRIMARY_RADIUS);
220
+ const ringRadius = inSubmenu ? SUB_RADIUS : PRIMARY_RADIUS;
221
+ const reach = (inSubmenu ? PRIMARY_RADIUS + SUB_RADIUS : PRIMARY_RADIUS) + ITEM_REACH;
222
+ const cx = clamp(renderCenter.x, reach + VIEWPORT_MARGIN, window.innerWidth - reach - VIEWPORT_MARGIN);
223
+ const cy = clamp(renderCenter.y, reach + VIEWPORT_MARGIN, window.innerHeight - reach - VIEWPORT_MARGIN);
224
+ const renderSpoke = (options) => {
225
+ const { delay = 0, isActive = false, isHighlighted = false, item, nonInteractive = false, onClick, opacity, testId, x, y } = options;
226
+ const hasSubmenu = Boolean(item.items && item.items.length > 0);
227
+ const circle = /* @__PURE__ */ jsxs("button", {
228
+ "aria-disabled": item.disabled,
229
+ "aria-haspopup": hasSubmenu ? "menu" : void 0,
230
+ "aria-label": item.label,
231
+ className: cn("size-12 absolute flex -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full", "border border-border-primary bg-popover-bg text-popover-text shadow-elevation-3", "hover:scale-110 hover:cursor-pointer hover:border-toggle-border hover:bg-level-three", "motion-safe:transition-[left,top,opacity,transform] motion-safe:duration-300", BOUNCE_EASE, (isHighlighted || isActive) && "border-toggle-border bg-level-three", isHighlighted && "scale-110", item.destructive === true && "text-danger-300", item.disabled === true && "pointer-events-none hover:scale-100", nonInteractive && "pointer-events-none"),
232
+ "data-testid": testId,
233
+ disabled: item.disabled,
234
+ onClick,
235
+ role: "menuitem",
236
+ style: {
237
+ left: x,
238
+ opacity,
239
+ top: y,
240
+ transitionDelay: `${delay}ms`
241
+ },
242
+ type: "button",
243
+ children: [item.icon, hasSubmenu && /* @__PURE__ */ jsx("span", {
244
+ "aria-hidden": true,
245
+ className: "size-4 -right-0.5 -top-0.5 font-semibold absolute flex items-center justify-center rounded-full border border-border-primary bg-level-four text-[9px] leading-none text-text-primary tabular-nums",
246
+ "data-testid": `${testId}-submenu-count`,
247
+ children: item.items?.length
248
+ })]
249
+ });
250
+ return /* @__PURE__ */ jsxs(Fragment, { children: [item.description ? /* @__PURE__ */ jsxs(Tooltip, {
251
+ delayDuration: 400,
252
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, {
253
+ asChild: true,
254
+ children: circle
255
+ }), /* @__PURE__ */ jsx(TooltipContent, { children: item.description })]
256
+ }) : circle, /* @__PURE__ */ jsx("span", {
257
+ "aria-hidden": true,
258
+ className: cn("w-20 font-medium leading-tight pointer-events-none absolute -translate-x-1/2 text-center text-[10px]", LABEL_SHADOW, "motion-safe:transition-[left,top,opacity] motion-safe:duration-300", BOUNCE_EASE, item.destructive === true ? "text-danger-300" : "text-text-primary"),
259
+ "data-testid": `${testId}-label`,
260
+ style: {
261
+ left: x,
262
+ opacity,
263
+ top: y + LABEL_OFFSET,
264
+ transitionDelay: `${delay}ms`
265
+ },
266
+ children: item.label
267
+ })] }, item.id);
268
+ };
269
+ return createPortal(/* @__PURE__ */ jsx("div", {
270
+ className: "inset-0 fixed z-50 motion-safe:duration-150 motion-safe:animate-in motion-safe:fade-in-0",
271
+ "data-testid": resolvedTestId,
272
+ onContextMenu: (event) => {
273
+ event.preventDefault();
274
+ },
275
+ onPointerDown: (event) => {
276
+ if (event.target === event.currentTarget) onClose();
277
+ },
278
+ ref,
279
+ children: /* @__PURE__ */ jsxs("div", {
280
+ "aria-label": ariaLabel,
281
+ className: cn("focus-visible:ring-ring absolute origin-center focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none", className),
282
+ "data-testid": `${resolvedTestId}-menu`,
283
+ onKeyDown: handleKeyDown,
284
+ ref: containerRef,
285
+ role: "menu",
286
+ style: {
287
+ left: cx,
288
+ top: cy
289
+ },
290
+ tabIndex: -1,
291
+ children: [
292
+ !inSubmenu && /* @__PURE__ */ jsx("span", {
293
+ "aria-hidden": true,
294
+ className: "size-1.5 absolute -translate-x-1/2 -translate-y-1/2 rounded-full bg-text-secondary opacity-60",
295
+ "data-testid": `${resolvedTestId}-center`
296
+ }),
297
+ backSkin !== null && renderSpoke({
298
+ isActive: true,
299
+ item: backSkin.item,
300
+ nonInteractive: !inSubmenu,
301
+ onClick: goBack,
302
+ opacity: inSubmenu ? 1 : 0,
303
+ testId: `${resolvedTestId}-back`,
304
+ x: backSkin.offset.x,
305
+ y: backSkin.offset.y
306
+ }),
307
+ activeItems.map((item, index) => {
308
+ const offset = inSubmenu ? positionOnArc(index, activeItems.length, ringRadius) : positionForIndex(index, activeItems.length, ringRadius);
309
+ const settled = expanded || item.id === returningId;
310
+ return renderSpoke({
311
+ delay: item.id === returningId ? 0 : expanded ? index * 25 : 0,
312
+ isHighlighted: index === highlightIndex,
313
+ item,
314
+ onClick: () => {
315
+ activate(item);
316
+ },
317
+ opacity: settled ? item.disabled === true ? .4 : 1 : 0,
318
+ testId: `${resolvedTestId}-item`,
319
+ x: settled ? base.x + offset.x : spreadOrigin.x,
320
+ y: settled ? base.y + offset.y : spreadOrigin.y
321
+ });
322
+ })
323
+ ]
324
+ })
325
+ }), document.body);
326
+ };
327
+ RadialMenu.displayName = "RadialMenu";
328
+
329
+ //#endregion
330
+ export { RadialMenu };
331
+ //# sourceMappingURL=RadialMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RadialMenu.js","names":[],"sources":["../src/components/RadialMenu/RadialMenu.tsx"],"sourcesContent":["import { ChevronDownIcon } from '@components/Icons'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@components/Tooltip/Tooltip'\nimport { cn } from '@utils/twUtils'\nimport { Fragment, useCallback, useEffect, useMemo, useRef, useState, type ReactNode, type Ref } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport interface RadialMenuItem {\n /** Optional longer description shown in a tooltip on hover, since labels are kept short. */\n description?: string\n destructive?: boolean\n disabled?: boolean\n icon: ReactNode\n id: string\n items?: RadialMenuItem[]\n label: string\n onSelect?: () => void\n}\n\nexport interface RadialMenuProps {\n ariaLabel: string\n backLabel?: string\n className?: string\n dataTestId?: string\n items: RadialMenuItem[]\n onClose: () => void\n position: { x: number; y: number } | null\n}\n\nconst BOUNCE_EASE = 'motion-safe:ease-[cubic-bezier(0.34,1.56,0.64,1)]'\n// Stacked soft shadows form a dense-but-soft halo so labels stay legible over any background.\nconst LABEL_SHADOW =\n '[text-shadow:0_0_5px_var(--color-text-inverted),0_0_5px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_22px_var(--color-text-inverted)]'\nconst CLOSE_MS = 260\nconst LEVEL_MS = 200\nconst LABEL_OFFSET = 30\nconst PRIMARY_RADIUS = 70\nconst SUB_RADIUS = 96\nconst ITEM_REACH = 44\nconst VIEWPORT_MARGIN = 16\n\n// When the menu is larger than the viewport (min > max), center it; otherwise clamp normally.\nconst clamp = (value: number, min: number, max: number) => (min > max ? (min + max) / 2 : Math.min(Math.max(value, min), max))\n\n// Double rAF so the collapsed \"from\" state paints before the transition target is applied.\nconst runAfterPaint = (callback: () => void) => {\n let inner = 0\n const outer = requestAnimationFrame(() => {\n inner = requestAnimationFrame(callback)\n })\n return () => {\n cancelAnimationFrame(outer)\n cancelAnimationFrame(inner)\n }\n}\n\nconst positionForIndex = (index: number, count: number, radius: number) => {\n const angle = -Math.PI / 2 + (index * 2 * Math.PI) / count\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }\n}\n\n// Fanned across an arc centered straight up, so a sub-ring arches above its parent.\nconst SUB_ARC_CENTER = -Math.PI / 2\nconst SUB_ARC_SPAN = Math.PI * 0.7\nconst positionOnArc = (index: number, count: number, radius: number) => {\n const angle = count <= 1 ? SUB_ARC_CENTER : SUB_ARC_CENTER - SUB_ARC_SPAN / 2 + (index / (count - 1)) * SUB_ARC_SPAN\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }\n}\n\nexport const RadialMenu = ({ ariaLabel, backLabel = 'Back', className, dataTestId, items, onClose, position, ref }: RadialMenuProps & { ref?: Ref<HTMLDivElement> }) => {\n const resolvedTestId = dataTestId ?? 'spectral-radial-menu'\n const containerRef = useRef<HTMLDivElement>(null)\n const levelTimersRef = useRef<number[]>([])\n const [activeSubmenuId, setActiveSubmenuId] = useState<string | null>(null)\n const [highlightIndex, setHighlightIndex] = useState(-1)\n // Items animate between `spreadOrigin` (collapsed) and `base + ring offset` (expanded).\n const [renderCenter, setRenderCenter] = useState(position)\n const [expanded, setExpanded] = useState(false)\n const [spreadOrigin, setSpreadOrigin] = useState<{ x: number; y: number }>({ x: 0, y: 0 })\n // Parent re-skinned as a back control, kept mounted so it swaps cleanly with the real item.\n const [backSkin, setBackSkin] = useState<{ item: RadialMenuItem; offset: { x: number; y: number } } | null>(null)\n // The returned item appears instantly at its spot (swapping in for the chevron) rather than fading.\n const [returningId, setReturningId] = useState<string | null>(null)\n\n const clearLevelTimers = useCallback(() => {\n levelTimersRef.current.forEach((id) => clearTimeout(id))\n levelTimersRef.current = []\n }, [])\n\n useEffect(() => {\n // Cancel any in-flight level-change timers so a stale callback can't mutate a new cycle.\n clearLevelTimers()\n if (position !== null) {\n setRenderCenter(position)\n setActiveSubmenuId(null)\n setHighlightIndex(-1)\n setSpreadOrigin({ x: 0, y: 0 })\n setBackSkin(null)\n setReturningId(null)\n setExpanded(false)\n return runAfterPaint(() => {\n setExpanded(true)\n containerRef.current?.focus()\n })\n }\n setExpanded(false)\n // Collapse to the center, not the lingering sub-ring origin, so close mirrors open.\n setSpreadOrigin({ x: 0, y: 0 })\n const timer = setTimeout(() => {\n setRenderCenter(null)\n setBackSkin(null)\n }, CLOSE_MS)\n return () => {\n clearTimeout(timer)\n }\n }, [clearLevelTimers, position])\n\n useEffect(() => {\n if (position === null) {\n return () => {}\n }\n window.addEventListener('scroll', onClose, true)\n window.addEventListener('resize', onClose)\n return () => {\n window.removeEventListener('scroll', onClose, true)\n window.removeEventListener('resize', onClose)\n }\n }, [position, onClose])\n\n const renderCenterRef = useRef(renderCenter)\n renderCenterRef.current = renderCenter\n useEffect(() => {\n if (renderCenterRef.current === null) {\n return () => {}\n }\n return runAfterPaint(() => {\n setExpanded(true)\n })\n }, [activeSubmenuId])\n\n useEffect(() => clearLevelTimers, [clearLevelTimers])\n\n const activeItems = useMemo(() => {\n if (activeSubmenuId === null) return items\n return items.find((item) => item.id === activeSubmenuId)?.items ?? items\n }, [activeSubmenuId, items])\n\n const activate = useCallback(\n (item: RadialMenuItem) => {\n if (item.disabled === true) return\n if (item.items && item.items.length > 0) {\n clearLevelTimers()\n const index = items.findIndex((candidate) => candidate.id === item.id)\n const offset = positionForIndex(index, items.length, PRIMARY_RADIUS)\n setSpreadOrigin(offset)\n setReturningId(null)\n setBackSkin({\n item: {\n ...item,\n description: undefined,\n icon: (\n <ChevronDownIcon\n className='rotate-90'\n size={20}\n />\n ),\n id: `${item.id}__back`,\n items: undefined,\n label: backLabel,\n },\n offset,\n })\n setActiveSubmenuId(item.id)\n setHighlightIndex(-1)\n setExpanded(false)\n return\n }\n item.onSelect?.()\n onClose()\n },\n [backLabel, clearLevelTimers, items, onClose],\n )\n\n const goBack = useCallback(() => {\n const returningSubmenuId = activeSubmenuId\n const index = items.findIndex((candidate) => candidate.id === returningSubmenuId)\n setSpreadOrigin(index >= 0 ? positionForIndex(index, items.length, PRIMARY_RADIUS) : { x: 0, y: 0 })\n setExpanded(false)\n setHighlightIndex(-1)\n clearLevelTimers()\n levelTimersRef.current = [\n window.setTimeout(() => {\n if (renderCenterRef.current === null) return\n setActiveSubmenuId(null)\n setBackSkin(null)\n setReturningId(returningSubmenuId)\n }, LEVEL_MS),\n window.setTimeout(() => {\n if (renderCenterRef.current !== null) {\n setReturningId(null)\n }\n }, LEVEL_MS + 350),\n ]\n }, [activeSubmenuId, clearLevelTimers, items])\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const count = activeItems.length\n if (count === 0) return\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown': {\n event.preventDefault()\n setHighlightIndex((index) => (index < 0 ? 0 : (index + 1) % count))\n break\n }\n case 'ArrowLeft':\n case 'ArrowUp': {\n event.preventDefault()\n setHighlightIndex((index) => (index < 0 ? count - 1 : (index - 1 + count) % count))\n break\n }\n case 'Enter':\n case ' ': {\n event.preventDefault()\n const item = activeItems[highlightIndex]\n if (item) activate(item)\n break\n }\n case 'Escape': {\n event.preventDefault()\n if (activeSubmenuId === null) {\n onClose()\n } else {\n goBack()\n }\n break\n }\n case 'Tab': {\n event.preventDefault()\n break\n }\n default: {\n break\n }\n }\n },\n [activate, activeItems, activeSubmenuId, goBack, highlightIndex, onClose],\n )\n\n if (renderCenter === null || typeof document === 'undefined') {\n return null\n }\n\n const inSubmenu = activeSubmenuId !== null\n const parentIndex = inSubmenu ? items.findIndex((item) => item.id === activeSubmenuId) : -1\n const parentItem = parentIndex >= 0 ? (items[parentIndex] ?? null) : null\n const base = parentItem === null ? { x: 0, y: 0 } : positionForIndex(parentIndex, items.length, PRIMARY_RADIUS)\n const ringRadius = inSubmenu ? SUB_RADIUS : PRIMARY_RADIUS\n\n const reach = (inSubmenu ? PRIMARY_RADIUS + SUB_RADIUS : PRIMARY_RADIUS) + ITEM_REACH\n const cx = clamp(renderCenter.x, reach + VIEWPORT_MARGIN, window.innerWidth - reach - VIEWPORT_MARGIN)\n const cy = clamp(renderCenter.y, reach + VIEWPORT_MARGIN, window.innerHeight - reach - VIEWPORT_MARGIN)\n\n const renderSpoke = (options: { delay?: number; isActive?: boolean; isHighlighted?: boolean; item: RadialMenuItem; nonInteractive?: boolean; onClick: () => void; opacity: number; testId: string; x: number; y: number }) => {\n const { delay = 0, isActive = false, isHighlighted = false, item, nonInteractive = false, onClick, opacity, testId, x, y } = options\n const hasSubmenu = Boolean(item.items && item.items.length > 0)\n const circle = (\n <button\n aria-disabled={item.disabled}\n aria-haspopup={hasSubmenu ? 'menu' : undefined}\n aria-label={item.label}\n className={cn(\n 'size-12 absolute flex -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full',\n 'border border-border-primary bg-popover-bg text-popover-text shadow-elevation-3',\n 'hover:scale-110 hover:cursor-pointer hover:border-toggle-border hover:bg-level-three',\n 'motion-safe:transition-[left,top,opacity,transform] motion-safe:duration-300',\n BOUNCE_EASE,\n (isHighlighted || isActive) && 'border-toggle-border bg-level-three',\n isHighlighted && 'scale-110',\n item.destructive === true && 'text-danger-300',\n item.disabled === true && 'pointer-events-none hover:scale-100',\n nonInteractive && 'pointer-events-none',\n )}\n data-testid={testId}\n disabled={item.disabled}\n onClick={onClick}\n role='menuitem'\n style={{ left: x, opacity, top: y, transitionDelay: `${delay}ms` }}\n type='button'\n >\n {item.icon}\n {hasSubmenu && (\n <span\n aria-hidden\n className='size-4 -right-0.5 -top-0.5 font-semibold absolute flex items-center justify-center rounded-full border border-border-primary bg-level-four text-[9px] leading-none text-text-primary tabular-nums'\n data-testid={`${testId}-submenu-count`}\n >\n {item.items?.length}\n </span>\n )}\n </button>\n )\n return (\n <Fragment key={item.id}>\n {item.description ? (\n <Tooltip delayDuration={400}>\n <TooltipTrigger asChild>{circle}</TooltipTrigger>\n <TooltipContent>{item.description}</TooltipContent>\n </Tooltip>\n ) : (\n circle\n )}\n <span\n aria-hidden\n className={cn(\n 'w-20 font-medium leading-tight pointer-events-none absolute -translate-x-1/2 text-center text-[10px]',\n LABEL_SHADOW,\n 'motion-safe:transition-[left,top,opacity] motion-safe:duration-300',\n BOUNCE_EASE,\n item.destructive === true ? 'text-danger-300' : 'text-text-primary',\n )}\n data-testid={`${testId}-label`}\n style={{ left: x, opacity, top: y + LABEL_OFFSET, transitionDelay: `${delay}ms` }}\n >\n {item.label}\n </span>\n </Fragment>\n )\n }\n\n return createPortal(\n <div\n className='inset-0 fixed z-50 motion-safe:duration-150 motion-safe:animate-in motion-safe:fade-in-0'\n data-testid={resolvedTestId}\n onContextMenu={(event) => {\n event.preventDefault()\n }}\n onPointerDown={(event) => {\n if (event.target === event.currentTarget) {\n onClose()\n }\n }}\n ref={ref}\n >\n <div\n aria-label={ariaLabel}\n className={cn('focus-visible:ring-ring absolute origin-center focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none', className)}\n data-testid={`${resolvedTestId}-menu`}\n onKeyDown={handleKeyDown}\n ref={containerRef}\n role='menu'\n style={{ left: cx, top: cy }}\n tabIndex={-1}\n >\n {!inSubmenu && (\n <span\n aria-hidden\n className='size-1.5 absolute -translate-x-1/2 -translate-y-1/2 rounded-full bg-text-secondary opacity-60'\n data-testid={`${resolvedTestId}-center`}\n />\n )}\n {backSkin !== null &&\n renderSpoke({\n isActive: true,\n item: backSkin.item,\n nonInteractive: !inSubmenu,\n onClick: goBack,\n opacity: inSubmenu ? 1 : 0,\n testId: `${resolvedTestId}-back`,\n x: backSkin.offset.x,\n y: backSkin.offset.y,\n })}\n {activeItems.map((item, index) => {\n const offset = inSubmenu ? positionOnArc(index, activeItems.length, ringRadius) : positionForIndex(index, activeItems.length, ringRadius)\n const settled = expanded || item.id === returningId\n return renderSpoke({\n delay: item.id === returningId ? 0 : expanded ? index * 25 : 0,\n isHighlighted: index === highlightIndex,\n item,\n onClick: () => {\n activate(item)\n },\n opacity: settled ? (item.disabled === true ? 0.4 : 1) : 0,\n testId: `${resolvedTestId}-item`,\n x: settled ? base.x + offset.x : spreadOrigin.x,\n y: settled ? base.y + offset.y : spreadOrigin.y,\n })\n })}\n </div>\n </div>,\n document.body,\n )\n}\n\nRadialMenu.displayName = 'RadialMenu'\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { Accordion, AccordionProps } from "./Accordion.js";
3
3
  import { Alert, AlertProps } from "./Alert.js";
4
+ import { AlertDialog, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogPopupProps, AlertDialogProps, AlertDialogTitle, AlertDialogTrigger, AlertDialogTriggerProps } from "./AlertDialog.js";
4
5
  import { Avatar, AvatarProps } from "./Avatar.js";
5
6
  import { Badge, BadgeProps } from "./Badge.js";
6
7
  import { Button, ButtonProps } from "./Button.js";
@@ -110,6 +111,7 @@ import { Kbd, KbdGroup, KbdGroupProps, KbdProps, KbdSymbol } from "./Kbd.js";
110
111
  import { Label, LabelProps } from "./Label.js";
111
112
  import { MultiSelect, MultiSelectProps } from "./MultiSelect.js";
112
113
  import { Popover, PopoverAnchor, PopoverContent, PopoverContentProps, PopoverTrigger } from "./Popover.js";
114
+ import { RadialMenu, RadialMenuItem, RadialMenuProps } from "./RadialMenu.js";
113
115
  import { RadioButton, RadioButtonProps } from "./RadioButton.js";
114
116
  import { RadioButtonGroup, RadioButtonGroupItem, RadioButtonGroupItemProps, RadioButtonGroupProps } from "./RadioButtonGroup.js";
115
117
  import { RadioGroup, RadioGroupItem, RadioGroupItemProps, RadioGroupProps } from "./RadioGroup.js";
@@ -132,4 +134,4 @@ import { useControllableState } from "./hooks/useControllableState.js";
132
134
  import { useUncontrolledState } from "./hooks/useUncontrolledState.js";
133
135
  import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea } from "./primitives/input-group.js";
134
136
  import { cn } from "./utils/twUtils.js";
135
- export { Accordion, type AccordionProps, AdjustmentsIcon, Alert, type AlertProps, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, type AvatarProps, Badge, type BadgeProps, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, type ButtonGroupProps, ButtonGroupSeparator, ButtonIcon, type ButtonIconProps, ButtonIconSlideout, type ButtonIconSlideoutProps, type ButtonProps, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, type CheckboxProps, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, type ComboboxOption, type ComboboxProps, ControlGroupSelect, type ControlGroupSelectCaptionLayout, type ControlGroupSelectProps, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, type DataCardProps, DatabaseIcon, DateTimePicker, type DateTimePickerProps, DeleteIcon, Dialog, Drawer, type DrawerProps, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, type FormFieldMessageValue, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, type HoverCardProps, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, type InputNumericProps, InputOTP, type InputOTPBaseProps, type InputOTPProps, type InputProps, InputSearch, type InputSearchOption, type InputSearchProps, Kbd, KbdGroup, type KbdGroupProps, type KbdProps, type KbdSymbol, KeyboardIcon, Label, LabelIcon, type LabelProps, LassoIcon, LineToolIcon, LinkIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, type MultiSelectProps, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadioButton, RadioButtonGroup, RadioButtonGroupItem, type RadioButtonGroupItemProps, type RadioButtonGroupProps, type RadioButtonProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, type SelectExtendedProps, type SelectProps, Separator, type SeparatorProps, SettingsIcon, Skeleton, Slider, type SliderProps, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, type SpectralProviderProps, StackIcon, StarIcon, SvgIdContext, Switch, type SwitchProps, SyncIcon, SyncOffIcon, Tabs, type TabsProps, Textarea, type TextareaProps, Toast, type ToastProps, Toggle, ToggleGroup, ToggleGroupItem, type ToggleGroupItemProps, type ToggleGroupProps, ToggleGroupSplitMenuItem, type ToggleGroupSplitMenuItemProps, type ToggleProps, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, type TrayBaseProps, type TrayBodyProps, type TrayContentProps, UndoIcon, UnlinkIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
137
+ export { Accordion, type AccordionProps, AdjustmentsIcon, Alert, AlertDialog, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, type AlertDialogPopupProps, type AlertDialogProps, AlertDialogTitle, AlertDialogTrigger, type AlertDialogTriggerProps, type AlertProps, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, type AvatarProps, Badge, type BadgeProps, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, type ButtonGroupProps, ButtonGroupSeparator, ButtonIcon, type ButtonIconProps, ButtonIconSlideout, type ButtonIconSlideoutProps, type ButtonProps, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, type CheckboxProps, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, type ComboboxOption, type ComboboxProps, ControlGroupSelect, type ControlGroupSelectCaptionLayout, type ControlGroupSelectProps, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, type DataCardProps, DatabaseIcon, DateTimePicker, type DateTimePickerProps, DeleteIcon, Dialog, Drawer, type DrawerProps, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, type FormFieldMessageValue, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, type HoverCardProps, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, type InputNumericProps, InputOTP, type InputOTPBaseProps, type InputOTPProps, type InputProps, InputSearch, type InputSearchOption, type InputSearchProps, Kbd, KbdGroup, type KbdGroupProps, type KbdProps, type KbdSymbol, KeyboardIcon, Label, LabelIcon, type LabelProps, LassoIcon, LineToolIcon, LinkIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, type MultiSelectProps, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadialMenu, type RadialMenuItem, type RadialMenuProps, RadioButton, RadioButtonGroup, RadioButtonGroupItem, type RadioButtonGroupItemProps, type RadioButtonGroupProps, type RadioButtonProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, type SelectExtendedProps, type SelectProps, Separator, type SeparatorProps, SettingsIcon, Skeleton, Slider, type SliderProps, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, type SpectralProviderProps, StackIcon, StarIcon, SvgIdContext, Switch, type SwitchProps, SyncIcon, SyncOffIcon, Tabs, type TabsProps, Textarea, type TextareaProps, Toast, type ToastProps, Toggle, ToggleGroup, ToggleGroupItem, type ToggleGroupItemProps, type ToggleGroupProps, ToggleGroupSplitMenuItem, type ToggleGroupSplitMenuItemProps, type ToggleProps, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, type TrayBaseProps, type TrayBodyProps, type TrayContentProps, UndoIcon, UnlinkIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
package/dist/index.js CHANGED
@@ -88,9 +88,10 @@ import { useAccordionAutoScroll } from "./hooks/useAccordionAutoScroll.js";
88
88
  import { cn } from "./utils/twUtils.js";
89
89
  import { Accordion } from "./Accordion.js";
90
90
  import { Alert } from "./Alert.js";
91
+ import { Button } from "./Button.js";
92
+ import { AlertDialog, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogTitle, AlertDialogTrigger } from "./AlertDialog.js";
91
93
  import { Avatar } from "./Avatar.js";
92
94
  import { Badge } from "./Badge.js";
93
- import { Button } from "./Button.js";
94
95
  import { Separator } from "./Separator.js";
95
96
  import { ButtonGroup, ButtonGroupItem, ButtonGroupSeparator } from "./ButtonGroup.js";
96
97
  import { ButtonIcon } from "./ButtonIcon.js";
@@ -116,6 +117,8 @@ import { InputOTP } from "./InputOTP.js";
116
117
  import { InputSearch } from "./InputSearch.js";
117
118
  import { Kbd, KbdGroup } from "./Kbd.js";
118
119
  import { MultiSelect } from "./MultiSelect.js";
120
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip.js";
121
+ import { RadialMenu } from "./RadialMenu.js";
119
122
  import { RadioButtonGroup, RadioButtonGroupItem } from "./RadioButtonGroup.js";
120
123
  import { RadioButton } from "./RadioButton.js";
121
124
  import { RadioGroup, RadioGroupItem } from "./RadioGroup.js";
@@ -130,7 +133,6 @@ import { Toggle } from "./Toggle.js";
130
133
  import { ToggleGroupItem } from "./ToggleGroup/ToggleGroupItem.js";
131
134
  import { ToggleGroupSplitMenuItem } from "./ToggleGroup/ToggleGroupSplitMenuItem.js";
132
135
  import { ToggleGroup } from "./ToggleGroup.js";
133
- import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip.js";
134
136
  import { Tray } from "./Tray.js";
135
137
 
136
- export { Accordion, AdjustmentsIcon, Alert, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, Badge, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, ButtonGroupSeparator, ButtonIcon, ButtonIconSlideout, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, ControlGroupSelect, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, DatabaseIcon, DateTimePicker, DeleteIcon, Dialog, Drawer, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, InputOTP, InputSearch, Kbd, KbdGroup, KeyboardIcon, Label, LabelIcon, LassoIcon, LineToolIcon, LinkIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadioButton, RadioButtonGroup, RadioButtonGroupItem, RadioGroup, RadioGroupItem, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, Separator, SettingsIcon, Skeleton, Slider, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, StackIcon, StarIcon, SvgIdContext, Switch, SyncIcon, SyncOffIcon, Tabs, Textarea, Toast, Toggle, ToggleGroup, ToggleGroupItem, ToggleGroupSplitMenuItem, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, UndoIcon, UnlinkIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
138
+ export { Accordion, AdjustmentsIcon, Alert, AlertDialog, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogTitle, AlertDialogTrigger, AnalyzeIcon, AnnotationsIcon, ApprovedIcon, ArrowDownIcon, ArrowUpIcon, Avatar, Badge, BoxToolIcon, Button, ButtonGroup, ButtonGroupItem, ButtonGroupSeparator, ButtonIcon, ButtonIconSlideout, CalendarIcon, CheckCircleIcon, CheckSquareIcon, Checkbox, CheckmarkIcon, ChevronDownIcon, ChevronUpIcon, ClockIcon, CloseCircleIcon, CloseIcon, Combobox, ControlGroupSelect, Crosshairs2Icon, CrosshairsIcon, DashboardIcon, DataCard, DatabaseIcon, DateTimePicker, DeleteIcon, Dialog, Drawer, DurationIcon, EditIcon, EmailIcon, EraserIcon, ErrorIcon, ErrorMessage, EyeClosedIcon, EyeClosedIcon2, EyeOpenIcon, FileDownloadIcon, GoToFirstIcon, GoToLastIcon, HarmonicCursorsIcon, HoverCard, HoverCardContent, HoverCardTrigger, IconBase, InfoIcon, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputNumeric, InputOTP, InputSearch, Kbd, KbdGroup, KeyboardIcon, Label, LabelIcon, LassoIcon, LineToolIcon, LinkIcon, LiveViewIcon, LoaderIcon, LocationIcon, LogoutIcon, MaximizeIcon, MeasureIcon, MenuDotsIcon, MenuIcon, MessagesIcon, MetadataIcon, MinimizeIcon, MinusIcon, MultiSelect, OntologyIcon, PanelIconClose, PanelIconOpen, PauseIcon, PlayIcon, PlusIcon, PolygonIcon, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrinterIcon, ProgressCheckIcon, RadialMenu, RadioButton, RadioButtonGroup, RadioButtonGroupItem, RadioGroup, RadioGroupItem, ResetIcon, ReviewedIcon, ScissorsIcon, SearchIcon, Select, Separator, SettingsIcon, Skeleton, Slider, SortAscendingIcon, SortAtoZIcon, SortDescendingIcon, SortZtoAIcon, SparklesIcon, SpectralProvider, StackIcon, StarIcon, SvgIdContext, Switch, SyncIcon, SyncOffIcon, Tabs, Textarea, Toast, Toggle, ToggleGroup, ToggleGroupItem, ToggleGroupSplitMenuItem, Tooltip, TooltipContent, TooltipTrigger, TrashIcon, Tray, UndoIcon, UnlinkIcon, UploadIcon, User2Icon, UserIcon, WarningIcon, WarningMessage, ZoomAllIcon, ZoomXIcon, ZoomYIcon, cn, toast, useAccordionAutoScroll, useControllableState, useUncontrolledState };
@@ -73,41 +73,49 @@
73
73
  --color-badge-destructive-bg: var(--horizon-color-danger-400);
74
74
  --color-badge-destructive-border: var(--horizon-color-danger-400);
75
75
 
76
- --color-button-primary-bg: var(--horizon-color-accent);
77
- --color-button-primary-bg--hover: var(--horizon-color-level-two);
78
- --color-button-primary-bg--disabled: var(--horizon-color-neutral-500);
79
- --color-button-primary-border: var(--horizon-color-accent);
80
- --color-button-primary-border--hover: var(--horizon-color-level-two);
76
+ --color-button-primary-bg: oklch(from var(--horizon-color-accent) l c h / 0.2);
77
+ --color-button-primary-bg--hover: oklch(from var(--horizon-color-accent) l c h / 0.4);
78
+ --color-button-primary-bg--disabled: oklch(from var(--horizon-color-level-four) l c h / 0.3);
79
+ --color-button-primary-bg--loading: oklch(from var(--horizon-color-level-four) l c h / 0.3);
80
+ --color-button-primary-border: oklch(from var(--horizon-color-accent) l c h / 0.8);
81
81
  --color-button-primary-border--disabled: var(--horizon-color-neutral-500);
82
- --color-button-primary-text: var(--horizon-color-neutral-900);
82
+ --color-button-primary-border--loading: var(--horizon-color-neutral-500);
83
+ --color-button-primary-text: var(--horizon-color-accent);
83
84
  --color-button-primary-text--hover: var(--horizon-color-accent);
84
- --color-button-primary-text--disabled: var(--horizon-color-neutral-200);
85
-
86
- --color-button-secondary-bg: var(--horizon-color-level-two);
87
- --color-button-secondary-bg--hover: var(--horizon-color-accent);
88
- --color-button-secondary-bg--disabled: var(--horizon-color-neutral-500);
85
+ --color-button-primary-text--disabled: var(--horizon-color-neutral-400);
86
+ --color-button-primary-text--loading: var(--horizon-color-neutral-400);
87
+
88
+ --color-button-secondary-bg: var(--horizon-color-transparent);
89
+ --color-button-secondary-bg--hover: oklch(from var(--horizon-color-neutral-50) l c h / 0.2);
90
+ --color-button-secondary-bg--disabled: oklch(from var(--horizon-color-level-four) l c h / 0.3);
91
+ --color-button-secondary-bg--loading: var(--horizon-color-transparent);
92
+ --color-button-secondary-border: var(--horizon-color-neutral-50);
93
+ --color-button-secondary-border--hover: var(--horizon-color-neutral-50);
94
+ --color-button-secondary-border--disabled: var(--horizon-color-neutral-600);
95
+ --color-button-secondary-border--loading: var(--horizon-color-neutral-600);
89
96
  --color-button-secondary-text: var(--horizon-color-neutral-50);
90
- --color-button-secondary-text--hover: var(--horizon-color-level-two);
91
- --color-button-secondary-text--disabled: var(--horizon-color-neutral-200);
92
- --color-button-secondary-border: var(--horizon-color-neutral-900);
93
- --color-button-secondary-border--disabled: var(--horizon-color-neutral-500);
94
-
95
- --color-button-outline-bg: var(--horizon-color-transparent);
96
- --color-button-outline-bg--hover: var(--horizon-color-level-two);
97
- --color-button-outline-text: var(--horizon-color-neutral-50);
98
- --color-button-outline-text--hover: var(--horizon-color-accent);
99
- --color-button-outline-text--disabled: var(--horizon-color-neutral-400);
100
- --color-button-outline-border: var(--horizon-color-accent);
101
- --color-button-outline-border--disabled: var(--horizon-color-neutral-400);
97
+ --color-button-secondary-text--hover: var(--horizon-color-neutral-50);
98
+ --color-button-secondary-text--disabled: var(--horizon-color-neutral-400);
99
+ --color-button-secondary-text--loading: var(--horizon-color-neutral-400);
102
100
 
103
101
  --color-button-ghost-bg: var(--horizon-color-transparent);
104
102
  --color-button-ghost-bg--hover: var(--horizon-color-level-two);
105
103
  --color-button-ghost-text: var(--horizon-color-neutral-50);
106
104
  --color-button-ghost-text--hover: var(--horizon-color-accent);
107
105
  --color-button-ghost-text--disabled: var(--horizon-color-neutral-400);
108
-
109
- --color-button-danger: var(--horizon-color-danger-400);
110
- --color-button-danger--hover: oklch(from var(--horizon-color-danger-500) l c h / 0.8);
106
+ --color-button-ghost-text--loading: var(--horizon-color-neutral-400);
107
+
108
+ --color-button-destructive-bg: oklch(from var(--horizon-color-danger-400) l c h / 0.15);
109
+ --color-button-destructive-bg--hover: oklch(from var(--horizon-color-danger-400) l c h / 0.3);
110
+ --color-button-destructive-bg--disabled: oklch(from var(--horizon-color-level-four) l c h / 0.15);
111
+ --color-button-destructive-bg--loading: oklch(from var(--horizon-color-level-four) l c h / 0.15);
112
+ --color-button-destructive-border: oklch(from var(--horizon-color-danger-400) l c h / 0.6);
113
+ --color-button-destructive-border--disabled: oklch(from var(--horizon-color-danger-400) l c h / 0.6);
114
+ --color-button-destructive-border--loading: oklch(from var(--horizon-color-danger-400) l c h / 0.6);
115
+ --color-button-destructive-text: var(--horizon-color-danger-400);
116
+ --color-button-destructive-text--hover: var(--horizon-color-danger-400);
117
+ --color-button-destructive-text--disabled: oklch(from var(--horizon-color-danger-400) l c h / 0.6);
118
+ --color-button-destructive-text--loading: oklch(from var(--horizon-color-danger-400) l c h / 0.6);
111
119
 
112
120
  --color-checkbox-border: var(--horizon-color-neutral-100);
113
121
  --color-checkbox-border--focus: var(--horizon-color-accent);