@epicbagel/saas-kit 1.1.0 → 1.2.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/README.md +36 -4
- package/THIRD_PARTY_NOTICES.md +217 -0
- package/dist/chunk-KADJ56AH.cjs +416 -0
- package/dist/chunk-M7FYGAGO.js +416 -0
- package/dist/primitives/alert.d.cts +1 -1
- package/dist/primitives/alert.d.ts +1 -1
- package/dist/primitives/badge.d.cts +1 -1
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/bubble.d.cts +1 -1
- package/dist/primitives/bubble.d.ts +1 -1
- package/dist/primitives/button.d.cts +2 -2
- package/dist/primitives/button.d.ts +2 -2
- package/dist/primitives/chat.cjs +21 -4
- package/dist/primitives/chat.d.cts +19 -0
- package/dist/primitives/chat.d.ts +19 -0
- package/dist/primitives/chat.js +20 -3
- package/dist/primitives/file-dropzone.cjs +8 -2
- package/dist/primitives/file-dropzone.d.cts +66 -3
- package/dist/primitives/file-dropzone.d.ts +66 -3
- package/dist/primitives/file-dropzone.js +9 -3
- package/package.json +1 -1
- package/dist/chunk-6V4XMQ6D.js +0 -118
- package/dist/chunk-I5UQSBBZ.cjs +0 -118
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
Text
|
|
4
|
+
} from "./chunk-EIY5LLSP.js";
|
|
5
|
+
import {
|
|
6
|
+
Tooltip,
|
|
7
|
+
TooltipContent,
|
|
8
|
+
TooltipTrigger
|
|
9
|
+
} from "./chunk-ZRLUUZHK.js";
|
|
10
|
+
import {
|
|
11
|
+
Button
|
|
12
|
+
} from "./chunk-HQRWRRZJ.js";
|
|
13
|
+
import {
|
|
14
|
+
cn
|
|
15
|
+
} from "./chunk-G6SRSWXB.js";
|
|
16
|
+
|
|
17
|
+
// src/primitives/file-dropzone.tsx
|
|
18
|
+
import { FileUp, Loader2 } from "lucide-react";
|
|
19
|
+
import { createPortal } from "react-dom";
|
|
20
|
+
import {
|
|
21
|
+
forwardRef,
|
|
22
|
+
useCallback,
|
|
23
|
+
useEffect,
|
|
24
|
+
useRef,
|
|
25
|
+
useState
|
|
26
|
+
} from "react";
|
|
27
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
28
|
+
function FileDropzone({
|
|
29
|
+
onFile,
|
|
30
|
+
onFiles,
|
|
31
|
+
multiple = false,
|
|
32
|
+
accept,
|
|
33
|
+
disabled = false,
|
|
34
|
+
busy: externalBusy = false,
|
|
35
|
+
label = "Drop a file, or choose one",
|
|
36
|
+
description,
|
|
37
|
+
busyLabel = "Reading file\u2026",
|
|
38
|
+
icon,
|
|
39
|
+
variant = "dropzone",
|
|
40
|
+
className
|
|
41
|
+
}) {
|
|
42
|
+
const inputRef = useRef(null);
|
|
43
|
+
const [dragging, setDragging] = useState(false);
|
|
44
|
+
const [internalBusy, setInternalBusy] = useState(false);
|
|
45
|
+
const busy = externalBusy || internalBusy;
|
|
46
|
+
useFileDropGuard();
|
|
47
|
+
async function select(files) {
|
|
48
|
+
if (files.length === 0 || disabled || busy) return;
|
|
49
|
+
setInternalBusy(true);
|
|
50
|
+
try {
|
|
51
|
+
if (multiple) {
|
|
52
|
+
await onFiles?.(files);
|
|
53
|
+
} else {
|
|
54
|
+
const first = files[0];
|
|
55
|
+
if (first) await onFile?.(first);
|
|
56
|
+
}
|
|
57
|
+
} finally {
|
|
58
|
+
setInternalBusy(false);
|
|
59
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function onDrop(event) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
setDragging(false);
|
|
65
|
+
void select(Array.from(event.dataTransfer.files ?? []));
|
|
66
|
+
}
|
|
67
|
+
const trigger = /* @__PURE__ */ jsx(
|
|
68
|
+
Button,
|
|
69
|
+
{
|
|
70
|
+
type: "button",
|
|
71
|
+
"data-file-dropzone": "true",
|
|
72
|
+
variant: "outline",
|
|
73
|
+
size: variant === "icon" ? "icon" : "default",
|
|
74
|
+
"aria-label": variant === "icon" ? String(busy ? busyLabel : label) : void 0,
|
|
75
|
+
disabled: disabled || busy,
|
|
76
|
+
onClick: () => inputRef.current?.click(),
|
|
77
|
+
onDragEnter: (event) => {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
setDragging(true);
|
|
80
|
+
},
|
|
81
|
+
onDragOver: (event) => {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
setDragging(true);
|
|
84
|
+
},
|
|
85
|
+
onDragLeave: () => setDragging(false),
|
|
86
|
+
onDrop,
|
|
87
|
+
className: cn(
|
|
88
|
+
variant === "dropzone" ? "h-auto w-full flex-col whitespace-normal border-2 border-dashed px-6 py-8 text-center" : variant === "button" ? "w-fit" : "",
|
|
89
|
+
dragging && "border-primary bg-primary/5",
|
|
90
|
+
className
|
|
91
|
+
),
|
|
92
|
+
children: /* @__PURE__ */ jsx(
|
|
93
|
+
FileDropPrompt,
|
|
94
|
+
{
|
|
95
|
+
variant,
|
|
96
|
+
icon: busy ? /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin" }) : icon ?? /* @__PURE__ */ jsx(FileUp, { className: "h-5 w-5" }),
|
|
97
|
+
label: busy ? busyLabel : label,
|
|
98
|
+
description
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
"input",
|
|
106
|
+
{
|
|
107
|
+
ref: inputRef,
|
|
108
|
+
type: "file",
|
|
109
|
+
accept,
|
|
110
|
+
multiple,
|
|
111
|
+
disabled: disabled || busy,
|
|
112
|
+
onChange: (event) => void select(Array.from(event.target.files ?? [])),
|
|
113
|
+
className: "sr-only",
|
|
114
|
+
tabIndex: -1,
|
|
115
|
+
"aria-hidden": "true"
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
variant === "icon" && description ? /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
119
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: trigger }),
|
|
120
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: description })
|
|
121
|
+
] }) : trigger
|
|
122
|
+
] });
|
|
123
|
+
}
|
|
124
|
+
function carriesFiles(data) {
|
|
125
|
+
if (!data) return false;
|
|
126
|
+
return Array.from(data.types ?? []).includes("Files") || (data.files?.length ?? 0) > 0;
|
|
127
|
+
}
|
|
128
|
+
function isNativeFileInput(target) {
|
|
129
|
+
const element = target;
|
|
130
|
+
return element?.tagName === "INPUT" && element.type === "file";
|
|
131
|
+
}
|
|
132
|
+
var inPageDrag = false;
|
|
133
|
+
var inPageDragUsers = 0;
|
|
134
|
+
var markInPageDrag = () => {
|
|
135
|
+
inPageDrag = true;
|
|
136
|
+
};
|
|
137
|
+
var clearInPageDrag = () => {
|
|
138
|
+
inPageDrag = false;
|
|
139
|
+
};
|
|
140
|
+
var clearAfterDrop = () => {
|
|
141
|
+
window.setTimeout(clearInPageDrag, 0);
|
|
142
|
+
};
|
|
143
|
+
var clearOnMove = () => {
|
|
144
|
+
if (inPageDrag) inPageDrag = false;
|
|
145
|
+
};
|
|
146
|
+
function trackInPageDrags() {
|
|
147
|
+
if (inPageDragUsers++ === 0) {
|
|
148
|
+
window.addEventListener("dragstart", markInPageDrag, true);
|
|
149
|
+
window.addEventListener("dragend", clearInPageDrag, true);
|
|
150
|
+
window.addEventListener("drop", clearAfterDrop);
|
|
151
|
+
window.addEventListener("mousemove", clearOnMove, { passive: true });
|
|
152
|
+
}
|
|
153
|
+
return () => {
|
|
154
|
+
if (--inPageDragUsers > 0) return;
|
|
155
|
+
window.removeEventListener("dragstart", markInPageDrag, true);
|
|
156
|
+
window.removeEventListener("dragend", clearInPageDrag, true);
|
|
157
|
+
window.removeEventListener("drop", clearAfterDrop);
|
|
158
|
+
window.removeEventListener("mousemove", clearOnMove);
|
|
159
|
+
inPageDrag = false;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function isUnclaimedFileDrag(event) {
|
|
163
|
+
return !event.defaultPrevented && !inPageDrag && carriesFiles(event.dataTransfer) && !isNativeFileInput(event.target);
|
|
164
|
+
}
|
|
165
|
+
function installFileDropGuard() {
|
|
166
|
+
if (typeof window === "undefined") return () => void 0;
|
|
167
|
+
const release = trackInPageDrags();
|
|
168
|
+
const onDragOver = (event) => {
|
|
169
|
+
if (!isUnclaimedFileDrag(event)) return;
|
|
170
|
+
event.preventDefault();
|
|
171
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "none";
|
|
172
|
+
};
|
|
173
|
+
const onDrop = (event) => {
|
|
174
|
+
if (isUnclaimedFileDrag(event)) event.preventDefault();
|
|
175
|
+
};
|
|
176
|
+
window.addEventListener("dragover", onDragOver);
|
|
177
|
+
window.addEventListener("drop", onDrop);
|
|
178
|
+
let installed = true;
|
|
179
|
+
return () => {
|
|
180
|
+
if (!installed) return;
|
|
181
|
+
installed = false;
|
|
182
|
+
window.removeEventListener("dragover", onDragOver);
|
|
183
|
+
window.removeEventListener("drop", onDrop);
|
|
184
|
+
release();
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function useFileDropGuard(enabled = true) {
|
|
188
|
+
useEffect(() => enabled ? installFileDropGuard() : void 0, [enabled]);
|
|
189
|
+
}
|
|
190
|
+
function FileDropPrompt({
|
|
191
|
+
icon,
|
|
192
|
+
label,
|
|
193
|
+
description,
|
|
194
|
+
variant = "dropzone"
|
|
195
|
+
}) {
|
|
196
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
197
|
+
/* @__PURE__ */ jsx("span", { className: cn(
|
|
198
|
+
"flex items-center justify-center text-muted-foreground",
|
|
199
|
+
variant === "dropzone" && "mb-2 h-10 w-10 rounded-full bg-muted"
|
|
200
|
+
), children: icon }),
|
|
201
|
+
/* @__PURE__ */ jsx("span", { className: variant === "icon" ? "sr-only" : "font-medium", children: label }),
|
|
202
|
+
description && variant === "dropzone" && /* @__PURE__ */ jsx(Text, { variant: "muted", className: "mt-1 max-w-md text-xs", children: description })
|
|
203
|
+
] });
|
|
204
|
+
}
|
|
205
|
+
var DRAG_HEARTBEAT_MS = 1e3;
|
|
206
|
+
function visibleBand(element) {
|
|
207
|
+
const rect = element.getBoundingClientRect();
|
|
208
|
+
const viewport = window.innerHeight;
|
|
209
|
+
if (rect.top >= 0 && rect.bottom <= viewport) return null;
|
|
210
|
+
const top = Math.max(rect.top, 0);
|
|
211
|
+
const bottom = Math.min(rect.bottom, viewport);
|
|
212
|
+
return bottom > top ? { top: top - rect.top, height: bottom - top } : null;
|
|
213
|
+
}
|
|
214
|
+
var FileDropArea = forwardRef(function FileDropArea2({
|
|
215
|
+
onFiles,
|
|
216
|
+
disabled = false,
|
|
217
|
+
scope = "area",
|
|
218
|
+
label = "Drop to attach",
|
|
219
|
+
description,
|
|
220
|
+
icon,
|
|
221
|
+
className,
|
|
222
|
+
children,
|
|
223
|
+
onDragEnter,
|
|
224
|
+
onDragOver,
|
|
225
|
+
onDragLeave,
|
|
226
|
+
...props
|
|
227
|
+
}, ref) {
|
|
228
|
+
const active = Boolean(onFiles) && !disabled;
|
|
229
|
+
const [dragging, setDragging] = useState(false);
|
|
230
|
+
const [band, setBand] = useState(null);
|
|
231
|
+
const root = useRef(null);
|
|
232
|
+
const entered = useRef([]);
|
|
233
|
+
useFileDropGuard(Boolean(onFiles));
|
|
234
|
+
const setRoot = useCallback(
|
|
235
|
+
(node) => {
|
|
236
|
+
root.current = node;
|
|
237
|
+
if (typeof ref === "function") ref(node);
|
|
238
|
+
else if (ref) ref.current = node;
|
|
239
|
+
},
|
|
240
|
+
[ref]
|
|
241
|
+
);
|
|
242
|
+
const reset = useCallback(() => {
|
|
243
|
+
entered.current = [];
|
|
244
|
+
setDragging(false);
|
|
245
|
+
}, []);
|
|
246
|
+
const enter = useCallback((target) => {
|
|
247
|
+
if (target && !entered.current.includes(target)) entered.current.push(target);
|
|
248
|
+
setDragging(true);
|
|
249
|
+
}, []);
|
|
250
|
+
const leave = useCallback((target, container) => {
|
|
251
|
+
entered.current = entered.current.filter(
|
|
252
|
+
(node) => node !== target && node instanceof Node && node.isConnected && container.contains(node)
|
|
253
|
+
);
|
|
254
|
+
if (entered.current.length === 0) setDragging(false);
|
|
255
|
+
}, []);
|
|
256
|
+
const deliver = useCallback(
|
|
257
|
+
(data) => {
|
|
258
|
+
const files = Array.from(data?.files ?? []);
|
|
259
|
+
if (files.length > 0) void onFiles?.(files);
|
|
260
|
+
},
|
|
261
|
+
[onFiles]
|
|
262
|
+
);
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
if (scope !== "page" || !active) return;
|
|
265
|
+
const onEnter = (event) => {
|
|
266
|
+
if (!isUnclaimedFileDrag(event)) return;
|
|
267
|
+
event.preventDefault();
|
|
268
|
+
enter(event.target);
|
|
269
|
+
};
|
|
270
|
+
const onOver = (event) => {
|
|
271
|
+
if (!isUnclaimedFileDrag(event)) return;
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
|
274
|
+
if (entered.current.length === 0) enter(event.target);
|
|
275
|
+
};
|
|
276
|
+
const onLeave = (event) => leave(event.target, document);
|
|
277
|
+
const onDrop = (event) => {
|
|
278
|
+
reset();
|
|
279
|
+
if (!isUnclaimedFileDrag(event)) return;
|
|
280
|
+
event.preventDefault();
|
|
281
|
+
deliver(event.dataTransfer);
|
|
282
|
+
};
|
|
283
|
+
document.addEventListener("dragenter", onEnter);
|
|
284
|
+
document.addEventListener("dragover", onOver);
|
|
285
|
+
document.addEventListener("dragleave", onLeave);
|
|
286
|
+
document.addEventListener("drop", onDrop);
|
|
287
|
+
return () => {
|
|
288
|
+
document.removeEventListener("dragenter", onEnter);
|
|
289
|
+
document.removeEventListener("dragover", onOver);
|
|
290
|
+
document.removeEventListener("dragleave", onLeave);
|
|
291
|
+
document.removeEventListener("drop", onDrop);
|
|
292
|
+
};
|
|
293
|
+
}, [active, deliver, enter, leave, reset, scope]);
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
if (!dragging) return;
|
|
296
|
+
let heartbeat = window.setTimeout(reset, DRAG_HEARTBEAT_MS);
|
|
297
|
+
const onDragOverAnywhere = () => {
|
|
298
|
+
window.clearTimeout(heartbeat);
|
|
299
|
+
heartbeat = window.setTimeout(reset, DRAG_HEARTBEAT_MS);
|
|
300
|
+
};
|
|
301
|
+
const onLeaveWindow = (event) => {
|
|
302
|
+
if (event.relatedTarget) return;
|
|
303
|
+
const { clientX: x, clientY: y } = event;
|
|
304
|
+
if (x <= 0 || y <= 0 || x >= window.innerWidth || y >= window.innerHeight) reset();
|
|
305
|
+
};
|
|
306
|
+
window.addEventListener("dragover", onDragOverAnywhere, true);
|
|
307
|
+
window.addEventListener("drop", reset);
|
|
308
|
+
window.addEventListener("dragend", reset);
|
|
309
|
+
window.addEventListener("dragleave", onLeaveWindow);
|
|
310
|
+
window.addEventListener("mousemove", reset, { passive: true });
|
|
311
|
+
return () => {
|
|
312
|
+
window.clearTimeout(heartbeat);
|
|
313
|
+
window.removeEventListener("dragover", onDragOverAnywhere, true);
|
|
314
|
+
window.removeEventListener("drop", reset);
|
|
315
|
+
window.removeEventListener("dragend", reset);
|
|
316
|
+
window.removeEventListener("dragleave", onLeaveWindow);
|
|
317
|
+
window.removeEventListener("mousemove", reset);
|
|
318
|
+
};
|
|
319
|
+
}, [dragging, reset]);
|
|
320
|
+
const showOverlay = dragging && active;
|
|
321
|
+
useEffect(() => {
|
|
322
|
+
if (!showOverlay || scope !== "area") return;
|
|
323
|
+
const measure = () => {
|
|
324
|
+
if (root.current) setBand(visibleBand(root.current));
|
|
325
|
+
};
|
|
326
|
+
measure();
|
|
327
|
+
window.addEventListener("scroll", measure, { capture: true, passive: true });
|
|
328
|
+
window.addEventListener("resize", measure);
|
|
329
|
+
return () => {
|
|
330
|
+
window.removeEventListener("scroll", measure, { capture: true });
|
|
331
|
+
window.removeEventListener("resize", measure);
|
|
332
|
+
setBand(null);
|
|
333
|
+
};
|
|
334
|
+
}, [scope, showOverlay]);
|
|
335
|
+
const handlesHere = active && scope === "area";
|
|
336
|
+
const prompt = /* @__PURE__ */ jsx(FileDropPrompt, { icon: icon ?? /* @__PURE__ */ jsx(FileUp, { className: "h-5 w-5" }), label, description });
|
|
337
|
+
let overlay = null;
|
|
338
|
+
if (showOverlay && scope === "page" && typeof document !== "undefined") {
|
|
339
|
+
overlay = createPortal(
|
|
340
|
+
/* @__PURE__ */ jsx(
|
|
341
|
+
"div",
|
|
342
|
+
{
|
|
343
|
+
"data-slot": "file-drop-overlay",
|
|
344
|
+
"data-scope": "page",
|
|
345
|
+
"aria-hidden": "true",
|
|
346
|
+
className: "pointer-events-none fixed inset-0 z-50 bg-background/80 p-3 supports-backdrop-filter:backdrop-blur-sm motion-safe:animate-in motion-safe:fade-in-0",
|
|
347
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex h-full flex-col items-center justify-center rounded-xl border-2 border-dashed border-primary p-6 text-center text-sm", children: prompt })
|
|
348
|
+
}
|
|
349
|
+
),
|
|
350
|
+
document.body
|
|
351
|
+
);
|
|
352
|
+
} else if (showOverlay && scope === "area") {
|
|
353
|
+
overlay = /* @__PURE__ */ jsx(
|
|
354
|
+
"div",
|
|
355
|
+
{
|
|
356
|
+
"data-slot": "file-drop-overlay",
|
|
357
|
+
"data-scope": "area",
|
|
358
|
+
"aria-hidden": "true",
|
|
359
|
+
className: "absolute inset-0 z-30 rounded-[inherit] border-2 border-dashed border-primary bg-background/90 text-sm supports-backdrop-filter:backdrop-blur-sm motion-safe:animate-in motion-safe:fade-in-0 [&_*]:pointer-events-none",
|
|
360
|
+
children: /* @__PURE__ */ jsx(
|
|
361
|
+
"div",
|
|
362
|
+
{
|
|
363
|
+
"data-slot": "file-drop-prompt",
|
|
364
|
+
className: "absolute inset-x-0 flex flex-col items-center justify-center p-6 text-center",
|
|
365
|
+
style: band ?? { top: 0, height: "100%" },
|
|
366
|
+
children: prompt
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
return /* @__PURE__ */ jsxs(
|
|
373
|
+
"div",
|
|
374
|
+
{
|
|
375
|
+
ref: setRoot,
|
|
376
|
+
"data-file-drop": showOverlay ? "active" : void 0,
|
|
377
|
+
className: cn("relative", className),
|
|
378
|
+
...props,
|
|
379
|
+
onDragEnter: (event) => {
|
|
380
|
+
onDragEnter?.(event);
|
|
381
|
+
if (!handlesHere || !isUnclaimedFileDrag(event.nativeEvent)) return;
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
enter(event.target);
|
|
384
|
+
},
|
|
385
|
+
onDragOver: (event) => {
|
|
386
|
+
onDragOver?.(event);
|
|
387
|
+
if (!handlesHere || !isUnclaimedFileDrag(event.nativeEvent)) return;
|
|
388
|
+
event.preventDefault();
|
|
389
|
+
event.dataTransfer.dropEffect = "copy";
|
|
390
|
+
if (entered.current.length === 0) enter(event.target);
|
|
391
|
+
},
|
|
392
|
+
onDragLeave: (event) => {
|
|
393
|
+
onDragLeave?.(event);
|
|
394
|
+
if (scope === "area") leave(event.target, event.currentTarget);
|
|
395
|
+
},
|
|
396
|
+
onDrop: (event) => {
|
|
397
|
+
if (scope !== "area") return;
|
|
398
|
+
reset();
|
|
399
|
+
if (!onFiles || !isUnclaimedFileDrag(event.nativeEvent)) return;
|
|
400
|
+
event.preventDefault();
|
|
401
|
+
if (!disabled) deliver(event.dataTransfer);
|
|
402
|
+
},
|
|
403
|
+
children: [
|
|
404
|
+
children,
|
|
405
|
+
overlay
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
export {
|
|
412
|
+
FileDropzone,
|
|
413
|
+
installFileDropGuard,
|
|
414
|
+
useFileDropGuard,
|
|
415
|
+
FileDropArea
|
|
416
|
+
};
|
|
@@ -4,7 +4,7 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
4
4
|
import { VariantProps } from 'class-variance-authority';
|
|
5
5
|
|
|
6
6
|
declare const alertVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "success" | "default" | "destructive" | "warning" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface AlertProps extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof alertVariants> {
|
|
10
10
|
marginBottom?: "none" | "md";
|
|
@@ -4,7 +4,7 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
4
4
|
import { VariantProps } from 'class-variance-authority';
|
|
5
5
|
|
|
6
6
|
declare const alertVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "success" | "default" | "destructive" | "warning" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface AlertProps extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof alertVariants> {
|
|
10
10
|
marginBottom?: "none" | "md";
|
|
@@ -4,7 +4,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
import { ElementType } from 'react';
|
|
5
5
|
|
|
6
6
|
declare const badgeVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "neutral" | "error" | "success" | "default" | "secondary" | "destructive" | "outline" | "warning" | "info" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
10
10
|
weight?: "normal" | "semibold" | "bold";
|
|
@@ -4,7 +4,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
import { ElementType } from 'react';
|
|
5
5
|
|
|
6
6
|
declare const badgeVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "neutral" | "error" | "success" | "default" | "secondary" | "destructive" | "outline" | "warning" | "info" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
10
10
|
weight?: "normal" | "semibold" | "bold";
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare function BubbleGroup({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
7
7
|
declare const bubbleVariants: (props?: ({
|
|
8
|
-
variant?: "default" | "muted" | "
|
|
8
|
+
variant?: "default" | "muted" | "ghost" | "secondary" | "destructive" | "outline" | "tinted" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare function Bubble({ variant, align, className, ...props }: React.ComponentProps<"div"> & VariantProps<typeof bubbleVariants> & {
|
|
11
11
|
align?: "start" | "end";
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare function BubbleGroup({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
7
7
|
declare const bubbleVariants: (props?: ({
|
|
8
|
-
variant?: "default" | "muted" | "
|
|
8
|
+
variant?: "default" | "muted" | "ghost" | "secondary" | "destructive" | "outline" | "tinted" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare function Bubble({ variant, align, className, ...props }: React.ComponentProps<"div"> & VariantProps<typeof bubbleVariants> & {
|
|
11
11
|
align?: "start" | "end";
|
|
@@ -4,8 +4,8 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
4
4
|
import { VariantProps } from 'class-variance-authority';
|
|
5
5
|
|
|
6
6
|
declare const buttonVariants: (props?: ({
|
|
7
|
-
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "outline-muted" | "
|
|
8
|
-
size?: "filter" | "status" | "toolbar" | "sm" | "lg" | "default" | "micro" | "compact" | "hero" | "icon" | "icon-lg" | "icon-sm" | "icon-md" | "icon-xs" | "icon-compact" | "icon-micro" | "bare" | "voice-sm" | "voice" | "suggestion" |
|
|
7
|
+
variant?: "link" | "default" | "ghost" | "secondary" | "destructive" | "outline" | "outline-muted" | "quiet" | "ghost-destructive" | "link-muted" | "inverted" | "status-error" | "status-warning" | "status-muted" | null | undefined;
|
|
8
|
+
size?: "filter" | "status" | "toolbar" | "sm" | "lg" | "default" | "micro" | "tone" | "compact" | "hero" | "icon" | "icon-lg" | "icon-sm" | "icon-md" | "icon-xs" | "icon-compact" | "icon-micro" | "bare" | "voice-sm" | "voice" | "suggestion" | null | undefined;
|
|
9
9
|
width?: "auto" | "full" | "responsive" | null | undefined;
|
|
10
10
|
align?: "center" | "start" | null | undefined;
|
|
11
11
|
shrink?: boolean | null | undefined;
|
|
@@ -4,8 +4,8 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
4
4
|
import { VariantProps } from 'class-variance-authority';
|
|
5
5
|
|
|
6
6
|
declare const buttonVariants: (props?: ({
|
|
7
|
-
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "outline-muted" | "
|
|
8
|
-
size?: "filter" | "status" | "toolbar" | "sm" | "lg" | "default" | "micro" | "compact" | "hero" | "icon" | "icon-lg" | "icon-sm" | "icon-md" | "icon-xs" | "icon-compact" | "icon-micro" | "bare" | "voice-sm" | "voice" | "suggestion" |
|
|
7
|
+
variant?: "link" | "default" | "ghost" | "secondary" | "destructive" | "outline" | "outline-muted" | "quiet" | "ghost-destructive" | "link-muted" | "inverted" | "status-error" | "status-warning" | "status-muted" | null | undefined;
|
|
8
|
+
size?: "filter" | "status" | "toolbar" | "sm" | "lg" | "default" | "micro" | "tone" | "compact" | "hero" | "icon" | "icon-lg" | "icon-sm" | "icon-md" | "icon-xs" | "icon-compact" | "icon-micro" | "bare" | "voice-sm" | "voice" | "suggestion" | null | undefined;
|
|
9
9
|
width?: "auto" | "full" | "responsive" | null | undefined;
|
|
10
10
|
align?: "center" | "start" | null | undefined;
|
|
11
11
|
shrink?: boolean | null | undefined;
|
package/dist/primitives/chat.cjs
CHANGED
|
@@ -40,7 +40,8 @@ var _chunkKENBU3NVcjs = require('../chunk-KENBU3NV.cjs');
|
|
|
40
40
|
require('../chunk-ELPV44S6.cjs');
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
|
|
44
|
+
var _chunkKADJ56AHcjs = require('../chunk-KADJ56AH.cjs');
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
|
|
@@ -608,6 +609,9 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
608
609
|
emptyFileDropzone = false,
|
|
609
610
|
emptyFileDropzoneLabel = "Drop files here, or choose them",
|
|
610
611
|
emptyFileDropzoneDescription,
|
|
612
|
+
fileDrop = "conversation",
|
|
613
|
+
fileDropLabel,
|
|
614
|
+
fileDropDescription,
|
|
611
615
|
voice = true,
|
|
612
616
|
voiceTitle,
|
|
613
617
|
activeVoiceTitle,
|
|
@@ -660,21 +664,34 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
660
664
|
}),
|
|
661
665
|
[]
|
|
662
666
|
);
|
|
667
|
+
const dropFiles = _react.useCallback.call(void 0,
|
|
668
|
+
(files) => {
|
|
669
|
+
if (!conversationStarted && mode === "voice") setUserMode("text");
|
|
670
|
+
return _optionalChain([onFilesSelected, 'optionalCall', _16 => _16(files)]);
|
|
671
|
+
},
|
|
672
|
+
[conversationStarted, mode, onFilesSelected]
|
|
673
|
+
);
|
|
663
674
|
const application = layout === "application";
|
|
664
675
|
const workspace = layout === "workspace";
|
|
665
676
|
const showEmptyFileDropzone = emptyFileDropzone && Boolean(onFilesSelected) && attachments.length === 0;
|
|
677
|
+
const showEmptyPending = !conversationStarted && pending && Boolean(emptyPendingContent);
|
|
666
678
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
667
|
-
|
|
679
|
+
_chunkKADJ56AHcjs.FileDropArea,
|
|
668
680
|
{
|
|
669
681
|
"data-slot": "chat",
|
|
670
682
|
"data-layout": layout,
|
|
683
|
+
onFiles: onFilesSelected ? dropFiles : void 0,
|
|
684
|
+
disabled: fileDrop === false || showEmptyPending,
|
|
685
|
+
scope: fileDrop === "page" ? "page" : "area",
|
|
686
|
+
label: fileDropLabel,
|
|
687
|
+
description: fileDropDescription,
|
|
671
688
|
className: _chunkKWLSBTB2cjs.cn.call(void 0,
|
|
672
689
|
"w-full",
|
|
673
690
|
application && "flex min-h-[calc(100svh-4rem)] flex-1 flex-col md:block md:min-h-0",
|
|
674
691
|
workspace && "flex h-full min-h-0 flex-1 flex-col overflow-hidden",
|
|
675
692
|
className
|
|
676
693
|
),
|
|
677
|
-
children:
|
|
694
|
+
children: showEmptyPending ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
678
695
|
"div",
|
|
679
696
|
{
|
|
680
697
|
"data-slot": "chat-empty-pending",
|
|
@@ -727,7 +744,7 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
727
744
|
),
|
|
728
745
|
children: [
|
|
729
746
|
showEmptyFileDropzone && onFilesSelected && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
730
|
-
|
|
747
|
+
_chunkKADJ56AHcjs.FileDropzone,
|
|
731
748
|
{
|
|
732
749
|
multiple: true,
|
|
733
750
|
onFiles: onFilesSelected,
|
|
@@ -251,6 +251,21 @@ interface ChatProps {
|
|
|
251
251
|
emptyFileDropzoneLabel?: ReactNode;
|
|
252
252
|
/** Supporting file type or workflow copy inside the drop target. */
|
|
253
253
|
emptyFileDropzoneDescription?: ReactNode;
|
|
254
|
+
/**
|
|
255
|
+
* Where a dragged file can be dropped. With `onFilesSelected`, a file
|
|
256
|
+
* dragged over the conversation raises a "Drop to attach" overlay, and a
|
|
257
|
+
* drop hands every file to `onFilesSelected`, exactly as the composer's
|
|
258
|
+
* file menu does: same validation, same staged files, same errors.
|
|
259
|
+
* `"conversation"` (the default) takes drops on the whole Chat, `"page"`
|
|
260
|
+
* anywhere in the document (its overlay then covers the viewport), and
|
|
261
|
+
* `false` none. Either way a file dropped where nothing takes it never
|
|
262
|
+
* opens in the browser.
|
|
263
|
+
*/
|
|
264
|
+
fileDrop?: "conversation" | "page" | false;
|
|
265
|
+
/** The drop overlay's label. Defaults to "Drop to attach". */
|
|
266
|
+
fileDropLabel?: ReactNode;
|
|
267
|
+
/** A muted line under the overlay's label, such as the accepted types and limits. */
|
|
268
|
+
fileDropDescription?: ReactNode;
|
|
254
269
|
/** Enable voice dictation. Defaults to true. */
|
|
255
270
|
voice?: boolean;
|
|
256
271
|
/** Title shown inside the voice overlay. */
|
|
@@ -318,6 +333,10 @@ interface ChatHandle {
|
|
|
318
333
|
* **Active state** (messages exist) — always the compact text input;
|
|
319
334
|
* the mic stays as an accessory icon.
|
|
320
335
|
*
|
|
336
|
+
* **Files** — with `onFilesSelected`, the composer's menu picks files and a
|
|
337
|
+
* file dragged anywhere on the conversation can be dropped (`fileDrop`);
|
|
338
|
+
* both hand the files to `onFilesSelected`, which validates and stages them.
|
|
339
|
+
*
|
|
321
340
|
* Stateless w.r.t. the conversation — pass `messages` and handle
|
|
322
341
|
* `onSend` from the parent. The input text is managed internally.
|
|
323
342
|
*/
|
|
@@ -251,6 +251,21 @@ interface ChatProps {
|
|
|
251
251
|
emptyFileDropzoneLabel?: ReactNode;
|
|
252
252
|
/** Supporting file type or workflow copy inside the drop target. */
|
|
253
253
|
emptyFileDropzoneDescription?: ReactNode;
|
|
254
|
+
/**
|
|
255
|
+
* Where a dragged file can be dropped. With `onFilesSelected`, a file
|
|
256
|
+
* dragged over the conversation raises a "Drop to attach" overlay, and a
|
|
257
|
+
* drop hands every file to `onFilesSelected`, exactly as the composer's
|
|
258
|
+
* file menu does: same validation, same staged files, same errors.
|
|
259
|
+
* `"conversation"` (the default) takes drops on the whole Chat, `"page"`
|
|
260
|
+
* anywhere in the document (its overlay then covers the viewport), and
|
|
261
|
+
* `false` none. Either way a file dropped where nothing takes it never
|
|
262
|
+
* opens in the browser.
|
|
263
|
+
*/
|
|
264
|
+
fileDrop?: "conversation" | "page" | false;
|
|
265
|
+
/** The drop overlay's label. Defaults to "Drop to attach". */
|
|
266
|
+
fileDropLabel?: ReactNode;
|
|
267
|
+
/** A muted line under the overlay's label, such as the accepted types and limits. */
|
|
268
|
+
fileDropDescription?: ReactNode;
|
|
254
269
|
/** Enable voice dictation. Defaults to true. */
|
|
255
270
|
voice?: boolean;
|
|
256
271
|
/** Title shown inside the voice overlay. */
|
|
@@ -318,6 +333,10 @@ interface ChatHandle {
|
|
|
318
333
|
* **Active state** (messages exist) — always the compact text input;
|
|
319
334
|
* the mic stays as an accessory icon.
|
|
320
335
|
*
|
|
336
|
+
* **Files** — with `onFilesSelected`, the composer's menu picks files and a
|
|
337
|
+
* file dragged anywhere on the conversation can be dropped (`fileDrop`);
|
|
338
|
+
* both hand the files to `onFilesSelected`, which validates and stages them.
|
|
339
|
+
*
|
|
321
340
|
* Stateless w.r.t. the conversation — pass `messages` and handle
|
|
322
341
|
* `onSend` from the parent. The input text is managed internally.
|
|
323
342
|
*/
|