@timbal-ai/timbal-react 1.4.0 → 1.6.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/CHANGELOG.md +30 -0
- package/README.md +43 -4
- package/dist/app.cjs +3770 -1506
- package/dist/app.d.cts +76 -31
- package/dist/app.d.ts +76 -31
- package/dist/app.esm.js +30 -8
- package/dist/{chart-artifact-C8-Py6lc.d.cts → chart-artifact-C2pZQsaP.d.ts} +247 -41
- package/dist/{chart-artifact-CMnDys2t.d.ts → chart-artifact-VAqgH-My.d.cts} +247 -41
- package/dist/{chat-ClmzWzCX.d.cts → chat-DDsp-Vzz.d.cts} +1 -1
- package/dist/{chat-ClmzWzCX.d.ts → chat-DDsp-Vzz.d.ts} +1 -1
- package/dist/chat.cjs +280 -123
- package/dist/chat.d.cts +3 -3
- package/dist/chat.d.ts +3 -3
- package/dist/chat.esm.js +4 -3
- package/dist/chunk-24B4I4XC.esm.js +232 -0
- package/dist/{chunk-VOWNCS3F.esm.js → chunk-6SQMTBPL.esm.js} +1669 -504
- package/dist/chunk-EDEKQYSU.esm.js +10 -0
- package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
- package/dist/{chunk-THBA27QY.esm.js → chunk-HSL36SJ4.esm.js} +243 -124
- package/dist/chunk-JJOO4PR5.esm.js +391 -0
- package/dist/{chunk-QU7ET55D.esm.js → chunk-MBS7XHV2.esm.js} +335 -192
- package/dist/chunk-NO5AWNWT.esm.js +1066 -0
- package/dist/{chunk-VXMM2HX7.esm.js → chunk-R4RQT2XQ.esm.js} +3 -3
- package/dist/{chunk-OFWC4MIY.esm.js → chunk-TMP7RIA7.esm.js} +5 -3
- package/dist/{chunk-GQBYZRD7.esm.js → chunk-WQIQW7EM.esm.js} +40 -28
- package/dist/{chunk-OH23AX2V.esm.js → chunk-YYEI6XME.esm.js} +441 -957
- package/dist/{circular-progress-Ci8L-Hfa.d.cts → circular-progress-B9nnwzCu.d.cts} +20 -78
- package/dist/{circular-progress-Ci8L-Hfa.d.ts → circular-progress-B9nnwzCu.d.ts} +20 -78
- package/dist/index.cjs +5547 -3192
- package/dist/index.d.cts +10 -8
- package/dist/index.d.ts +10 -8
- package/dist/index.esm.js +76 -44
- package/dist/kanban-FFBeaZPS.d.cts +212 -0
- package/dist/kanban-FFBeaZPS.d.ts +212 -0
- package/dist/{layout-BTJyU8wd.d.ts → layout-CuKeSY74.d.ts} +1 -1
- package/dist/{layout-C2G-FcER.d.cts → layout-PzVwkJyL.d.cts} +1 -1
- package/dist/site.cjs +429 -0
- package/dist/site.d.cts +198 -0
- package/dist/site.d.ts +198 -0
- package/dist/site.esm.js +23 -0
- package/dist/studio.cjs +722 -363
- package/dist/studio.d.cts +2 -2
- package/dist/studio.d.ts +2 -2
- package/dist/studio.esm.js +8 -6
- package/dist/styles.css +56 -0
- package/dist/{timbal-v2-button-CNfdwGq4.d.cts → timbal-v2-button-DCAZNyUx.d.cts} +3 -3
- package/dist/{timbal-v2-button-CNfdwGq4.d.ts → timbal-v2-button-DCAZNyUx.d.ts} +3 -3
- package/dist/ui.cjs +1553 -708
- package/dist/ui.d.cts +11 -4
- package/dist/ui.d.ts +11 -4
- package/dist/ui.esm.js +45 -36
- package/dist/{welcome-DXqsGTwH.d.ts → welcome-B00oH5Io.d.cts} +5 -1
- package/dist/{welcome-BFGRoNfK.d.cts → welcome-DU-4NTjZ.d.ts} +5 -1
- package/package.json +9 -1
- package/dist/button-BoyX5pM_.d.cts +0 -18
- package/dist/button-BoyX5pM_.d.ts +0 -18
- package/dist/chunk-UCGVL7ZY.esm.js +0 -52
|
@@ -0,0 +1,1066 @@
|
|
|
1
|
+
import {
|
|
2
|
+
controlClass,
|
|
3
|
+
overlayItemClass,
|
|
4
|
+
overlayListPanelClass,
|
|
5
|
+
overlaySurfaceClass
|
|
6
|
+
} from "./chunk-MBS7XHV2.esm.js";
|
|
7
|
+
import {
|
|
8
|
+
cn
|
|
9
|
+
} from "./chunk-EDEKQYSU.esm.js";
|
|
10
|
+
|
|
11
|
+
// src/ui/copy-button.tsx
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
function CopyButton({
|
|
16
|
+
value,
|
|
17
|
+
timeout = 1500,
|
|
18
|
+
onCopied,
|
|
19
|
+
className,
|
|
20
|
+
children,
|
|
21
|
+
onClick,
|
|
22
|
+
...props
|
|
23
|
+
}) {
|
|
24
|
+
const [copied, setCopied] = React.useState(false);
|
|
25
|
+
const timer = React.useRef(void 0);
|
|
26
|
+
React.useEffect(() => () => clearTimeout(timer.current), []);
|
|
27
|
+
const handleClick = async (event) => {
|
|
28
|
+
onClick?.(event);
|
|
29
|
+
if (event.defaultPrevented) return;
|
|
30
|
+
try {
|
|
31
|
+
await navigator.clipboard.writeText(value);
|
|
32
|
+
setCopied(true);
|
|
33
|
+
onCopied?.(value);
|
|
34
|
+
clearTimeout(timer.current);
|
|
35
|
+
timer.current = setTimeout(() => setCopied(false), timeout);
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const Icon = copied ? CheckIcon : CopyIcon;
|
|
40
|
+
return /* @__PURE__ */ jsxs(
|
|
41
|
+
"button",
|
|
42
|
+
{
|
|
43
|
+
type: "button",
|
|
44
|
+
"data-slot": "copy-button",
|
|
45
|
+
"data-copied": copied || void 0,
|
|
46
|
+
"aria-label": copied ? "Copied" : "Copy",
|
|
47
|
+
onClick: handleClick,
|
|
48
|
+
className: cn(
|
|
49
|
+
"inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium text-muted-foreground transition-colors",
|
|
50
|
+
"hover:bg-accent hover:text-foreground data-[copied=true]:text-emerald-600 dark:data-[copied=true]:text-emerald-400",
|
|
51
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
|
|
52
|
+
children ? "h-7 px-1.5" : "size-7",
|
|
53
|
+
className
|
|
54
|
+
),
|
|
55
|
+
...props,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0", "aria-hidden": true }),
|
|
58
|
+
children
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/ui/checkbox.tsx
|
|
65
|
+
import { Checkbox as CheckboxPrimitive } from "radix-ui";
|
|
66
|
+
import { CheckIcon as CheckIcon2 } from "lucide-react";
|
|
67
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
68
|
+
function Checkbox({
|
|
69
|
+
className,
|
|
70
|
+
...props
|
|
71
|
+
}) {
|
|
72
|
+
return /* @__PURE__ */ jsx2(
|
|
73
|
+
CheckboxPrimitive.Root,
|
|
74
|
+
{
|
|
75
|
+
"data-slot": "checkbox",
|
|
76
|
+
className: cn(
|
|
77
|
+
"peer size-4 shrink-0 rounded-[4px] border border-border bg-gradient-to-b from-elevated-from to-elevated-to shadow-card outline-none transition-[box-shadow,background-color,border-color]",
|
|
78
|
+
"focus-visible:ring-2 focus-visible:ring-foreground/10 disabled:cursor-not-allowed disabled:opacity-50",
|
|
79
|
+
"data-[state=checked]:border-foreground/15 data-[state=checked]:from-primary-fill-from data-[state=checked]:to-primary-fill-to data-[state=checked]:text-primary-foreground",
|
|
80
|
+
className
|
|
81
|
+
),
|
|
82
|
+
...props,
|
|
83
|
+
children: /* @__PURE__ */ jsx2(
|
|
84
|
+
CheckboxPrimitive.Indicator,
|
|
85
|
+
{
|
|
86
|
+
"data-slot": "checkbox-indicator",
|
|
87
|
+
className: "flex items-center justify-center text-current animate-checkbox-pop",
|
|
88
|
+
children: /* @__PURE__ */ jsx2(CheckIcon2, { className: "size-2.5 stroke-[3.5px]" })
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/ui/select.tsx
|
|
96
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
97
|
+
import { CheckIcon as CheckIcon3, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
98
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
99
|
+
function Select({
|
|
100
|
+
...props
|
|
101
|
+
}) {
|
|
102
|
+
return /* @__PURE__ */ jsx3(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
103
|
+
}
|
|
104
|
+
function SelectGroup({
|
|
105
|
+
...props
|
|
106
|
+
}) {
|
|
107
|
+
return /* @__PURE__ */ jsx3(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
|
|
108
|
+
}
|
|
109
|
+
function SelectValue({
|
|
110
|
+
...props
|
|
111
|
+
}) {
|
|
112
|
+
return /* @__PURE__ */ jsx3(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
113
|
+
}
|
|
114
|
+
function SelectTrigger({
|
|
115
|
+
className,
|
|
116
|
+
size = "default",
|
|
117
|
+
children,
|
|
118
|
+
...props
|
|
119
|
+
}) {
|
|
120
|
+
return /* @__PURE__ */ jsxs2(
|
|
121
|
+
SelectPrimitive.Trigger,
|
|
122
|
+
{
|
|
123
|
+
"data-slot": "select-trigger",
|
|
124
|
+
"data-size": size,
|
|
125
|
+
className: cn(
|
|
126
|
+
controlClass({ size }),
|
|
127
|
+
"flex w-fit items-center justify-between gap-2 whitespace-nowrap *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
128
|
+
className
|
|
129
|
+
),
|
|
130
|
+
...props,
|
|
131
|
+
children: [
|
|
132
|
+
children,
|
|
133
|
+
/* @__PURE__ */ jsx3(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx3(ChevronDownIcon, { className: "size-4 opacity-50" }) })
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
function SelectContent({
|
|
139
|
+
className,
|
|
140
|
+
children,
|
|
141
|
+
position = "popper",
|
|
142
|
+
...props
|
|
143
|
+
}) {
|
|
144
|
+
return /* @__PURE__ */ jsx3(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs2(
|
|
145
|
+
SelectPrimitive.Content,
|
|
146
|
+
{
|
|
147
|
+
"data-slot": "select-content",
|
|
148
|
+
className: cn(
|
|
149
|
+
overlayListPanelClass,
|
|
150
|
+
"relative max-h-[var(--radix-select-content-available-height)] min-w-[8rem] origin-[var(--radix-select-content-transform-origin)] overflow-x-hidden overflow-y-auto",
|
|
151
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
152
|
+
className
|
|
153
|
+
),
|
|
154
|
+
position,
|
|
155
|
+
...props,
|
|
156
|
+
children: [
|
|
157
|
+
/* @__PURE__ */ jsx3(SelectScrollUpButton, {}),
|
|
158
|
+
/* @__PURE__ */ jsx3(
|
|
159
|
+
SelectPrimitive.Viewport,
|
|
160
|
+
{
|
|
161
|
+
className: cn(
|
|
162
|
+
"p-1",
|
|
163
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
164
|
+
),
|
|
165
|
+
children
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
/* @__PURE__ */ jsx3(SelectScrollDownButton, {})
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
) });
|
|
172
|
+
}
|
|
173
|
+
function SelectLabel({
|
|
174
|
+
className,
|
|
175
|
+
...props
|
|
176
|
+
}) {
|
|
177
|
+
return /* @__PURE__ */ jsx3(
|
|
178
|
+
SelectPrimitive.Label,
|
|
179
|
+
{
|
|
180
|
+
"data-slot": "select-label",
|
|
181
|
+
className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", className),
|
|
182
|
+
...props
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
function SelectItem({
|
|
187
|
+
className,
|
|
188
|
+
children,
|
|
189
|
+
...props
|
|
190
|
+
}) {
|
|
191
|
+
return /* @__PURE__ */ jsxs2(
|
|
192
|
+
SelectPrimitive.Item,
|
|
193
|
+
{
|
|
194
|
+
"data-slot": "select-item",
|
|
195
|
+
className: cn(
|
|
196
|
+
overlayItemClass,
|
|
197
|
+
"w-full py-1 pr-8 pl-2 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
198
|
+
className
|
|
199
|
+
),
|
|
200
|
+
...props,
|
|
201
|
+
children: [
|
|
202
|
+
/* @__PURE__ */ jsx3("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx3(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx3(CheckIcon3, { className: "size-4" }) }) }),
|
|
203
|
+
/* @__PURE__ */ jsx3(SelectPrimitive.ItemText, { children })
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
function SelectSeparator({
|
|
209
|
+
className,
|
|
210
|
+
...props
|
|
211
|
+
}) {
|
|
212
|
+
return /* @__PURE__ */ jsx3(
|
|
213
|
+
SelectPrimitive.Separator,
|
|
214
|
+
{
|
|
215
|
+
"data-slot": "select-separator",
|
|
216
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
217
|
+
...props
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
function SelectScrollUpButton({
|
|
222
|
+
className,
|
|
223
|
+
...props
|
|
224
|
+
}) {
|
|
225
|
+
return /* @__PURE__ */ jsx3(
|
|
226
|
+
SelectPrimitive.ScrollUpButton,
|
|
227
|
+
{
|
|
228
|
+
"data-slot": "select-scroll-up-button",
|
|
229
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
230
|
+
...props,
|
|
231
|
+
children: /* @__PURE__ */ jsx3(ChevronUpIcon, { className: "size-4" })
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
function SelectScrollDownButton({
|
|
236
|
+
className,
|
|
237
|
+
...props
|
|
238
|
+
}) {
|
|
239
|
+
return /* @__PURE__ */ jsx3(
|
|
240
|
+
SelectPrimitive.ScrollDownButton,
|
|
241
|
+
{
|
|
242
|
+
"data-slot": "select-scroll-down-button",
|
|
243
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
244
|
+
...props,
|
|
245
|
+
children: /* @__PURE__ */ jsx3(ChevronDownIcon, { className: "size-4" })
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/ui/popover.tsx
|
|
251
|
+
import { Popover as PopoverPrimitive } from "radix-ui";
|
|
252
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
253
|
+
function Popover({
|
|
254
|
+
...props
|
|
255
|
+
}) {
|
|
256
|
+
return /* @__PURE__ */ jsx4(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
257
|
+
}
|
|
258
|
+
function PopoverTrigger({
|
|
259
|
+
...props
|
|
260
|
+
}) {
|
|
261
|
+
return /* @__PURE__ */ jsx4(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
262
|
+
}
|
|
263
|
+
function PopoverAnchor({
|
|
264
|
+
...props
|
|
265
|
+
}) {
|
|
266
|
+
return /* @__PURE__ */ jsx4(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
267
|
+
}
|
|
268
|
+
function PopoverContent({
|
|
269
|
+
className,
|
|
270
|
+
align = "center",
|
|
271
|
+
sideOffset = 4,
|
|
272
|
+
variant = "default",
|
|
273
|
+
...props
|
|
274
|
+
}) {
|
|
275
|
+
return /* @__PURE__ */ jsx4(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx4(
|
|
276
|
+
PopoverPrimitive.Content,
|
|
277
|
+
{
|
|
278
|
+
"data-slot": "popover-content",
|
|
279
|
+
"data-variant": variant,
|
|
280
|
+
align,
|
|
281
|
+
sideOffset,
|
|
282
|
+
className: cn(
|
|
283
|
+
variant === "list" ? cn(
|
|
284
|
+
overlayListPanelClass,
|
|
285
|
+
"min-w-[8rem] origin-[var(--radix-popover-content-transform-origin)]"
|
|
286
|
+
) : cn(
|
|
287
|
+
overlaySurfaceClass,
|
|
288
|
+
"w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-3 outline-hidden"
|
|
289
|
+
),
|
|
290
|
+
className
|
|
291
|
+
),
|
|
292
|
+
...props
|
|
293
|
+
}
|
|
294
|
+
) });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/ui/skeleton.tsx
|
|
298
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
299
|
+
function Skeleton({ className, ...props }) {
|
|
300
|
+
return /* @__PURE__ */ jsx5(
|
|
301
|
+
"div",
|
|
302
|
+
{
|
|
303
|
+
"data-slot": "skeleton",
|
|
304
|
+
className: cn("animate-pulse rounded-lg bg-muted", className),
|
|
305
|
+
...props
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/ui/untitled-button.tsx
|
|
311
|
+
import { cva } from "class-variance-authority";
|
|
312
|
+
import { Slot } from "radix-ui";
|
|
313
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
314
|
+
var SOLID_SKEUOMORPHIC_SHADOW = "shadow-skeuomorphic-solid";
|
|
315
|
+
var BORDERED_SKEUOMORPHIC_SHADOW = "shadow-skeuomorphic-bordered";
|
|
316
|
+
var untitledButtonVariants = cva(
|
|
317
|
+
cn(
|
|
318
|
+
"relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center whitespace-nowrap font-medium",
|
|
319
|
+
"transition-all duration-300 ease-in-out outline-none border",
|
|
320
|
+
"focus-visible:ring-4 focus-visible:ring-primary/20 focus-visible:ring-offset-0",
|
|
321
|
+
"disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50",
|
|
322
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-5",
|
|
323
|
+
// Overflow hidden can clip out-of-bounds shadow, so we use precise overflow management and rounded-[inherit] on pseudo overlays
|
|
324
|
+
"after:absolute after:inset-0 after:rounded-[inherit] after:pointer-events-none after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0"
|
|
325
|
+
),
|
|
326
|
+
{
|
|
327
|
+
variants: {
|
|
328
|
+
color: {
|
|
329
|
+
primary: cn(
|
|
330
|
+
// Exact Premium Untitled UI primary BLACK / dark charcoal: gradient + borders
|
|
331
|
+
"bg-gradient-to-b from-[#344054] to-[#0F1117] text-white border-[#1A1E29]",
|
|
332
|
+
"hover:border-[#181C26]",
|
|
333
|
+
"active:border-[#0A0D14]",
|
|
334
|
+
SOLID_SKEUOMORPHIC_SHADOW,
|
|
335
|
+
// Hover/active overlays for beautiful animation (the gradient is static, the overlay opacity is transitioned)
|
|
336
|
+
"after:bg-white/[0.08] hover:after:opacity-100 active:after:bg-black/[0.12]",
|
|
337
|
+
// Premium Dark Mode inversion: Primary becomes white, popping out elegantly
|
|
338
|
+
"dark:bg-gradient-to-b dark:from-white dark:to-[#F9FAFB] dark:text-[#10121C] dark:border-white",
|
|
339
|
+
"dark:hover:border-[#D0D5DD] dark:hover:text-[#10121C]",
|
|
340
|
+
"dark:shadow-skeuomorphic-bordered",
|
|
341
|
+
"dark:after:bg-black/[0.04] dark:active:after:bg-black/[0.08]"
|
|
342
|
+
),
|
|
343
|
+
secondary: cn(
|
|
344
|
+
// Exact Untitled UI secondary: premium white/gray gradient + borders
|
|
345
|
+
"bg-gradient-to-b from-white to-[#F9FAFB] text-[#344054] border-[#D0D5DD]",
|
|
346
|
+
"hover:text-[#1D2939] hover:border-[#D0D5DD]",
|
|
347
|
+
BORDERED_SKEUOMORPHIC_SHADOW,
|
|
348
|
+
// Hover/active overlays for white/gray gradient
|
|
349
|
+
"after:bg-black/[0.03] hover:after:opacity-100 active:after:bg-black/[0.08]",
|
|
350
|
+
// Premium Dark Mode inversion: Secondary becomes dark charcoal/gray, merging into the background
|
|
351
|
+
"dark:bg-gradient-to-b dark:from-[#1F242F] dark:to-[#10121C] dark:text-[#D1D5DB] dark:border-[#344054]",
|
|
352
|
+
"dark:hover:border-[#475467] dark:hover:text-white",
|
|
353
|
+
"dark:shadow-skeuomorphic-solid",
|
|
354
|
+
"dark:after:bg-white/[0.06] dark:active:after:bg-black/[0.15]"
|
|
355
|
+
),
|
|
356
|
+
tertiary: cn(
|
|
357
|
+
"bg-transparent text-[#475467] border-transparent",
|
|
358
|
+
"hover:bg-[#F9FAFB] hover:text-[#344054]",
|
|
359
|
+
"active:bg-[#F2F4F7] active:text-[#1D2939]",
|
|
360
|
+
"after:hidden",
|
|
361
|
+
// No overlay needed for transparent surfaces
|
|
362
|
+
// Dark Mode
|
|
363
|
+
"dark:text-[#9CA3AF] dark:hover:bg-[#1F242F] dark:hover:text-white dark:active:bg-[#11131A]"
|
|
364
|
+
),
|
|
365
|
+
link: cn(
|
|
366
|
+
"h-auto! bg-transparent p-0! border-transparent text-[#1F242F] hover:text-[#10121C]",
|
|
367
|
+
"hover:underline",
|
|
368
|
+
"after:hidden",
|
|
369
|
+
// Dark Mode
|
|
370
|
+
"dark:text-[#9CA3AF] dark:hover:text-white"
|
|
371
|
+
),
|
|
372
|
+
"primary-destructive": cn(
|
|
373
|
+
// Exact Untitled UI primary destructive: premium red gradient + borders (vibrant and subtle, not too dark)
|
|
374
|
+
"bg-gradient-to-b from-[#D92D20] to-[#B42318] text-white border-[#B42318]",
|
|
375
|
+
"hover:border-[#9E1B12]",
|
|
376
|
+
"active:border-[#84140D]",
|
|
377
|
+
SOLID_SKEUOMORPHIC_SHADOW,
|
|
378
|
+
// Destructive red hover/active overlays
|
|
379
|
+
"after:bg-white/[0.12] hover:after:opacity-100 active:after:bg-black/[0.15]"
|
|
380
|
+
),
|
|
381
|
+
"secondary-destructive": cn(
|
|
382
|
+
// Exact Untitled UI secondary destructive: soft red bordered
|
|
383
|
+
"bg-gradient-to-b from-white to-[#F9FAFB] text-[#B42318] border-[#FDA29B]",
|
|
384
|
+
"hover:text-[#9E1B12] hover:border-[#FDA29B]",
|
|
385
|
+
BORDERED_SKEUOMORPHIC_SHADOW,
|
|
386
|
+
// Hover overlay
|
|
387
|
+
"after:bg-red-500/[0.04] hover:after:opacity-100 active:after:bg-red-950/[0.08]",
|
|
388
|
+
// Dark Mode Secondary Destructive: Charcoal fill with red borders and label
|
|
389
|
+
"dark:bg-gradient-to-b dark:from-[#1F242F] dark:to-[#10121C] dark:text-[#F87171] dark:border-[#9E1B12]/50",
|
|
390
|
+
"dark:hover:border-[#F87171]/40",
|
|
391
|
+
"dark:shadow-skeuomorphic-solid",
|
|
392
|
+
"dark:after:bg-white/[0.06] dark:active:after:bg-black/[0.15]"
|
|
393
|
+
)
|
|
394
|
+
},
|
|
395
|
+
size: {
|
|
396
|
+
sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
|
|
397
|
+
md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
|
|
398
|
+
lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
|
|
399
|
+
xl: "h-11 gap-2 rounded-lg px-4 text-base"
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
defaultVariants: {
|
|
403
|
+
color: "primary",
|
|
404
|
+
size: "md"
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
function UntitledButton({
|
|
409
|
+
className,
|
|
410
|
+
color,
|
|
411
|
+
size,
|
|
412
|
+
iconLeading,
|
|
413
|
+
iconTrailing,
|
|
414
|
+
isLoading = false,
|
|
415
|
+
asChild = false,
|
|
416
|
+
disabled,
|
|
417
|
+
type = "button",
|
|
418
|
+
children,
|
|
419
|
+
...props
|
|
420
|
+
}) {
|
|
421
|
+
const isDisabled = disabled || isLoading;
|
|
422
|
+
const classes = cn(untitledButtonVariants({ color, size }), className);
|
|
423
|
+
if (asChild) {
|
|
424
|
+
return /* @__PURE__ */ jsx6(
|
|
425
|
+
Slot.Root,
|
|
426
|
+
{
|
|
427
|
+
className: classes,
|
|
428
|
+
"aria-disabled": isDisabled ? true : void 0,
|
|
429
|
+
"data-slot": "untitled-button",
|
|
430
|
+
...props,
|
|
431
|
+
children
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
return /* @__PURE__ */ jsxs3(
|
|
436
|
+
"button",
|
|
437
|
+
{
|
|
438
|
+
type,
|
|
439
|
+
disabled: isDisabled,
|
|
440
|
+
"data-slot": "untitled-button",
|
|
441
|
+
className: classes,
|
|
442
|
+
...props,
|
|
443
|
+
children: [
|
|
444
|
+
isLoading ? /* @__PURE__ */ jsx6(
|
|
445
|
+
"span",
|
|
446
|
+
{
|
|
447
|
+
"aria-hidden": true,
|
|
448
|
+
className: "size-4 animate-spin rounded-full border-2 border-current border-t-transparent"
|
|
449
|
+
}
|
|
450
|
+
) : iconLeading,
|
|
451
|
+
children,
|
|
452
|
+
!isLoading ? iconTrailing : null
|
|
453
|
+
]
|
|
454
|
+
}
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/ui/banner.tsx
|
|
459
|
+
import { XIcon } from "lucide-react";
|
|
460
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
461
|
+
var bannerSoftClass = {
|
|
462
|
+
default: "border-border/50 bg-muted/30 text-foreground/90 dark:bg-muted/15",
|
|
463
|
+
primary: "border-primary/15 bg-primary/5 text-primary-800 dark:text-primary-200 [&_[data-banner-icon]]:text-primary",
|
|
464
|
+
success: "border-emerald-500/15 bg-emerald-500/5 text-emerald-800 dark:text-emerald-300 [&_[data-banner-icon]]:text-emerald-600 dark:[&_[data-banner-icon]]:text-emerald-400",
|
|
465
|
+
warn: "border-amber-500/15 bg-amber-500/5 text-amber-800 dark:text-amber-300 [&_[data-banner-icon]]:text-amber-600 dark:[&_[data-banner-icon]]:text-amber-400",
|
|
466
|
+
danger: "border-destructive/15 bg-destructive/5 text-destructive dark:text-red-300 [&_[data-banner-icon]]:text-destructive"
|
|
467
|
+
};
|
|
468
|
+
var bannerSolidClass = {
|
|
469
|
+
default: "border-transparent bg-foreground text-background shadow-sm",
|
|
470
|
+
primary: "border-transparent bg-gradient-to-r from-primary to-primary/95 text-primary-foreground shadow-sm shadow-primary/5",
|
|
471
|
+
success: "border-transparent bg-gradient-to-r from-emerald-600 to-emerald-500 text-white shadow-sm shadow-emerald-500/5 dark:from-emerald-500 dark:to-emerald-400",
|
|
472
|
+
warn: "border-transparent bg-gradient-to-r from-amber-500 to-amber-400 text-white shadow-sm shadow-amber-500/5 dark:from-amber-500 dark:to-amber-400",
|
|
473
|
+
danger: "border-transparent bg-gradient-to-r from-destructive to-destructive/95 text-destructive-foreground shadow-sm shadow-destructive/5"
|
|
474
|
+
};
|
|
475
|
+
var bannerOutlineClass = {
|
|
476
|
+
default: "border-border/80 bg-background/50 text-foreground/90 backdrop-blur-sm shadow-sm",
|
|
477
|
+
primary: "border-primary/30 bg-primary/[0.02] text-foreground [&_[data-banner-icon]]:text-primary shadow-sm",
|
|
478
|
+
success: "border-emerald-500/30 bg-emerald-500/[0.02] text-foreground [&_[data-banner-icon]]:text-emerald-600 dark:[&_[data-banner-icon]]:text-emerald-400 shadow-sm",
|
|
479
|
+
warn: "border-amber-500/30 bg-amber-500/[0.02] text-foreground [&_[data-banner-icon]]:text-amber-600 dark:[&_[data-banner-icon]]:text-amber-400 shadow-sm",
|
|
480
|
+
danger: "border-destructive/30 bg-destructive/[0.02] text-foreground [&_[data-banner-icon]]:text-destructive shadow-sm"
|
|
481
|
+
};
|
|
482
|
+
var bannerVariantClass = {
|
|
483
|
+
soft: bannerSoftClass,
|
|
484
|
+
solid: bannerSolidClass,
|
|
485
|
+
outline: bannerOutlineClass
|
|
486
|
+
};
|
|
487
|
+
var bannerSizeClass = {
|
|
488
|
+
sm: "gap-2.5 px-3.5 py-2 text-xs",
|
|
489
|
+
default: "gap-3 px-4 py-3 text-sm"
|
|
490
|
+
};
|
|
491
|
+
function Banner({
|
|
492
|
+
tone = "default",
|
|
493
|
+
variant = "soft",
|
|
494
|
+
size = "default",
|
|
495
|
+
icon,
|
|
496
|
+
title,
|
|
497
|
+
actions,
|
|
498
|
+
onDismiss,
|
|
499
|
+
className,
|
|
500
|
+
children,
|
|
501
|
+
...props
|
|
502
|
+
}) {
|
|
503
|
+
const isSolid = variant === "solid";
|
|
504
|
+
const isSingleLine = !title;
|
|
505
|
+
return /* @__PURE__ */ jsxs4(
|
|
506
|
+
"div",
|
|
507
|
+
{
|
|
508
|
+
"data-slot": "banner",
|
|
509
|
+
"data-variant": variant,
|
|
510
|
+
role: "status",
|
|
511
|
+
className: cn(
|
|
512
|
+
"flex w-full rounded-xl border transition-all duration-200",
|
|
513
|
+
isSingleLine ? "items-center" : "items-start",
|
|
514
|
+
bannerSizeClass[size],
|
|
515
|
+
bannerVariantClass[variant][tone],
|
|
516
|
+
className
|
|
517
|
+
),
|
|
518
|
+
...props,
|
|
519
|
+
children: [
|
|
520
|
+
icon ? /* @__PURE__ */ jsx7(
|
|
521
|
+
"span",
|
|
522
|
+
{
|
|
523
|
+
"data-banner-icon": true,
|
|
524
|
+
className: cn(
|
|
525
|
+
"shrink-0 [&_svg]:size-4",
|
|
526
|
+
isSingleLine ? "self-center" : "mt-0.5"
|
|
527
|
+
),
|
|
528
|
+
children: icon
|
|
529
|
+
}
|
|
530
|
+
) : null,
|
|
531
|
+
/* @__PURE__ */ jsxs4("div", { className: "min-w-0 flex-1", children: [
|
|
532
|
+
title ? /* @__PURE__ */ jsx7("p", { className: "font-medium tracking-tight", children: title }) : null,
|
|
533
|
+
children ? /* @__PURE__ */ jsx7(
|
|
534
|
+
"div",
|
|
535
|
+
{
|
|
536
|
+
className: cn(
|
|
537
|
+
isSolid ? "opacity-90" : "text-muted-foreground",
|
|
538
|
+
title && "mt-0.5"
|
|
539
|
+
),
|
|
540
|
+
children
|
|
541
|
+
}
|
|
542
|
+
) : null
|
|
543
|
+
] }),
|
|
544
|
+
actions ? /* @__PURE__ */ jsx7("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
|
|
545
|
+
onDismiss ? /* @__PURE__ */ jsx7(
|
|
546
|
+
"button",
|
|
547
|
+
{
|
|
548
|
+
type: "button",
|
|
549
|
+
"aria-label": "Dismiss",
|
|
550
|
+
onClick: onDismiss,
|
|
551
|
+
className: cn(
|
|
552
|
+
"-mr-1 inline-flex size-7 shrink-0 items-center justify-center rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
|
|
553
|
+
isSingleLine ? "self-center" : "-mt-0.5",
|
|
554
|
+
isSolid ? "opacity-80 hover:bg-background/15 hover:opacity-100" : "text-muted-foreground hover:bg-foreground/10 hover:text-foreground"
|
|
555
|
+
),
|
|
556
|
+
children: /* @__PURE__ */ jsx7(XIcon, { className: "size-4", "aria-hidden": true })
|
|
557
|
+
}
|
|
558
|
+
) : null
|
|
559
|
+
]
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/ui/timeline.tsx
|
|
565
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
566
|
+
var timelineRowGap = {
|
|
567
|
+
sm: "pb-4",
|
|
568
|
+
default: "pb-6"
|
|
569
|
+
};
|
|
570
|
+
var toneStyles = {
|
|
571
|
+
default: {
|
|
572
|
+
border: "border-neutral-300 dark:border-neutral-700",
|
|
573
|
+
dot: "bg-neutral-400 dark:bg-neutral-500",
|
|
574
|
+
icon: "text-neutral-500 dark:text-neutral-400"
|
|
575
|
+
},
|
|
576
|
+
primary: {
|
|
577
|
+
border: "border-primary/50 dark:border-primary/40",
|
|
578
|
+
dot: "bg-primary",
|
|
579
|
+
icon: "text-primary"
|
|
580
|
+
},
|
|
581
|
+
success: {
|
|
582
|
+
border: "border-emerald-500/40 dark:border-emerald-500/30",
|
|
583
|
+
dot: "bg-emerald-500",
|
|
584
|
+
icon: "text-emerald-500"
|
|
585
|
+
},
|
|
586
|
+
warn: {
|
|
587
|
+
border: "border-amber-500/40 dark:border-amber-500/30",
|
|
588
|
+
dot: "bg-amber-500",
|
|
589
|
+
icon: "text-amber-500"
|
|
590
|
+
},
|
|
591
|
+
danger: {
|
|
592
|
+
border: "border-destructive/40 dark:border-destructive/30",
|
|
593
|
+
dot: "bg-destructive",
|
|
594
|
+
icon: "text-destructive"
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
function Timeline({ items, size = "default", className, ...props }) {
|
|
598
|
+
return /* @__PURE__ */ jsx8("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
|
|
599
|
+
const last = index === items.length - 1;
|
|
600
|
+
const tone = item.tone ?? "default";
|
|
601
|
+
const styles = toneStyles[tone];
|
|
602
|
+
return /* @__PURE__ */ jsxs5(
|
|
603
|
+
"li",
|
|
604
|
+
{
|
|
605
|
+
className: cn("relative flex gap-3.5 last:pb-0", timelineRowGap[size]),
|
|
606
|
+
children: [
|
|
607
|
+
!last ? /* @__PURE__ */ jsx8(
|
|
608
|
+
"span",
|
|
609
|
+
{
|
|
610
|
+
"aria-hidden": true,
|
|
611
|
+
className: cn(
|
|
612
|
+
"absolute left-2.5 bottom-0 w-[1.5px] -translate-x-1/2 bg-neutral-200 dark:bg-neutral-800",
|
|
613
|
+
item.icon ? "top-6" : "top-3"
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
) : null,
|
|
617
|
+
/* @__PURE__ */ jsx8("div", { className: "relative flex w-5 shrink-0 justify-center pt-0.5", children: /* @__PURE__ */ jsx8(
|
|
618
|
+
"span",
|
|
619
|
+
{
|
|
620
|
+
className: cn(
|
|
621
|
+
"relative z-10 flex shrink-0 items-center justify-center rounded-full border-[1.5px] bg-background transition-colors",
|
|
622
|
+
styles.border,
|
|
623
|
+
item.icon ? "size-6" : "size-4"
|
|
624
|
+
),
|
|
625
|
+
children: item.icon ? /* @__PURE__ */ jsx8("div", { className: cn("text-xs", styles.icon), children: item.icon }) : /* @__PURE__ */ jsx8(
|
|
626
|
+
"span",
|
|
627
|
+
{
|
|
628
|
+
className: cn(
|
|
629
|
+
"size-1.5 rounded-full",
|
|
630
|
+
styles.dot
|
|
631
|
+
)
|
|
632
|
+
}
|
|
633
|
+
)
|
|
634
|
+
}
|
|
635
|
+
) }),
|
|
636
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 flex-1 pb-0.5", children: [
|
|
637
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between gap-2", children: [
|
|
638
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-normal text-foreground", children: item.title }),
|
|
639
|
+
item.meta ? /* @__PURE__ */ jsx8("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
|
|
640
|
+
] }),
|
|
641
|
+
item.description ? /* @__PURE__ */ jsx8("p", { className: "mt-0.5 text-[13px] leading-relaxed text-muted-foreground", children: item.description }) : null
|
|
642
|
+
] })
|
|
643
|
+
]
|
|
644
|
+
},
|
|
645
|
+
item.id
|
|
646
|
+
);
|
|
647
|
+
}) });
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/ui/kanban.tsx
|
|
651
|
+
import * as React2 from "react";
|
|
652
|
+
import {
|
|
653
|
+
DndContext,
|
|
654
|
+
DragOverlay,
|
|
655
|
+
KeyboardSensor,
|
|
656
|
+
PointerSensor,
|
|
657
|
+
closestCorners,
|
|
658
|
+
useDroppable,
|
|
659
|
+
useSensor,
|
|
660
|
+
useSensors
|
|
661
|
+
} from "@dnd-kit/core";
|
|
662
|
+
import {
|
|
663
|
+
SortableContext,
|
|
664
|
+
arrayMove,
|
|
665
|
+
sortableKeyboardCoordinates,
|
|
666
|
+
useSortable,
|
|
667
|
+
verticalListSortingStrategy
|
|
668
|
+
} from "@dnd-kit/sortable";
|
|
669
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
670
|
+
import { GripVerticalIcon } from "lucide-react";
|
|
671
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
672
|
+
var columnTitleToneClass = {
|
|
673
|
+
default: "text-foreground",
|
|
674
|
+
primary: "text-blue-600 dark:text-blue-400",
|
|
675
|
+
success: "text-emerald-600 dark:text-emerald-400",
|
|
676
|
+
warn: "text-amber-600 dark:text-amber-400",
|
|
677
|
+
danger: "text-rose-600 dark:text-rose-400"
|
|
678
|
+
};
|
|
679
|
+
var cardVariantClass = {
|
|
680
|
+
default: "border border-border/70 bg-card shadow-sm hover:border-border hover:shadow-md",
|
|
681
|
+
outline: "border border-border bg-card hover:border-foreground/25",
|
|
682
|
+
muted: "border border-transparent bg-muted/60 hover:bg-muted"
|
|
683
|
+
};
|
|
684
|
+
var cardToneClass = {
|
|
685
|
+
default: "bg-card shadow-sm hover:shadow-md",
|
|
686
|
+
primary: "bg-card shadow-sm hover:shadow-md",
|
|
687
|
+
success: "bg-card shadow-sm hover:shadow-md",
|
|
688
|
+
warn: "bg-card shadow-sm hover:shadow-md",
|
|
689
|
+
danger: "bg-card shadow-sm hover:shadow-md"
|
|
690
|
+
};
|
|
691
|
+
function cardSurfaceClass(variant, tone) {
|
|
692
|
+
return variant === "tonal" ? cardToneClass[tone] : cardVariantClass[variant];
|
|
693
|
+
}
|
|
694
|
+
var densityColumnClass = {
|
|
695
|
+
default: "w-72 gap-2.5",
|
|
696
|
+
compact: "w-64 gap-2"
|
|
697
|
+
};
|
|
698
|
+
var densityListClass = {
|
|
699
|
+
default: "gap-2.5",
|
|
700
|
+
compact: "gap-2"
|
|
701
|
+
};
|
|
702
|
+
var densityCardClass = {
|
|
703
|
+
default: "rounded-lg p-2",
|
|
704
|
+
compact: "rounded-lg p-1.5"
|
|
705
|
+
};
|
|
706
|
+
function defaultGetCardId(card) {
|
|
707
|
+
return card.id;
|
|
708
|
+
}
|
|
709
|
+
function SortableCard({
|
|
710
|
+
card,
|
|
711
|
+
cardId,
|
|
712
|
+
column,
|
|
713
|
+
density,
|
|
714
|
+
variant,
|
|
715
|
+
disabled,
|
|
716
|
+
dragHandle,
|
|
717
|
+
className,
|
|
718
|
+
renderCard
|
|
719
|
+
}) {
|
|
720
|
+
const {
|
|
721
|
+
attributes,
|
|
722
|
+
listeners,
|
|
723
|
+
setNodeRef,
|
|
724
|
+
transform,
|
|
725
|
+
transition,
|
|
726
|
+
isDragging
|
|
727
|
+
} = useSortable({ id: cardId, disabled });
|
|
728
|
+
const style = {
|
|
729
|
+
transform: CSS.Translate.toString(transform),
|
|
730
|
+
transition
|
|
731
|
+
};
|
|
732
|
+
const dragHandleProps = disabled ? void 0 : { ...attributes, ...listeners };
|
|
733
|
+
return /* @__PURE__ */ jsxs6(
|
|
734
|
+
"div",
|
|
735
|
+
{
|
|
736
|
+
ref: setNodeRef,
|
|
737
|
+
style,
|
|
738
|
+
"data-slot": "kanban-card",
|
|
739
|
+
"data-dragging": isDragging ? "" : void 0,
|
|
740
|
+
className: cn(
|
|
741
|
+
"group/kanban-card relative text-sm text-foreground transition",
|
|
742
|
+
densityCardClass[density],
|
|
743
|
+
cardSurfaceClass(variant, column.tone ?? "default"),
|
|
744
|
+
isDragging && "opacity-40",
|
|
745
|
+
className
|
|
746
|
+
),
|
|
747
|
+
children: [
|
|
748
|
+
!disabled && dragHandle === "auto" ? /* @__PURE__ */ jsx9(
|
|
749
|
+
"button",
|
|
750
|
+
{
|
|
751
|
+
type: "button",
|
|
752
|
+
"aria-label": "Drag card",
|
|
753
|
+
className: "absolute right-1.5 top-1.5 z-10 grid size-6 cursor-grab touch-none place-items-center rounded-md text-muted-foreground/40 opacity-0 transition hover:bg-foreground/5 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 group-hover/kanban-card:opacity-100 active:cursor-grabbing",
|
|
754
|
+
...attributes,
|
|
755
|
+
...listeners,
|
|
756
|
+
children: /* @__PURE__ */ jsx9(GripVerticalIcon, { className: "size-4", "aria-hidden": true })
|
|
757
|
+
}
|
|
758
|
+
) : null,
|
|
759
|
+
renderCard(card, { column, isDragging, isOverlay: false, dragHandleProps })
|
|
760
|
+
]
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
function KanbanColumnView({
|
|
765
|
+
column,
|
|
766
|
+
cardIds,
|
|
767
|
+
density,
|
|
768
|
+
cardVariant,
|
|
769
|
+
disabled,
|
|
770
|
+
dragHandle,
|
|
771
|
+
emptyColumnLabel,
|
|
772
|
+
className,
|
|
773
|
+
cardClassName,
|
|
774
|
+
getCardId,
|
|
775
|
+
renderColumnHeader,
|
|
776
|
+
renderCard
|
|
777
|
+
}) {
|
|
778
|
+
const tone = column.tone ?? "default";
|
|
779
|
+
const { setNodeRef, isOver } = useDroppable({ id: column.id, disabled });
|
|
780
|
+
return /* @__PURE__ */ jsxs6(
|
|
781
|
+
"div",
|
|
782
|
+
{
|
|
783
|
+
"data-slot": "kanban-column",
|
|
784
|
+
className: cn(
|
|
785
|
+
"flex shrink-0 flex-col",
|
|
786
|
+
densityColumnClass[density],
|
|
787
|
+
className
|
|
788
|
+
),
|
|
789
|
+
children: [
|
|
790
|
+
renderColumnHeader ? renderColumnHeader(column) : /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-0.5 px-1 pb-0.5", children: [
|
|
791
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
|
|
792
|
+
/* @__PURE__ */ jsx9(
|
|
793
|
+
"h3",
|
|
794
|
+
{
|
|
795
|
+
className: cn(
|
|
796
|
+
"min-w-0 flex-1 truncate text-xs font-medium",
|
|
797
|
+
columnTitleToneClass[tone]
|
|
798
|
+
),
|
|
799
|
+
children: column.title
|
|
800
|
+
}
|
|
801
|
+
),
|
|
802
|
+
/* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs font-normal tabular-nums text-muted-foreground/60", children: column.cards.length }),
|
|
803
|
+
column.action ? /* @__PURE__ */ jsx9("div", { className: "shrink-0", children: column.action }) : null
|
|
804
|
+
] }),
|
|
805
|
+
column.description ? /* @__PURE__ */ jsx9("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
|
|
806
|
+
] }),
|
|
807
|
+
/* @__PURE__ */ jsx9(SortableContext, { items: cardIds, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx9(
|
|
808
|
+
"div",
|
|
809
|
+
{
|
|
810
|
+
ref: setNodeRef,
|
|
811
|
+
"data-slot": "kanban-column-body",
|
|
812
|
+
className: cn(
|
|
813
|
+
"flex min-h-16 flex-1 flex-col rounded-xl transition-colors",
|
|
814
|
+
densityListClass[density],
|
|
815
|
+
isOver && "bg-muted/50"
|
|
816
|
+
),
|
|
817
|
+
children: column.cards.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "flex flex-1 items-center justify-center rounded-xl border border-dashed border-border/60 px-2 py-8 text-center text-xs text-muted-foreground/70", children: emptyColumnLabel ?? "Drop here" }) : column.cards.map((card) => {
|
|
818
|
+
const id = getCardId(card);
|
|
819
|
+
return /* @__PURE__ */ jsx9(
|
|
820
|
+
SortableCard,
|
|
821
|
+
{
|
|
822
|
+
card,
|
|
823
|
+
cardId: id,
|
|
824
|
+
column,
|
|
825
|
+
density,
|
|
826
|
+
variant: cardVariant,
|
|
827
|
+
disabled,
|
|
828
|
+
dragHandle,
|
|
829
|
+
className: cardClassName,
|
|
830
|
+
renderCard
|
|
831
|
+
},
|
|
832
|
+
id
|
|
833
|
+
);
|
|
834
|
+
})
|
|
835
|
+
}
|
|
836
|
+
) }),
|
|
837
|
+
column.footer ? /* @__PURE__ */ jsx9("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
|
|
838
|
+
]
|
|
839
|
+
}
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
function cloneColumns(columns) {
|
|
843
|
+
return columns.map((col) => ({ ...col, cards: [...col.cards] }));
|
|
844
|
+
}
|
|
845
|
+
function locateCard(columns, getCardId, cardId) {
|
|
846
|
+
for (const col of columns) {
|
|
847
|
+
const index = col.cards.findIndex((c) => getCardId(c) === cardId);
|
|
848
|
+
if (index !== -1) return { columnId: col.id, index };
|
|
849
|
+
}
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
function Kanban({
|
|
853
|
+
columns: columnsProp,
|
|
854
|
+
defaultColumns,
|
|
855
|
+
onColumnsChange,
|
|
856
|
+
onMove,
|
|
857
|
+
renderCard,
|
|
858
|
+
renderColumnHeader,
|
|
859
|
+
getCardId = defaultGetCardId,
|
|
860
|
+
emptyColumnLabel,
|
|
861
|
+
density = "default",
|
|
862
|
+
cardVariant = "default",
|
|
863
|
+
dragHandle = "auto",
|
|
864
|
+
disabled = false,
|
|
865
|
+
className,
|
|
866
|
+
columnClassName,
|
|
867
|
+
cardClassName,
|
|
868
|
+
...rest
|
|
869
|
+
}) {
|
|
870
|
+
const ariaLabel = rest["aria-label"] ?? "Kanban board";
|
|
871
|
+
const isControlled = columnsProp !== void 0;
|
|
872
|
+
const [internal, setInternal] = React2.useState(
|
|
873
|
+
() => cloneColumns(defaultColumns ?? columnsProp ?? [])
|
|
874
|
+
);
|
|
875
|
+
const [activeId, setActiveId] = React2.useState(null);
|
|
876
|
+
const dragOriginRef = React2.useRef(null);
|
|
877
|
+
React2.useEffect(() => {
|
|
878
|
+
if (isControlled && activeId === null) {
|
|
879
|
+
setInternal(cloneColumns(columnsProp));
|
|
880
|
+
}
|
|
881
|
+
}, [columnsProp, isControlled, activeId]);
|
|
882
|
+
const columns = internal;
|
|
883
|
+
const columnIds = React2.useMemo(
|
|
884
|
+
() => new Set(columns.map((c) => c.id)),
|
|
885
|
+
[columns]
|
|
886
|
+
);
|
|
887
|
+
const sensors = useSensors(
|
|
888
|
+
useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
|
|
889
|
+
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
890
|
+
);
|
|
891
|
+
const activeCard = React2.useMemo(() => {
|
|
892
|
+
if (!activeId) return null;
|
|
893
|
+
for (const col of columns) {
|
|
894
|
+
const card = col.cards.find((c) => getCardId(c) === activeId);
|
|
895
|
+
if (card) return { card, column: col };
|
|
896
|
+
}
|
|
897
|
+
return null;
|
|
898
|
+
}, [activeId, columns, getCardId]);
|
|
899
|
+
const resolveTargetColumnId = (overId) => {
|
|
900
|
+
if (columnIds.has(overId)) return overId;
|
|
901
|
+
const loc = locateCard(columns, getCardId, overId);
|
|
902
|
+
return loc?.columnId;
|
|
903
|
+
};
|
|
904
|
+
const handleDragStart = (event) => {
|
|
905
|
+
const id = String(event.active.id);
|
|
906
|
+
setActiveId(id);
|
|
907
|
+
dragOriginRef.current = locateCard(columns, getCardId, id);
|
|
908
|
+
};
|
|
909
|
+
const handleDragOver = (event) => {
|
|
910
|
+
const { active, over } = event;
|
|
911
|
+
if (!over) return;
|
|
912
|
+
const activeCardId = String(active.id);
|
|
913
|
+
const overId = String(over.id);
|
|
914
|
+
const from = locateCard(columns, getCardId, activeCardId);
|
|
915
|
+
const toColumnId = resolveTargetColumnId(overId);
|
|
916
|
+
if (!from || !toColumnId || from.columnId === toColumnId) return;
|
|
917
|
+
setInternal((prev) => {
|
|
918
|
+
const next = cloneColumns(prev);
|
|
919
|
+
const fromCol = next.find((c) => c.id === from.columnId);
|
|
920
|
+
const toCol = next.find((c) => c.id === toColumnId);
|
|
921
|
+
if (!fromCol || !toCol) return prev;
|
|
922
|
+
const movingIndex = fromCol.cards.findIndex(
|
|
923
|
+
(c) => getCardId(c) === activeCardId
|
|
924
|
+
);
|
|
925
|
+
if (movingIndex === -1) return prev;
|
|
926
|
+
const [moving] = fromCol.cards.splice(movingIndex, 1);
|
|
927
|
+
const overIsCard = !columnIds.has(overId);
|
|
928
|
+
const overIndex = overIsCard ? toCol.cards.findIndex((c) => getCardId(c) === overId) : toCol.cards.length;
|
|
929
|
+
const insertAt = overIndex === -1 ? toCol.cards.length : overIndex;
|
|
930
|
+
toCol.cards.splice(insertAt, 0, moving);
|
|
931
|
+
return next;
|
|
932
|
+
});
|
|
933
|
+
};
|
|
934
|
+
const finishDrag = (event) => {
|
|
935
|
+
const { active, over } = event;
|
|
936
|
+
const origin = dragOriginRef.current;
|
|
937
|
+
const movedCard = activeCard?.card;
|
|
938
|
+
dragOriginRef.current = null;
|
|
939
|
+
setActiveId(null);
|
|
940
|
+
if (!over || !origin) return;
|
|
941
|
+
const activeCardId = String(active.id);
|
|
942
|
+
const overId = String(over.id);
|
|
943
|
+
let next = columns;
|
|
944
|
+
const current = locateCard(columns, getCardId, activeCardId);
|
|
945
|
+
const toColumnId = resolveTargetColumnId(overId) ?? current?.columnId;
|
|
946
|
+
if (current && toColumnId && current.columnId === toColumnId) {
|
|
947
|
+
const col = columns.find((c) => c.id === toColumnId);
|
|
948
|
+
if (col) {
|
|
949
|
+
const oldIndex = col.cards.findIndex((c) => getCardId(c) === activeCardId);
|
|
950
|
+
const overIsCard = !columnIds.has(overId);
|
|
951
|
+
const overIndex = overIsCard ? col.cards.findIndex((c) => getCardId(c) === overId) : col.cards.length - 1;
|
|
952
|
+
const newIndex = overIndex === -1 ? col.cards.length - 1 : overIndex;
|
|
953
|
+
if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) {
|
|
954
|
+
next = columns.map(
|
|
955
|
+
(c) => c.id === toColumnId ? { ...c, cards: arrayMove(c.cards, oldIndex, newIndex) } : c
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
if (next !== columns) setInternal(next);
|
|
961
|
+
const finalLoc = locateCard(next, getCardId, activeCardId);
|
|
962
|
+
const moved = finalLoc && (finalLoc.columnId !== origin.columnId || finalLoc.index !== origin.index);
|
|
963
|
+
if (moved) {
|
|
964
|
+
if (movedCard) {
|
|
965
|
+
onMove?.({
|
|
966
|
+
card: movedCard,
|
|
967
|
+
cardId: activeCardId,
|
|
968
|
+
from: origin,
|
|
969
|
+
to: finalLoc,
|
|
970
|
+
columns: next
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
onColumnsChange?.(next);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
return /* @__PURE__ */ jsxs6(
|
|
977
|
+
DndContext,
|
|
978
|
+
{
|
|
979
|
+
sensors,
|
|
980
|
+
collisionDetection: closestCorners,
|
|
981
|
+
onDragStart: handleDragStart,
|
|
982
|
+
onDragOver: handleDragOver,
|
|
983
|
+
onDragEnd: finishDrag,
|
|
984
|
+
onDragCancel: () => {
|
|
985
|
+
dragOriginRef.current = null;
|
|
986
|
+
setActiveId(null);
|
|
987
|
+
if (isControlled) setInternal(cloneColumns(columnsProp));
|
|
988
|
+
},
|
|
989
|
+
children: [
|
|
990
|
+
/* @__PURE__ */ jsx9(
|
|
991
|
+
"div",
|
|
992
|
+
{
|
|
993
|
+
"data-slot": "kanban",
|
|
994
|
+
role: "list",
|
|
995
|
+
"aria-label": ariaLabel,
|
|
996
|
+
className: cn(
|
|
997
|
+
"flex w-full items-start overflow-x-auto p-1.5 pb-6 -m-1.5",
|
|
998
|
+
density === "compact" ? "gap-3" : "gap-4",
|
|
999
|
+
className
|
|
1000
|
+
),
|
|
1001
|
+
children: columns.map((column) => /* @__PURE__ */ jsx9(
|
|
1002
|
+
KanbanColumnView,
|
|
1003
|
+
{
|
|
1004
|
+
column,
|
|
1005
|
+
cardIds: column.cards.map(getCardId),
|
|
1006
|
+
density,
|
|
1007
|
+
cardVariant,
|
|
1008
|
+
disabled,
|
|
1009
|
+
dragHandle,
|
|
1010
|
+
emptyColumnLabel,
|
|
1011
|
+
className: columnClassName,
|
|
1012
|
+
cardClassName,
|
|
1013
|
+
getCardId,
|
|
1014
|
+
renderColumnHeader,
|
|
1015
|
+
renderCard
|
|
1016
|
+
},
|
|
1017
|
+
column.id
|
|
1018
|
+
))
|
|
1019
|
+
}
|
|
1020
|
+
),
|
|
1021
|
+
/* @__PURE__ */ jsx9(DragOverlay, { children: activeCard ? /* @__PURE__ */ jsx9(
|
|
1022
|
+
"div",
|
|
1023
|
+
{
|
|
1024
|
+
"data-slot": "kanban-card-overlay",
|
|
1025
|
+
className: cn(
|
|
1026
|
+
"text-sm text-foreground shadow-xl ring-1 ring-black/5",
|
|
1027
|
+
densityCardClass[density],
|
|
1028
|
+
cardSurfaceClass(cardVariant, activeCard.column.tone ?? "default"),
|
|
1029
|
+
"rotate-2 cursor-grabbing"
|
|
1030
|
+
),
|
|
1031
|
+
children: renderCard(activeCard.card, {
|
|
1032
|
+
column: activeCard.column,
|
|
1033
|
+
isDragging: true,
|
|
1034
|
+
isOverlay: true
|
|
1035
|
+
})
|
|
1036
|
+
}
|
|
1037
|
+
) : null })
|
|
1038
|
+
]
|
|
1039
|
+
}
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
export {
|
|
1044
|
+
CopyButton,
|
|
1045
|
+
Checkbox,
|
|
1046
|
+
Select,
|
|
1047
|
+
SelectGroup,
|
|
1048
|
+
SelectValue,
|
|
1049
|
+
SelectTrigger,
|
|
1050
|
+
SelectContent,
|
|
1051
|
+
SelectLabel,
|
|
1052
|
+
SelectItem,
|
|
1053
|
+
SelectSeparator,
|
|
1054
|
+
SelectScrollUpButton,
|
|
1055
|
+
SelectScrollDownButton,
|
|
1056
|
+
Popover,
|
|
1057
|
+
PopoverTrigger,
|
|
1058
|
+
PopoverAnchor,
|
|
1059
|
+
PopoverContent,
|
|
1060
|
+
Skeleton,
|
|
1061
|
+
untitledButtonVariants,
|
|
1062
|
+
UntitledButton,
|
|
1063
|
+
Banner,
|
|
1064
|
+
Timeline,
|
|
1065
|
+
Kanban
|
|
1066
|
+
};
|