haiku-react-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1550 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +177 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.js +1525 -0
- package/dist/index.js.map +1 -0
- package/dist/preset.css +87 -0
- package/dist/styles.css +1409 -0
- package/dist/styles.css.map +1 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1525 @@
|
|
|
1
|
+
import { cva } from 'class-variance-authority';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { Icon } from '@iconify/react';
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
import * as React2 from 'react';
|
|
7
|
+
import { useState, useId, useRef, useCallback, useEffect } from 'react';
|
|
8
|
+
|
|
9
|
+
// src/components/Button/button.tsx
|
|
10
|
+
function cn(...inputs) {
|
|
11
|
+
return twMerge(clsx(inputs));
|
|
12
|
+
}
|
|
13
|
+
var buttonVariants = cva(
|
|
14
|
+
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-300 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-primary-700 dark:focus-visible:ring-offset-zinc-950",
|
|
15
|
+
{
|
|
16
|
+
variants: {
|
|
17
|
+
variant: {
|
|
18
|
+
primary: "shadow-sm",
|
|
19
|
+
default: "border shadow-sm",
|
|
20
|
+
dashed: "border-2 border-dashed shadow-sm",
|
|
21
|
+
filled: "",
|
|
22
|
+
text: "",
|
|
23
|
+
link: "underline-offset-4 hover:underline"
|
|
24
|
+
},
|
|
25
|
+
color: {
|
|
26
|
+
default: "",
|
|
27
|
+
primary: "",
|
|
28
|
+
danger: "",
|
|
29
|
+
info: "",
|
|
30
|
+
success: "",
|
|
31
|
+
warning: ""
|
|
32
|
+
},
|
|
33
|
+
size: {
|
|
34
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
35
|
+
sm: "h-8 gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
36
|
+
lg: "h-10 px-6 has-[>svg]:px-4"
|
|
37
|
+
},
|
|
38
|
+
shape: {
|
|
39
|
+
default: "rounded-md",
|
|
40
|
+
rounded: "rounded-full",
|
|
41
|
+
square: "rounded-none"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
compoundVariants: [
|
|
45
|
+
// primary (solid)
|
|
46
|
+
{
|
|
47
|
+
variant: "primary",
|
|
48
|
+
color: "default",
|
|
49
|
+
className: "bg-default text-white hover:bg-default"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
variant: "primary",
|
|
53
|
+
color: "primary",
|
|
54
|
+
className: "bg-primary-900 text-primary-foreground hover:bg-primary-800"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
variant: "primary",
|
|
58
|
+
color: "danger",
|
|
59
|
+
className: "bg-danger text-white hover:bg-danger"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
variant: "primary",
|
|
63
|
+
color: "info",
|
|
64
|
+
className: "bg-info text-white hover:bg-info"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
variant: "primary",
|
|
68
|
+
color: "success",
|
|
69
|
+
className: "bg-success text-white hover:bg-success"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
variant: "primary",
|
|
73
|
+
color: "warning",
|
|
74
|
+
className: "bg-warning text-white hover:bg-warning"
|
|
75
|
+
},
|
|
76
|
+
// default (outline)
|
|
77
|
+
{
|
|
78
|
+
variant: "default",
|
|
79
|
+
color: "default",
|
|
80
|
+
className: "bg-white text-default border-default hover:bg-default-foreground hover:border-default"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
variant: "default",
|
|
84
|
+
color: "primary",
|
|
85
|
+
className: "bg-white text-primary-900 border-primary-300 hover:bg-primary-100/60 "
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
variant: "default",
|
|
89
|
+
color: "danger",
|
|
90
|
+
className: "bg-white text-danger border-danger-foreground hover:bg-danger-50 hover:bg-danger-foreground/6"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
variant: "default",
|
|
94
|
+
color: "info",
|
|
95
|
+
className: "bg-white text-info border-info-300 hover:bg-info-foreground/6 border-info-foreground"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
variant: "default",
|
|
99
|
+
color: "success",
|
|
100
|
+
className: "bg-white text-success border-success-300 hover:bg-success-foreground/6 border-success-foreground"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
variant: "default",
|
|
104
|
+
color: "warning",
|
|
105
|
+
className: "bg-white text-warning border-warning-300 hover:bg-warning-foreground/6 border-warning-foreground"
|
|
106
|
+
},
|
|
107
|
+
// dashed (dashed outline)
|
|
108
|
+
{
|
|
109
|
+
variant: "dashed",
|
|
110
|
+
color: "default",
|
|
111
|
+
className: "bg-white text-default border-default hover:bg-default-foreground hover:border-default"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
variant: "dashed",
|
|
115
|
+
color: "primary",
|
|
116
|
+
className: "bg-white text-primary-900 border-primary-300 hover:bg-primary-foreground hover:border-primary-400"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
variant: "dashed",
|
|
120
|
+
color: "danger",
|
|
121
|
+
className: "bg-white text-danger border-danger-foreground hover:bg-danger-50 hover:border-danger-foreground"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
variant: "dashed",
|
|
125
|
+
color: "info",
|
|
126
|
+
className: "bg-white text-blue-700 border-blue-300 hover:bg-blue-50 hover:border-blue-400"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
variant: "dashed",
|
|
130
|
+
color: "success",
|
|
131
|
+
className: "bg-white text-emerald-700 border-emerald-300 hover:bg-emerald-50 hover:border-emerald-400"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
variant: "dashed",
|
|
135
|
+
color: "warning",
|
|
136
|
+
className: "bg-white text-amber-800 border-amber-300 hover:bg-amber-50 hover:border-amber-400"
|
|
137
|
+
},
|
|
138
|
+
// filled (tinted)
|
|
139
|
+
{
|
|
140
|
+
variant: "filled",
|
|
141
|
+
color: "default",
|
|
142
|
+
className: "bg-zinc-100 text-zinc-900 hover:bg-zinc-200"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
variant: "filled",
|
|
146
|
+
color: "primary",
|
|
147
|
+
className: "bg-primary-100 text-primary-900 hover:bg-primary-200"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
variant: "filled",
|
|
151
|
+
color: "danger",
|
|
152
|
+
className: "bg-red-50 text-red-700 hover:bg-red-100"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
variant: "filled",
|
|
156
|
+
color: "info",
|
|
157
|
+
className: "bg-blue-50 text-blue-700 hover:bg-blue-100"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
variant: "filled",
|
|
161
|
+
color: "success",
|
|
162
|
+
className: "bg-emerald-50 text-emerald-700 hover:bg-emerald-100"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
variant: "filled",
|
|
166
|
+
color: "warning",
|
|
167
|
+
className: "bg-amber-50 text-amber-800 hover:bg-amber-100"
|
|
168
|
+
},
|
|
169
|
+
// text (no border/bg)
|
|
170
|
+
{
|
|
171
|
+
variant: "text",
|
|
172
|
+
color: "default",
|
|
173
|
+
className: "text-zinc-900 hover:bg-zinc-100"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
variant: "text",
|
|
177
|
+
color: "primary",
|
|
178
|
+
className: "text-primary-900 hover:bg-primary-100"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
variant: "text",
|
|
182
|
+
color: "danger",
|
|
183
|
+
className: "text-red-700 hover:bg-red-50"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
variant: "text",
|
|
187
|
+
color: "info",
|
|
188
|
+
className: "text-blue-700 hover:bg-blue-50"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
variant: "text",
|
|
192
|
+
color: "success",
|
|
193
|
+
className: "text-emerald-700 hover:bg-emerald-50"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
variant: "text",
|
|
197
|
+
color: "warning",
|
|
198
|
+
className: "text-amber-800 hover:bg-amber-50"
|
|
199
|
+
},
|
|
200
|
+
// link (no border/bg)
|
|
201
|
+
{
|
|
202
|
+
variant: "link",
|
|
203
|
+
color: "default",
|
|
204
|
+
className: "text-zinc-900 hover:text-zinc-700"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
variant: "link",
|
|
208
|
+
color: "primary",
|
|
209
|
+
className: "text-primary-900 hover:text-primary-800"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
variant: "link",
|
|
213
|
+
color: "danger",
|
|
214
|
+
className: "text-red-700 hover:text-red-600"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
variant: "link",
|
|
218
|
+
color: "info",
|
|
219
|
+
className: "text-blue-700 hover:text-blue-600"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
variant: "link",
|
|
223
|
+
color: "success",
|
|
224
|
+
className: "text-emerald-700 hover:text-emerald-600"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
variant: "link",
|
|
228
|
+
color: "warning",
|
|
229
|
+
className: "text-amber-800 hover:text-amber-700"
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
defaultVariants: {
|
|
233
|
+
variant: "default",
|
|
234
|
+
color: "default",
|
|
235
|
+
size: "default",
|
|
236
|
+
shape: "default"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
function Button({
|
|
241
|
+
className,
|
|
242
|
+
variant = "default",
|
|
243
|
+
size = "default",
|
|
244
|
+
color,
|
|
245
|
+
shape = "default",
|
|
246
|
+
loading = false,
|
|
247
|
+
disabled,
|
|
248
|
+
children,
|
|
249
|
+
icon,
|
|
250
|
+
as = "button",
|
|
251
|
+
...props
|
|
252
|
+
}) {
|
|
253
|
+
const resolvedColor = color != null ? color : variant === "primary" ? "primary" : "default";
|
|
254
|
+
const isDisabled = disabled || loading;
|
|
255
|
+
const isLabel = as === "label" || "htmlFor" in props;
|
|
256
|
+
const baseClassName = cn(
|
|
257
|
+
buttonVariants({ color: resolvedColor, variant, size, shape, className }),
|
|
258
|
+
loading && "disabled:cursor-wait opacity-70",
|
|
259
|
+
isDisabled && "cursor-not-allowed opacity-50"
|
|
260
|
+
);
|
|
261
|
+
if (isLabel) {
|
|
262
|
+
const { disabled: _disabled, ...labelProps } = props;
|
|
263
|
+
return /* @__PURE__ */ jsxs(
|
|
264
|
+
"label",
|
|
265
|
+
{
|
|
266
|
+
"data-slot": "button",
|
|
267
|
+
"data-variant": variant,
|
|
268
|
+
"data-size": size,
|
|
269
|
+
"data-shape": shape,
|
|
270
|
+
"data-color": resolvedColor,
|
|
271
|
+
"data-loading": loading ? "" : void 0,
|
|
272
|
+
"aria-busy": loading || void 0,
|
|
273
|
+
"aria-disabled": isDisabled || void 0,
|
|
274
|
+
className: baseClassName,
|
|
275
|
+
...labelProps,
|
|
276
|
+
children: [
|
|
277
|
+
loading ? /* @__PURE__ */ jsx(
|
|
278
|
+
"span",
|
|
279
|
+
{
|
|
280
|
+
"aria-hidden": "true",
|
|
281
|
+
className: "size-4 animate-spin rounded-full border-2 border-current border-t-transparent"
|
|
282
|
+
}
|
|
283
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon, width: "16", height: "16" }) : icon : null,
|
|
284
|
+
children
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return /* @__PURE__ */ jsxs(
|
|
290
|
+
"button",
|
|
291
|
+
{
|
|
292
|
+
"data-slot": "button",
|
|
293
|
+
"data-variant": variant,
|
|
294
|
+
"data-size": size,
|
|
295
|
+
"data-shape": shape,
|
|
296
|
+
"data-color": resolvedColor,
|
|
297
|
+
"data-loading": loading ? "" : void 0,
|
|
298
|
+
"aria-busy": loading || void 0,
|
|
299
|
+
disabled: isDisabled,
|
|
300
|
+
className: baseClassName,
|
|
301
|
+
...props,
|
|
302
|
+
children: [
|
|
303
|
+
loading ? /* @__PURE__ */ jsx(
|
|
304
|
+
"span",
|
|
305
|
+
{
|
|
306
|
+
"aria-hidden": "true",
|
|
307
|
+
className: "size-4 animate-spin rounded-full border-2 border-current border-t-transparent"
|
|
308
|
+
}
|
|
309
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon, width: "16", height: "16" }) : icon : null,
|
|
310
|
+
children
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
var avatarVariants = cva(
|
|
316
|
+
"inline-flex items-center justify-center overflow-hidden align-middle text-primary-foreground",
|
|
317
|
+
{
|
|
318
|
+
variants: {
|
|
319
|
+
variant: {
|
|
320
|
+
default: "bg-muted-foreground ",
|
|
321
|
+
ghost: "text-white",
|
|
322
|
+
primary: "bg-primary-900 text-primary-foreground",
|
|
323
|
+
info: "bg-info text-info-foreground",
|
|
324
|
+
success: "bg-success text-success-foreground",
|
|
325
|
+
warning: "bg-warning text-warning-foreground",
|
|
326
|
+
danger: "bg-danger text-danger-foreground"
|
|
327
|
+
},
|
|
328
|
+
shape: {
|
|
329
|
+
rounded: "rounded-full",
|
|
330
|
+
square: "rounded-md"
|
|
331
|
+
},
|
|
332
|
+
size: {
|
|
333
|
+
sm: "h-8 w-8",
|
|
334
|
+
md: "h-10 w-10",
|
|
335
|
+
lg: "h-12 w-12"
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
defaultVariants: {
|
|
339
|
+
shape: "rounded",
|
|
340
|
+
size: "md",
|
|
341
|
+
variant: "default"
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
function Avatar({
|
|
346
|
+
alt,
|
|
347
|
+
icon,
|
|
348
|
+
shape,
|
|
349
|
+
size,
|
|
350
|
+
variant,
|
|
351
|
+
className,
|
|
352
|
+
url,
|
|
353
|
+
...props
|
|
354
|
+
}) {
|
|
355
|
+
return /* @__PURE__ */ jsx("div", { className: cn(avatarVariants({ shape, size, variant }), className), ...props, children: icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon, className: "w-[80%] h-[80%]" }) : icon : url ? /* @__PURE__ */ jsx("img", { src: url, alt, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: alt ? alt.charAt(0).toUpperCase() : "?" }) });
|
|
356
|
+
}
|
|
357
|
+
function PlayBoard({
|
|
358
|
+
icon,
|
|
359
|
+
heading,
|
|
360
|
+
description,
|
|
361
|
+
actions,
|
|
362
|
+
code,
|
|
363
|
+
defaultExpanded = false,
|
|
364
|
+
previewClassName,
|
|
365
|
+
className,
|
|
366
|
+
children,
|
|
367
|
+
...props
|
|
368
|
+
}) {
|
|
369
|
+
const [expanded, setExpanded] = React2.useState(defaultExpanded);
|
|
370
|
+
const codeId = React2.useId();
|
|
371
|
+
const hasCode = typeof code === "string" && code.trim().length > 0;
|
|
372
|
+
return /* @__PURE__ */ jsxs(
|
|
373
|
+
"section",
|
|
374
|
+
{
|
|
375
|
+
className: cn(
|
|
376
|
+
"overflow-hidden rounded-xl border border-zinc-200/70 bg-white shadow-sm dark:border-zinc-800 dark:bg-zinc-950",
|
|
377
|
+
className
|
|
378
|
+
),
|
|
379
|
+
...props,
|
|
380
|
+
children: [
|
|
381
|
+
(heading || description || actions) && /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-zinc-200/70 px-5 py-4 dark:border-zinc-800", children: [
|
|
382
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
383
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
384
|
+
icon && /* @__PURE__ */ jsx("div", { className: "flex size-9 items-center justify-center rounded-full bg-zinc-100 text-zinc-600 dark:bg-zinc-900 dark:text-zinc-300", children: icon }),
|
|
385
|
+
heading && /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-zinc-900 dark:text-zinc-50", children: heading })
|
|
386
|
+
] }),
|
|
387
|
+
description && /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: description })
|
|
388
|
+
] }),
|
|
389
|
+
actions && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: actions })
|
|
390
|
+
] }),
|
|
391
|
+
/* @__PURE__ */ jsx("div", { className: cn("px-5 py-6", previewClassName), children: /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-zinc-50 p-5 dark:bg-zinc-900/40", children }) }),
|
|
392
|
+
hasCode && /* @__PURE__ */ jsxs("div", { className: "border-t border-zinc-200/70 dark:border-zinc-800", children: [
|
|
393
|
+
/* @__PURE__ */ jsx(
|
|
394
|
+
"button",
|
|
395
|
+
{
|
|
396
|
+
type: "button",
|
|
397
|
+
className: "flex w-full items-center justify-center gap-2 px-5 py-3 text-sm text-zinc-600 hover:bg-zinc-50 dark:text-zinc-300 dark:hover:bg-zinc-900/30",
|
|
398
|
+
"aria-expanded": expanded,
|
|
399
|
+
"aria-controls": codeId,
|
|
400
|
+
onClick: () => setExpanded((v) => !v),
|
|
401
|
+
children: expanded ? "\u6536\u8D77\u4EE3\u7801" : "\u5C55\u5F00\u4EE3\u7801"
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
expanded && /* @__PURE__ */ jsx(
|
|
405
|
+
"pre",
|
|
406
|
+
{
|
|
407
|
+
id: codeId,
|
|
408
|
+
className: "overflow-x-auto border-t border-zinc-200/70 bg-zinc-950 px-5 py-4 text-xs leading-relaxed text-zinc-50 dark:border-zinc-800",
|
|
409
|
+
children: /* @__PURE__ */ jsx("code", { children: code })
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
] })
|
|
413
|
+
]
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
var playBoard_default = PlayBoard;
|
|
418
|
+
function getLongestCommonPrefix(strings) {
|
|
419
|
+
if (!strings.length) return "";
|
|
420
|
+
return strings.reduce((prefix, current) => {
|
|
421
|
+
let index = 0;
|
|
422
|
+
while (index < prefix.length && index < current.length && prefix[index] === current[index]) {
|
|
423
|
+
index += 1;
|
|
424
|
+
}
|
|
425
|
+
return prefix.slice(0, index);
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function isNumber(value) {
|
|
429
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
430
|
+
}
|
|
431
|
+
function getUid() {
|
|
432
|
+
return Math.random().toString(16).slice(2);
|
|
433
|
+
}
|
|
434
|
+
function requestFrame(callback) {
|
|
435
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
436
|
+
return requestAnimationFrame(callback);
|
|
437
|
+
}
|
|
438
|
+
return setTimeout(() => callback(Date.now()), 16);
|
|
439
|
+
}
|
|
440
|
+
function cancelFrame(id) {
|
|
441
|
+
if (typeof cancelAnimationFrame !== "undefined") {
|
|
442
|
+
cancelAnimationFrame(id);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
clearTimeout(id);
|
|
446
|
+
}
|
|
447
|
+
function useTyping({
|
|
448
|
+
streaming,
|
|
449
|
+
content,
|
|
450
|
+
typing,
|
|
451
|
+
onTyping,
|
|
452
|
+
onTypingComplete
|
|
453
|
+
}) {
|
|
454
|
+
const [chunks, setChunks] = React2.useState([]);
|
|
455
|
+
const rafId = React2.useRef(-1);
|
|
456
|
+
const currentTaskId = React2.useRef(1);
|
|
457
|
+
const animatingRef = React2.useRef(false);
|
|
458
|
+
const renderedRef = React2.useRef("");
|
|
459
|
+
const streamingRef = React2.useRef(streaming);
|
|
460
|
+
streamingRef.current = streaming;
|
|
461
|
+
const mergedConfig = React2.useMemo(() => {
|
|
462
|
+
var _a, _b, _c, _d;
|
|
463
|
+
const base = {
|
|
464
|
+
effect: "fade-in",
|
|
465
|
+
interval: 100,
|
|
466
|
+
step: 6,
|
|
467
|
+
keepPrefix: true
|
|
468
|
+
};
|
|
469
|
+
if (typing === false) return base;
|
|
470
|
+
if (typing === true) return base;
|
|
471
|
+
const interval = (_a = typing.interval) != null ? _a : base.interval;
|
|
472
|
+
const step = (_b = typing.step) != null ? _b : base.step;
|
|
473
|
+
const effect = (_c = typing.effect) != null ? _c : base.effect;
|
|
474
|
+
const keepPrefix = (_d = typing.keepPrefix) != null ? _d : base.keepPrefix;
|
|
475
|
+
if (!isNumber(interval) || interval <= 0) {
|
|
476
|
+
throw new Error("[Bubble] invalid typing.interval, expect positive number.");
|
|
477
|
+
}
|
|
478
|
+
const stepIsNumber = isNumber(step);
|
|
479
|
+
if (!stepIsNumber && !Array.isArray(step)) {
|
|
480
|
+
throw new Error("[Bubble] invalid typing.step, expect number or [min,max].");
|
|
481
|
+
}
|
|
482
|
+
if (stepIsNumber && step <= 0) {
|
|
483
|
+
throw new Error("[Bubble] invalid typing.step, expect positive number.");
|
|
484
|
+
}
|
|
485
|
+
if (Array.isArray(step)) {
|
|
486
|
+
if (!isNumber(step[0]) || step[0] <= 0) {
|
|
487
|
+
throw new Error("[Bubble] invalid typing.step[0], expect positive number.");
|
|
488
|
+
}
|
|
489
|
+
if (!isNumber(step[1]) || step[1] <= 0) {
|
|
490
|
+
throw new Error("[Bubble] invalid typing.step[1], expect positive number.");
|
|
491
|
+
}
|
|
492
|
+
if (step[0] > step[1]) {
|
|
493
|
+
throw new Error("[Bubble] invalid typing.step, step[0] should <= step[1].");
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return { effect, interval, step, keepPrefix };
|
|
497
|
+
}, [typing]);
|
|
498
|
+
const typingSourceRef = React2.useRef({
|
|
499
|
+
content,
|
|
500
|
+
interval: mergedConfig.interval,
|
|
501
|
+
step: mergedConfig.step
|
|
502
|
+
});
|
|
503
|
+
typingSourceRef.current = {
|
|
504
|
+
content,
|
|
505
|
+
interval: mergedConfig.interval,
|
|
506
|
+
step: mergedConfig.step
|
|
507
|
+
};
|
|
508
|
+
const reset = React2.useCallback(() => {
|
|
509
|
+
cancelFrame(rafId.current);
|
|
510
|
+
rafId.current = -1;
|
|
511
|
+
setChunks([]);
|
|
512
|
+
renderedRef.current = "";
|
|
513
|
+
animatingRef.current = false;
|
|
514
|
+
}, []);
|
|
515
|
+
const execute = React2.useCallback(
|
|
516
|
+
(taskId) => {
|
|
517
|
+
let lastFrameTime = 0;
|
|
518
|
+
renderedRef.current = mergedConfig.keepPrefix ? getLongestCommonPrefix([typingSourceRef.current.content, renderedRef.current]) : "";
|
|
519
|
+
setChunks(
|
|
520
|
+
renderedRef.current ? [{ text: renderedRef.current, id: getUid(), taskId, done: true }] : []
|
|
521
|
+
);
|
|
522
|
+
const tick = () => {
|
|
523
|
+
if (taskId !== currentTaskId.current) return;
|
|
524
|
+
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
525
|
+
const { content: fullContent, interval, step } = typingSourceRef.current;
|
|
526
|
+
if (now - lastFrameTime < interval) {
|
|
527
|
+
rafId.current = requestFrame(tick);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const currentLen = renderedRef.current.length;
|
|
531
|
+
const resolvedStep = isNumber(step) ? step : Math.floor(Math.random() * (step[1] - step[0] + 1)) + step[0];
|
|
532
|
+
const nextText = fullContent.slice(currentLen, currentLen + resolvedStep);
|
|
533
|
+
if (!nextText) {
|
|
534
|
+
if (streamingRef.current) {
|
|
535
|
+
rafId.current = requestFrame(tick);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
setChunks([
|
|
539
|
+
{
|
|
540
|
+
text: renderedRef.current,
|
|
541
|
+
id: getUid(),
|
|
542
|
+
taskId,
|
|
543
|
+
done: true
|
|
544
|
+
}
|
|
545
|
+
]);
|
|
546
|
+
onTypingComplete == null ? void 0 : onTypingComplete(fullContent);
|
|
547
|
+
animatingRef.current = false;
|
|
548
|
+
currentTaskId.current += 1;
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
renderedRef.current += nextText;
|
|
552
|
+
setChunks(
|
|
553
|
+
(prev) => (
|
|
554
|
+
//把这段 nextText 作为一个新 chunk 追加到数组尾部(触发 UI 更新)。
|
|
555
|
+
prev.concat({
|
|
556
|
+
id: getUid(),
|
|
557
|
+
text: nextText,
|
|
558
|
+
taskId,
|
|
559
|
+
done: false
|
|
560
|
+
})
|
|
561
|
+
)
|
|
562
|
+
);
|
|
563
|
+
animatingRef.current = true;
|
|
564
|
+
lastFrameTime = now;
|
|
565
|
+
rafId.current = requestFrame(tick);
|
|
566
|
+
onTyping == null ? void 0 : onTyping(renderedRef.current, fullContent);
|
|
567
|
+
};
|
|
568
|
+
tick();
|
|
569
|
+
},
|
|
570
|
+
[mergedConfig.keepPrefix, onTyping, onTypingComplete]
|
|
571
|
+
);
|
|
572
|
+
React2.useEffect(() => {
|
|
573
|
+
if (!content) return reset();
|
|
574
|
+
if (content === renderedRef.current) return;
|
|
575
|
+
if (animatingRef.current && !content.startsWith(renderedRef.current)) {
|
|
576
|
+
cancelFrame(rafId.current);
|
|
577
|
+
animatingRef.current = false;
|
|
578
|
+
rafId.current = requestFrame(() => execute(currentTaskId.current += 1));
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (!animatingRef.current) {
|
|
582
|
+
execute(currentTaskId.current);
|
|
583
|
+
}
|
|
584
|
+
}, [content, execute, reset]);
|
|
585
|
+
React2.useEffect(() => () => cancelFrame(rafId.current), []);
|
|
586
|
+
return {
|
|
587
|
+
chunks,
|
|
588
|
+
animating: animatingRef.current,
|
|
589
|
+
config: mergedConfig,
|
|
590
|
+
renderedText: renderedRef.current
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
function TypingContent({
|
|
594
|
+
content,
|
|
595
|
+
typing,
|
|
596
|
+
streaming,
|
|
597
|
+
className,
|
|
598
|
+
cursor = "|",
|
|
599
|
+
onTyping,
|
|
600
|
+
onTypingComplete
|
|
601
|
+
}) {
|
|
602
|
+
const { chunks, animating, config } = useTyping({
|
|
603
|
+
streaming,
|
|
604
|
+
content,
|
|
605
|
+
typing,
|
|
606
|
+
onTyping,
|
|
607
|
+
onTypingComplete
|
|
608
|
+
});
|
|
609
|
+
const effect = config.effect;
|
|
610
|
+
const isTypingEffect = typing !== true && effect === "typing";
|
|
611
|
+
return /* @__PURE__ */ jsxs(
|
|
612
|
+
"div",
|
|
613
|
+
{
|
|
614
|
+
className: cn(
|
|
615
|
+
"whitespace-pre-wrap break-words",
|
|
616
|
+
className
|
|
617
|
+
),
|
|
618
|
+
children: [
|
|
619
|
+
chunks.map(
|
|
620
|
+
(chunk) => config.effect === "fade-in" && !chunk.done ? /* @__PURE__ */ jsx(
|
|
621
|
+
"span",
|
|
622
|
+
{
|
|
623
|
+
className: "inline animate-[haiku-bubble-fade-in_0.1s_linear]",
|
|
624
|
+
children: chunk.text
|
|
625
|
+
},
|
|
626
|
+
chunk.id
|
|
627
|
+
) : /* @__PURE__ */ jsx(React2.Fragment, { children: chunk.text }, chunk.id)
|
|
628
|
+
),
|
|
629
|
+
isTypingEffect && animating ? /* @__PURE__ */ jsx(
|
|
630
|
+
"span",
|
|
631
|
+
{
|
|
632
|
+
"aria-hidden": "true",
|
|
633
|
+
className: cn("ml-1 select-none font-black", "animate-[haiku-bubble-cursor-blink_0.8s_linear_infinite]"),
|
|
634
|
+
children: cursor
|
|
635
|
+
}
|
|
636
|
+
) : null
|
|
637
|
+
]
|
|
638
|
+
}
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
var bubbleRootVariants = cva("flex gap-3", {
|
|
642
|
+
variants: {
|
|
643
|
+
placement: {
|
|
644
|
+
start: "flex-row",
|
|
645
|
+
end: "flex-row-reverse"
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
defaultVariants: {
|
|
649
|
+
placement: "start"
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
var bubbleContentVariants = cva(
|
|
653
|
+
"max-w-full min-w-0 px-4 py-3 text-sm leading-6 text-default break-words",
|
|
654
|
+
{
|
|
655
|
+
variants: {
|
|
656
|
+
variant: {
|
|
657
|
+
filled: "bg-zinc-100 text-zinc-900 dark:bg-zinc-900/60 dark:text-zinc-50",
|
|
658
|
+
outlined: "border border-zinc-200/70 bg-white text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
|
|
659
|
+
shadow: "bg-white text-zinc-900 shadow-sm dark:bg-zinc-950 dark:text-zinc-50",
|
|
660
|
+
borderless: "bg-transparent p-0"
|
|
661
|
+
},
|
|
662
|
+
shape: {
|
|
663
|
+
default: "rounded-xl",
|
|
664
|
+
round: "rounded-full",
|
|
665
|
+
corner: "rounded-xl"
|
|
666
|
+
},
|
|
667
|
+
placement: {
|
|
668
|
+
start: "",
|
|
669
|
+
end: ""
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
compoundVariants: [
|
|
673
|
+
{ shape: "corner", placement: "start", className: "rounded-tl-sm" },
|
|
674
|
+
{ shape: "corner", placement: "end", className: "rounded-tr-sm" }
|
|
675
|
+
],
|
|
676
|
+
defaultVariants: {
|
|
677
|
+
variant: "filled",
|
|
678
|
+
shape: "default",
|
|
679
|
+
placement: "start"
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
);
|
|
683
|
+
function renderSlot(slot, content, info) {
|
|
684
|
+
if (!slot) return null;
|
|
685
|
+
return typeof slot === "function" ? slot(content, info) : slot;
|
|
686
|
+
}
|
|
687
|
+
function DefaultLoading() {
|
|
688
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
689
|
+
/* @__PURE__ */ jsx("span", { className: "size-1.5 animate-bounce rounded-full bg-zinc-400 [animation-delay:-0.2s] dark:bg-zinc-500" }),
|
|
690
|
+
/* @__PURE__ */ jsx("span", { className: "size-1.5 animate-bounce rounded-full bg-zinc-400 [animation-delay:-0.1s] dark:bg-zinc-500" }),
|
|
691
|
+
/* @__PURE__ */ jsx("span", { className: "size-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500" })
|
|
692
|
+
] });
|
|
693
|
+
}
|
|
694
|
+
function Bubble({
|
|
695
|
+
content,
|
|
696
|
+
info,
|
|
697
|
+
placement = "start",
|
|
698
|
+
variant = "filled",
|
|
699
|
+
shape = "default",
|
|
700
|
+
header,
|
|
701
|
+
footer,
|
|
702
|
+
avatar,
|
|
703
|
+
extra,
|
|
704
|
+
contentRender,
|
|
705
|
+
typing,
|
|
706
|
+
typingCursor,
|
|
707
|
+
streaming = false,
|
|
708
|
+
loading = false,
|
|
709
|
+
loadingRender,
|
|
710
|
+
onTyping,
|
|
711
|
+
onTypingComplete,
|
|
712
|
+
className,
|
|
713
|
+
...props
|
|
714
|
+
}) {
|
|
715
|
+
const mergedInfo = info != null ? info : {};
|
|
716
|
+
const rendered = contentRender ? contentRender(content, mergedInfo) : content;
|
|
717
|
+
const mergedTyping = typeof typing === "function" ? typing(content, mergedInfo) : typing;
|
|
718
|
+
const useInnerAnimation = Boolean(mergedTyping) && typeof rendered === "string";
|
|
719
|
+
React2.useEffect(() => {
|
|
720
|
+
if (useInnerAnimation) return;
|
|
721
|
+
if (streaming) return;
|
|
722
|
+
if (typeof rendered === "string" && rendered) {
|
|
723
|
+
onTypingComplete == null ? void 0 : onTypingComplete(rendered);
|
|
724
|
+
}
|
|
725
|
+
}, [rendered, streaming, useInnerAnimation, onTypingComplete]);
|
|
726
|
+
const contentNode = (() => {
|
|
727
|
+
if (loading) {
|
|
728
|
+
return loadingRender ? loadingRender() : /* @__PURE__ */ jsx(DefaultLoading, {});
|
|
729
|
+
}
|
|
730
|
+
if (useInnerAnimation) {
|
|
731
|
+
return /* @__PURE__ */ jsx(
|
|
732
|
+
TypingContent,
|
|
733
|
+
{
|
|
734
|
+
content: rendered,
|
|
735
|
+
streaming,
|
|
736
|
+
typing: mergedTyping,
|
|
737
|
+
cursor: typingCursor,
|
|
738
|
+
onTyping,
|
|
739
|
+
onTypingComplete
|
|
740
|
+
}
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
return typeof rendered === "string" ? /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap break-words", children: rendered }) : rendered;
|
|
744
|
+
})();
|
|
745
|
+
const avatarNode = renderSlot(avatar, content, mergedInfo);
|
|
746
|
+
const headerNode = renderSlot(header, content, mergedInfo);
|
|
747
|
+
const footerNode = renderSlot(footer, content, mergedInfo);
|
|
748
|
+
const extraNode = renderSlot(extra, content, mergedInfo);
|
|
749
|
+
return /* @__PURE__ */ jsxs(
|
|
750
|
+
"div",
|
|
751
|
+
{
|
|
752
|
+
className: cn(bubbleRootVariants({ placement }), className),
|
|
753
|
+
"data-slot": "bubble",
|
|
754
|
+
"data-placement": placement,
|
|
755
|
+
"data-variant": variant,
|
|
756
|
+
"data-shape": shape,
|
|
757
|
+
"data-loading": loading ? "" : void 0,
|
|
758
|
+
...props,
|
|
759
|
+
children: [
|
|
760
|
+
avatarNode ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: avatarNode }) : null,
|
|
761
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 max-w-full flex-col", children: [
|
|
762
|
+
headerNode ? /* @__PURE__ */ jsx("div", { className: cn("mb-1 text-xs text-zinc-500 dark:text-zinc-400"), children: headerNode }) : null,
|
|
763
|
+
/* @__PURE__ */ jsx(
|
|
764
|
+
"div",
|
|
765
|
+
{
|
|
766
|
+
className: cn(
|
|
767
|
+
bubbleContentVariants({ variant, shape, placement }),
|
|
768
|
+
typeof rendered === "string" && "whitespace-pre-wrap"
|
|
769
|
+
),
|
|
770
|
+
children: contentNode
|
|
771
|
+
}
|
|
772
|
+
),
|
|
773
|
+
footerNode ? /* @__PURE__ */ jsx(
|
|
774
|
+
"div",
|
|
775
|
+
{
|
|
776
|
+
className: cn(
|
|
777
|
+
"mt-2 text-xs text-zinc-500 dark:text-zinc-400",
|
|
778
|
+
placement === "end" && "text-right"
|
|
779
|
+
),
|
|
780
|
+
children: footerNode
|
|
781
|
+
}
|
|
782
|
+
) : null
|
|
783
|
+
] }),
|
|
784
|
+
extraNode && !loading ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: extraNode }) : null
|
|
785
|
+
]
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// src/components/Upload/utils.ts
|
|
791
|
+
var uploadFile = ({
|
|
792
|
+
action,
|
|
793
|
+
file,
|
|
794
|
+
method = "post",
|
|
795
|
+
header,
|
|
796
|
+
onProgress,
|
|
797
|
+
signal,
|
|
798
|
+
formData: providedFormData
|
|
799
|
+
}) => new Promise((resolve, reject) => {
|
|
800
|
+
const xhr = new XMLHttpRequest();
|
|
801
|
+
xhr.open(method.toUpperCase(), action, true);
|
|
802
|
+
const handleAbort = () => {
|
|
803
|
+
xhr.abort();
|
|
804
|
+
};
|
|
805
|
+
if (header) {
|
|
806
|
+
Object.entries(header).forEach(([key, value]) => {
|
|
807
|
+
xhr.setRequestHeader(key, value);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
xhr.upload.onprogress = (event) => {
|
|
811
|
+
onProgress == null ? void 0 : onProgress(event);
|
|
812
|
+
};
|
|
813
|
+
xhr.onload = () => {
|
|
814
|
+
signal == null ? void 0 : signal.removeEventListener("abort", handleAbort);
|
|
815
|
+
resolve(xhr.responseText);
|
|
816
|
+
};
|
|
817
|
+
xhr.onerror = (event) => {
|
|
818
|
+
signal == null ? void 0 : signal.removeEventListener("abort", handleAbort);
|
|
819
|
+
reject(event);
|
|
820
|
+
};
|
|
821
|
+
xhr.onabort = () => {
|
|
822
|
+
signal == null ? void 0 : signal.removeEventListener("abort", handleAbort);
|
|
823
|
+
reject(new DOMException("Upload aborted", "AbortError"));
|
|
824
|
+
};
|
|
825
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
826
|
+
reject(new DOMException("Upload aborted", "AbortError"));
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
signal == null ? void 0 : signal.addEventListener("abort", handleAbort);
|
|
830
|
+
const formData = providedFormData || new FormData();
|
|
831
|
+
if (!providedFormData && file) {
|
|
832
|
+
formData.append("file", file);
|
|
833
|
+
}
|
|
834
|
+
xhr.send(formData);
|
|
835
|
+
});
|
|
836
|
+
var uploadTriggerVariants = cva(
|
|
837
|
+
"flex cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-300 focus-visible:ring-offset-2 focus-visible:ring-offset-white",
|
|
838
|
+
{
|
|
839
|
+
variants: {
|
|
840
|
+
type: {
|
|
841
|
+
button: "",
|
|
842
|
+
dragger: "flex-col border-2 border-dashed text-center",
|
|
843
|
+
image: "relative overflow-hidden p-0"
|
|
844
|
+
},
|
|
845
|
+
variant: {
|
|
846
|
+
default: "border border-default bg-white text-default hover:bg-default-foreground/10",
|
|
847
|
+
dashed: "border-2 border-dashed border-default bg-white text-default hover:bg-default-foreground/10"
|
|
848
|
+
},
|
|
849
|
+
size: {
|
|
850
|
+
sm: "",
|
|
851
|
+
md: "",
|
|
852
|
+
lg: ""
|
|
853
|
+
},
|
|
854
|
+
shape: {
|
|
855
|
+
rounded: "rounded-md",
|
|
856
|
+
square: "rounded-none"
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
compoundVariants: [
|
|
860
|
+
{ type: "button", size: "sm", className: "h-8 px-3 text-xs" },
|
|
861
|
+
{ type: "button", size: "md", className: "h-9 px-4 text-sm" },
|
|
862
|
+
{ type: "button", size: "lg", className: "h-10 px-6 text-sm" },
|
|
863
|
+
{ type: "dragger", size: "sm", className: "min-h-[96px] px-4 py-6 text-xs" },
|
|
864
|
+
{ type: "dragger", size: "md", className: "min-h-[120px] px-6 py-8 text-sm" },
|
|
865
|
+
{ type: "dragger", size: "lg", className: "min-h-[152px] px-8 py-10 text-sm" },
|
|
866
|
+
{ type: "image", size: "sm", className: "h-15 w-15" },
|
|
867
|
+
{ type: "image", size: "md", className: "h-23 w-23" },
|
|
868
|
+
{ type: "image", size: "lg", className: "h-35 w-35" },
|
|
869
|
+
{ type: "image", shape: "rounded", className: "rounded-full" }
|
|
870
|
+
],
|
|
871
|
+
defaultVariants: {
|
|
872
|
+
type: "button",
|
|
873
|
+
variant: "default",
|
|
874
|
+
size: "md",
|
|
875
|
+
shape: "rounded"
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
);
|
|
879
|
+
function UploadTrigger({
|
|
880
|
+
inputId,
|
|
881
|
+
type = "button",
|
|
882
|
+
variant,
|
|
883
|
+
size,
|
|
884
|
+
shape,
|
|
885
|
+
className,
|
|
886
|
+
isDragging,
|
|
887
|
+
children,
|
|
888
|
+
...props
|
|
889
|
+
}) {
|
|
890
|
+
return /* @__PURE__ */ jsx(
|
|
891
|
+
"label",
|
|
892
|
+
{
|
|
893
|
+
htmlFor: inputId,
|
|
894
|
+
className: cn(
|
|
895
|
+
uploadTriggerVariants({ type, variant, size, shape }),
|
|
896
|
+
isDragging && "border-primary-300 text-primary-900",
|
|
897
|
+
className
|
|
898
|
+
),
|
|
899
|
+
...props,
|
|
900
|
+
children
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function ButtonUpload({
|
|
905
|
+
inputId,
|
|
906
|
+
variant,
|
|
907
|
+
size,
|
|
908
|
+
shape,
|
|
909
|
+
className,
|
|
910
|
+
status,
|
|
911
|
+
progress,
|
|
912
|
+
onCancel,
|
|
913
|
+
onReset
|
|
914
|
+
}) {
|
|
915
|
+
const isUploading = status === "uploading";
|
|
916
|
+
const widthClass = isUploading ? "w-[154px]" : "w-[130px]";
|
|
917
|
+
const mergedClassName = cn(
|
|
918
|
+
widthClass,
|
|
919
|
+
"justify-center transition-[width] duration-200 ease-out",
|
|
920
|
+
isUploading && "relative overflow-hidden",
|
|
921
|
+
className
|
|
922
|
+
);
|
|
923
|
+
const labelContent = status === "uploading" ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 tabular-nums", children: [
|
|
924
|
+
/* @__PURE__ */ jsx("span", { children: "\u4E0A\u4F20\u4E2D" }),
|
|
925
|
+
/* @__PURE__ */ jsx("span", { className: "w-[3ch] text-right", children: progress }),
|
|
926
|
+
"%"
|
|
927
|
+
] }) : status === "success" ? "\u4E0A\u4F20\u5B8C\u6210" : status === "error" ? "\u4E0A\u4F20\u5931\u8D25" : "\u70B9\u51FB\u4E0A\u4F20\u6587\u4EF6";
|
|
928
|
+
const reloadButton = status === "success" && onReset ? /* @__PURE__ */ jsx(
|
|
929
|
+
"button",
|
|
930
|
+
{
|
|
931
|
+
type: "button",
|
|
932
|
+
"aria-label": "Reset upload",
|
|
933
|
+
className: "ml-1 inline-flex items-center",
|
|
934
|
+
onClick: (event) => {
|
|
935
|
+
event.preventDefault();
|
|
936
|
+
event.stopPropagation();
|
|
937
|
+
onReset();
|
|
938
|
+
},
|
|
939
|
+
children: /* @__PURE__ */ jsx(
|
|
940
|
+
Icon,
|
|
941
|
+
{
|
|
942
|
+
icon: "bx:reset",
|
|
943
|
+
width: "18",
|
|
944
|
+
height: "18",
|
|
945
|
+
className: "cursor-pointer"
|
|
946
|
+
}
|
|
947
|
+
)
|
|
948
|
+
}
|
|
949
|
+
) : null;
|
|
950
|
+
const cancelButton = status === "uploading" && onCancel ? /* @__PURE__ */ jsx(
|
|
951
|
+
"button",
|
|
952
|
+
{
|
|
953
|
+
type: "button",
|
|
954
|
+
"aria-label": "Cancel upload",
|
|
955
|
+
className: "ml-1 inline-flex items-center",
|
|
956
|
+
onClick: (event) => {
|
|
957
|
+
event.preventDefault();
|
|
958
|
+
event.stopPropagation();
|
|
959
|
+
onCancel();
|
|
960
|
+
},
|
|
961
|
+
children: /* @__PURE__ */ jsx(
|
|
962
|
+
Icon,
|
|
963
|
+
{
|
|
964
|
+
icon: "fluent-emoji-flat:stop-sign",
|
|
965
|
+
width: "18",
|
|
966
|
+
height: "18",
|
|
967
|
+
className: "cursor-pointer"
|
|
968
|
+
}
|
|
969
|
+
)
|
|
970
|
+
}
|
|
971
|
+
) : null;
|
|
972
|
+
const statusIcon = status === "success" ? /* @__PURE__ */ jsx(Icon, { icon: "icon-park:success", width: "16", height: "16" }) : status === "error" ? /* @__PURE__ */ jsx(Icon, { icon: "material-icon-theme:folder-error", width: "16", height: "16" }) : status === "uploading" ? /* @__PURE__ */ jsx(Icon, { icon: "line-md:loading-loop", width: "16", height: "16" }) : /* @__PURE__ */ jsx(Icon, { icon: "line-md:upload", width: "16", height: "16" });
|
|
973
|
+
return /* @__PURE__ */ jsxs(
|
|
974
|
+
UploadTrigger,
|
|
975
|
+
{
|
|
976
|
+
inputId,
|
|
977
|
+
type: "button",
|
|
978
|
+
variant,
|
|
979
|
+
size,
|
|
980
|
+
shape,
|
|
981
|
+
className: mergedClassName,
|
|
982
|
+
children: [
|
|
983
|
+
isUploading && /* @__PURE__ */ jsx(
|
|
984
|
+
"span",
|
|
985
|
+
{
|
|
986
|
+
"aria-hidden": "true",
|
|
987
|
+
className: "pointer-events-none absolute inset-0 opacity-80 haiku-upload-shine"
|
|
988
|
+
}
|
|
989
|
+
),
|
|
990
|
+
/* @__PURE__ */ jsxs("span", { className: "relative z-10 inline-flex items-center gap-2", children: [
|
|
991
|
+
statusIcon,
|
|
992
|
+
labelContent,
|
|
993
|
+
cancelButton,
|
|
994
|
+
reloadButton
|
|
995
|
+
] })
|
|
996
|
+
]
|
|
997
|
+
}
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
function DraggerUpload({
|
|
1001
|
+
inputId,
|
|
1002
|
+
isDragging,
|
|
1003
|
+
onDragOver,
|
|
1004
|
+
onDragLeave,
|
|
1005
|
+
onDrop,
|
|
1006
|
+
variant,
|
|
1007
|
+
size,
|
|
1008
|
+
shape,
|
|
1009
|
+
className,
|
|
1010
|
+
status,
|
|
1011
|
+
progress
|
|
1012
|
+
}) {
|
|
1013
|
+
return /* @__PURE__ */ jsx(
|
|
1014
|
+
UploadTrigger,
|
|
1015
|
+
{
|
|
1016
|
+
inputId,
|
|
1017
|
+
type: "dragger",
|
|
1018
|
+
variant,
|
|
1019
|
+
size,
|
|
1020
|
+
shape,
|
|
1021
|
+
className,
|
|
1022
|
+
isDragging,
|
|
1023
|
+
onDragOver,
|
|
1024
|
+
onDragEnter: onDragOver,
|
|
1025
|
+
onDragLeave,
|
|
1026
|
+
onDrop,
|
|
1027
|
+
children: status === "uploading" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1028
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium inline-flex tabular-nums", children: [
|
|
1029
|
+
/* @__PURE__ */ jsx("span", { children: "\u4E0A\u4F20\u4E2D" }),
|
|
1030
|
+
/* @__PURE__ */ jsxs("span", { className: "w-[4ch] text-right", children: [
|
|
1031
|
+
progress,
|
|
1032
|
+
"%"
|
|
1033
|
+
] })
|
|
1034
|
+
] }),
|
|
1035
|
+
/* @__PURE__ */ jsx("div", { className: "h-1 w-full rounded-full bg-default-foreground/20", children: /* @__PURE__ */ jsx(
|
|
1036
|
+
"div",
|
|
1037
|
+
{
|
|
1038
|
+
className: "h-full rounded-full bg-primary-900",
|
|
1039
|
+
style: { width: `${progress}%` }
|
|
1040
|
+
}
|
|
1041
|
+
) })
|
|
1042
|
+
] }) : status === "error" ? /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "\u4E0A\u4F20\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" }) : status === "success" ? /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "\u4E0A\u4F20\u5B8C\u6210" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1043
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "\u70B9\u51FB\u6216\u62D6\u62FD\u4E0A\u4F20" }),
|
|
1044
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-default opacity-70", children: "\u652F\u6301\u591A\u6587\u4EF6" })
|
|
1045
|
+
] })
|
|
1046
|
+
}
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
function ImageUpload({
|
|
1050
|
+
inputId,
|
|
1051
|
+
previewUrl,
|
|
1052
|
+
variant,
|
|
1053
|
+
size,
|
|
1054
|
+
shape,
|
|
1055
|
+
className,
|
|
1056
|
+
status,
|
|
1057
|
+
progress
|
|
1058
|
+
}) {
|
|
1059
|
+
const isUploading = status === "uploading";
|
|
1060
|
+
return /* @__PURE__ */ jsxs(
|
|
1061
|
+
UploadTrigger,
|
|
1062
|
+
{
|
|
1063
|
+
inputId,
|
|
1064
|
+
type: "image",
|
|
1065
|
+
variant,
|
|
1066
|
+
size,
|
|
1067
|
+
shape,
|
|
1068
|
+
className,
|
|
1069
|
+
children: [
|
|
1070
|
+
previewUrl ? /* @__PURE__ */ jsx("img", { src: previewUrl, alt: "\u4E0A\u4F20\u9884\u89C8", className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "text-xs inline-flex font-medium", children: "\u4E0A\u4F20" }),
|
|
1071
|
+
isUploading && /* @__PURE__ */ jsx(
|
|
1072
|
+
"span",
|
|
1073
|
+
{
|
|
1074
|
+
"aria-hidden": "true",
|
|
1075
|
+
className: "pointer-events-none absolute inset-0 z-30 opacity-70 haiku-upload-shine"
|
|
1076
|
+
}
|
|
1077
|
+
),
|
|
1078
|
+
isUploading && /* @__PURE__ */ jsxs("span", { className: "absolute inset-0 z-20 tabular-nums text-right flex items-center justify-center bg-black/35 text-xs font-medium text-white", children: [
|
|
1079
|
+
progress,
|
|
1080
|
+
"%"
|
|
1081
|
+
] }),
|
|
1082
|
+
status === "error" && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 z-20 flex items-center justify-center bg-black/60 text-xs font-medium text-white", children: "\u4E0A\u4F20\u5931\u8D25" })
|
|
1083
|
+
]
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function FileItem({ file, onRemove, onCancel }) {
|
|
1088
|
+
const { id, file: fileData, status, progress, error } = file;
|
|
1089
|
+
const iconMap = {
|
|
1090
|
+
idle: { icon: "lucide:file", className: "text-gray-400" },
|
|
1091
|
+
uploading: { icon: "lucide:loader-2", className: "text-blue-500 animate-spin" },
|
|
1092
|
+
success: { icon: "lucide:check-circle", className: "text-green-500" },
|
|
1093
|
+
error: { icon: "lucide:x-circle", className: "text-red-500" }
|
|
1094
|
+
};
|
|
1095
|
+
const iconConfig = iconMap[status] || iconMap.idle;
|
|
1096
|
+
const formatSize = (bytes) => {
|
|
1097
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1098
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1099
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
1100
|
+
};
|
|
1101
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-3 border rounded-md bg-white dark:bg-zinc-800", children: [
|
|
1102
|
+
/* @__PURE__ */ jsx(Icon, { icon: iconConfig.icon, className: iconConfig.className, width: 20 }),
|
|
1103
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1104
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1105
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium text-md", children: fileData.name }),
|
|
1106
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500 ml-2", children: formatSize(fileData.size) })
|
|
1107
|
+
] }),
|
|
1108
|
+
(status === "uploading" || status === "success") && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
1109
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-full bg-gray-200 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1110
|
+
"div",
|
|
1111
|
+
{
|
|
1112
|
+
className: "h-full bg-blue-500 transition-all duration-300",
|
|
1113
|
+
style: { width: `${progress}%` }
|
|
1114
|
+
}
|
|
1115
|
+
) }),
|
|
1116
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
|
|
1117
|
+
progress,
|
|
1118
|
+
"%"
|
|
1119
|
+
] })
|
|
1120
|
+
] }),
|
|
1121
|
+
status === "error" && error && /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-red-500", children: error.message })
|
|
1122
|
+
] }),
|
|
1123
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1124
|
+
status === "uploading" && onCancel && /* @__PURE__ */ jsx(
|
|
1125
|
+
"button",
|
|
1126
|
+
{
|
|
1127
|
+
type: "button",
|
|
1128
|
+
onClick: () => onCancel(id),
|
|
1129
|
+
className: "p-1 hover:bg-gray-100 rounded",
|
|
1130
|
+
title: "\u53D6\u6D88",
|
|
1131
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:x", className: "w-4 h-4 text-gray-500" })
|
|
1132
|
+
}
|
|
1133
|
+
),
|
|
1134
|
+
status !== "uploading" && onRemove && /* @__PURE__ */ jsx(
|
|
1135
|
+
"button",
|
|
1136
|
+
{
|
|
1137
|
+
type: "button",
|
|
1138
|
+
onClick: () => onRemove(id),
|
|
1139
|
+
className: "p-1 hover:bg-gray-100 rounded",
|
|
1140
|
+
title: "\u5220\u9664",
|
|
1141
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:trash-2", className: "w-4 h-4 text-gray-500" })
|
|
1142
|
+
}
|
|
1143
|
+
)
|
|
1144
|
+
] })
|
|
1145
|
+
] });
|
|
1146
|
+
}
|
|
1147
|
+
function FileList({ files, onRemove, onCancel, show = true }) {
|
|
1148
|
+
if (!show || files.length === 0) {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-2 mt-4", children: files.map((file) => /* @__PURE__ */ jsx(
|
|
1152
|
+
FileItem,
|
|
1153
|
+
{
|
|
1154
|
+
file,
|
|
1155
|
+
onRemove,
|
|
1156
|
+
onCancel
|
|
1157
|
+
},
|
|
1158
|
+
file.id
|
|
1159
|
+
)) });
|
|
1160
|
+
}
|
|
1161
|
+
var DEFAULT_OPTIONS = {
|
|
1162
|
+
mode: "button",
|
|
1163
|
+
autoUpload: true,
|
|
1164
|
+
showFileList: true,
|
|
1165
|
+
variant: "default",
|
|
1166
|
+
size: "md",
|
|
1167
|
+
shape: "rounded",
|
|
1168
|
+
method: "post",
|
|
1169
|
+
name: "file"
|
|
1170
|
+
};
|
|
1171
|
+
var createUploadError = (type, message, file, originalError) => ({
|
|
1172
|
+
type,
|
|
1173
|
+
message,
|
|
1174
|
+
file,
|
|
1175
|
+
originalError
|
|
1176
|
+
});
|
|
1177
|
+
var generateFileId = () => Math.random().toString(36).substring(2, 11);
|
|
1178
|
+
function Upload(config) {
|
|
1179
|
+
const options = {
|
|
1180
|
+
...DEFAULT_OPTIONS,
|
|
1181
|
+
...config
|
|
1182
|
+
};
|
|
1183
|
+
const {
|
|
1184
|
+
mode,
|
|
1185
|
+
autoUpload,
|
|
1186
|
+
showFileList,
|
|
1187
|
+
directory,
|
|
1188
|
+
accept,
|
|
1189
|
+
maxSize,
|
|
1190
|
+
minSize,
|
|
1191
|
+
maxCount,
|
|
1192
|
+
variant,
|
|
1193
|
+
size,
|
|
1194
|
+
shape,
|
|
1195
|
+
action,
|
|
1196
|
+
method,
|
|
1197
|
+
headers,
|
|
1198
|
+
data,
|
|
1199
|
+
name,
|
|
1200
|
+
onChange,
|
|
1201
|
+
onProgress,
|
|
1202
|
+
onSuccess,
|
|
1203
|
+
onError,
|
|
1204
|
+
onComplete
|
|
1205
|
+
} = options;
|
|
1206
|
+
const [files, setFiles] = useState([]);
|
|
1207
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
1208
|
+
const [previewUrl, setPreviewUrl] = useState(null);
|
|
1209
|
+
const [status, setStatus] = useState("idle");
|
|
1210
|
+
const [progress, setProgress] = useState(0);
|
|
1211
|
+
const inputId = useId();
|
|
1212
|
+
const abortRef = useRef(null);
|
|
1213
|
+
const cancelRef = useRef(false);
|
|
1214
|
+
const inputRef = useRef(null);
|
|
1215
|
+
const isDisabled = status === "uploading";
|
|
1216
|
+
const notifyChange = useCallback((newFiles) => {
|
|
1217
|
+
onChange == null ? void 0 : onChange(newFiles);
|
|
1218
|
+
}, [onChange]);
|
|
1219
|
+
const validateFile = useCallback((file) => {
|
|
1220
|
+
if (maxSize && file.size > maxSize) {
|
|
1221
|
+
return createUploadError(
|
|
1222
|
+
"FILE_SIZE_EXCEEDED",
|
|
1223
|
+
`\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC7 ${Math.round(maxSize / 1024 / 1024)}MB`,
|
|
1224
|
+
file
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
if (minSize && file.size < minSize) {
|
|
1228
|
+
return createUploadError(
|
|
1229
|
+
"FILE_SIZE_EXCEEDED",
|
|
1230
|
+
`\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u5C0F\u4E8E ${Math.round(minSize / 1024)}KB`,
|
|
1231
|
+
file
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
return null;
|
|
1235
|
+
}, [maxSize, minSize]);
|
|
1236
|
+
const validateFiles = useCallback((newFiles) => {
|
|
1237
|
+
const valid = [];
|
|
1238
|
+
const errors = [];
|
|
1239
|
+
if (maxCount && newFiles.length > maxCount) {
|
|
1240
|
+
const error = createUploadError(
|
|
1241
|
+
"FILE_COUNT_EXCEEDED",
|
|
1242
|
+
`\u6700\u591A\u53EA\u80FD\u4E0A\u4F20 ${maxCount} \u4E2A\u6587\u4EF6`
|
|
1243
|
+
);
|
|
1244
|
+
errors.push(error);
|
|
1245
|
+
}
|
|
1246
|
+
for (const file of newFiles) {
|
|
1247
|
+
const error = validateFile(file);
|
|
1248
|
+
if (error) {
|
|
1249
|
+
errors.push(error);
|
|
1250
|
+
} else {
|
|
1251
|
+
valid.push(file);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return { valid, errors };
|
|
1255
|
+
}, [maxCount, validateFile]);
|
|
1256
|
+
const uploadSingleFile = async (fileItem, totalBytes, uploadedBytes) => {
|
|
1257
|
+
const { file } = fileItem;
|
|
1258
|
+
const controller = new AbortController();
|
|
1259
|
+
abortRef.current = controller;
|
|
1260
|
+
try {
|
|
1261
|
+
const formData = new FormData();
|
|
1262
|
+
formData.append(name, file);
|
|
1263
|
+
if (data) {
|
|
1264
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
1265
|
+
formData.append(key, String(value));
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
const responseText = await uploadFile({
|
|
1269
|
+
action,
|
|
1270
|
+
method,
|
|
1271
|
+
header: headers,
|
|
1272
|
+
signal: controller.signal,
|
|
1273
|
+
formData,
|
|
1274
|
+
onProgress: (event) => {
|
|
1275
|
+
if (!event.lengthComputable || file.size === 0) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const percent = Math.min(
|
|
1279
|
+
100,
|
|
1280
|
+
Math.round(event.loaded / file.size * 100)
|
|
1281
|
+
);
|
|
1282
|
+
const currentTotalBytes = uploadedBytes + event.loaded;
|
|
1283
|
+
setProgress(
|
|
1284
|
+
Math.round(currentTotalBytes / totalBytes * 100)
|
|
1285
|
+
);
|
|
1286
|
+
onProgress == null ? void 0 : onProgress(percent, file);
|
|
1287
|
+
setFiles(
|
|
1288
|
+
(prev) => prev.map(
|
|
1289
|
+
(f) => f.id === fileItem.id ? { ...f, progress: percent } : f
|
|
1290
|
+
)
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
const updatedFile = {
|
|
1295
|
+
...fileItem,
|
|
1296
|
+
status: "success",
|
|
1297
|
+
progress: 100,
|
|
1298
|
+
response: responseText
|
|
1299
|
+
};
|
|
1300
|
+
onSuccess == null ? void 0 : onSuccess(responseText, file);
|
|
1301
|
+
return updatedFile;
|
|
1302
|
+
} catch (event) {
|
|
1303
|
+
const isAbort = cancelRef.current || (event == null ? void 0 : event.name) === "AbortError";
|
|
1304
|
+
if (isAbort) {
|
|
1305
|
+
return { ...fileItem, status: "idle", progress: 0 };
|
|
1306
|
+
}
|
|
1307
|
+
const error = createUploadError(
|
|
1308
|
+
"NETWORK_ERROR",
|
|
1309
|
+
event instanceof Error ? event.message : "\u4E0A\u4F20\u5931\u8D25",
|
|
1310
|
+
file,
|
|
1311
|
+
event instanceof Error ? event : void 0
|
|
1312
|
+
);
|
|
1313
|
+
onError == null ? void 0 : onError(error, file);
|
|
1314
|
+
return { ...fileItem, status: "error", error };
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
const uploadFiles = useCallback(async (filesToUpload) => {
|
|
1318
|
+
var _a;
|
|
1319
|
+
if (!action || filesToUpload.length === 0) {
|
|
1320
|
+
setStatus("idle");
|
|
1321
|
+
setProgress(0);
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (!autoUpload) {
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
cancelRef.current = false;
|
|
1328
|
+
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
1329
|
+
abortRef.current = null;
|
|
1330
|
+
setStatus("uploading");
|
|
1331
|
+
setProgress(0);
|
|
1332
|
+
const totalBytes = filesToUpload.reduce((sum, f) => sum + f.file.size, 0);
|
|
1333
|
+
let uploadedBytes = 0;
|
|
1334
|
+
const uploadedFiles = [];
|
|
1335
|
+
for (const fileItem of filesToUpload) {
|
|
1336
|
+
if (cancelRef.current) {
|
|
1337
|
+
setStatus("idle");
|
|
1338
|
+
setProgress(0);
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
setFiles(
|
|
1342
|
+
(prev) => prev.map(
|
|
1343
|
+
(f) => f.id === fileItem.id ? { ...f, status: "uploading" } : f
|
|
1344
|
+
)
|
|
1345
|
+
);
|
|
1346
|
+
const result = await uploadSingleFile(fileItem, totalBytes, uploadedBytes);
|
|
1347
|
+
uploadedFiles.push(result);
|
|
1348
|
+
setFiles(
|
|
1349
|
+
(prev) => prev.map(
|
|
1350
|
+
(f) => f.id === fileItem.id ? { ...f, status: result.status, response: result.response } : f
|
|
1351
|
+
)
|
|
1352
|
+
);
|
|
1353
|
+
if (result.status === "success") {
|
|
1354
|
+
uploadedBytes += fileItem.file.size;
|
|
1355
|
+
setProgress(
|
|
1356
|
+
totalBytes === 0 ? 100 : Math.round(uploadedBytes / totalBytes * 100)
|
|
1357
|
+
);
|
|
1358
|
+
} else if (result.status === "error") {
|
|
1359
|
+
setStatus("error");
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
setStatus("success");
|
|
1364
|
+
setProgress(100);
|
|
1365
|
+
onComplete == null ? void 0 : onComplete(uploadedFiles);
|
|
1366
|
+
}, [action, autoUpload, headers, method, name, data, onComplete, onError, onProgress, onSuccess]);
|
|
1367
|
+
const handleFiles = useCallback((fileList) => {
|
|
1368
|
+
const newFiles = Array.from(fileList);
|
|
1369
|
+
const { valid, errors } = validateFiles(newFiles);
|
|
1370
|
+
errors.forEach((error) => {
|
|
1371
|
+
onError == null ? void 0 : onError(error, error.file);
|
|
1372
|
+
});
|
|
1373
|
+
if (valid.length === 0) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
const fileItems = valid.map((file) => ({
|
|
1377
|
+
id: generateFileId(),
|
|
1378
|
+
file,
|
|
1379
|
+
status: "idle",
|
|
1380
|
+
progress: 0
|
|
1381
|
+
}));
|
|
1382
|
+
setFiles((prev) => {
|
|
1383
|
+
const newFileList = [...prev, ...fileItems];
|
|
1384
|
+
notifyChange(newFileList);
|
|
1385
|
+
return newFileList;
|
|
1386
|
+
});
|
|
1387
|
+
void uploadFiles(fileItems);
|
|
1388
|
+
}, [validateFiles, notifyChange, uploadFiles, onError]);
|
|
1389
|
+
const handleInputChange = (event) => {
|
|
1390
|
+
if (event.target.files) {
|
|
1391
|
+
handleFiles(event.target.files);
|
|
1392
|
+
event.target.value = "";
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
const handleDragOver = (event) => {
|
|
1396
|
+
event.preventDefault();
|
|
1397
|
+
setIsDragging(true);
|
|
1398
|
+
};
|
|
1399
|
+
const handleDragLeave = (event) => {
|
|
1400
|
+
event.preventDefault();
|
|
1401
|
+
setIsDragging(false);
|
|
1402
|
+
};
|
|
1403
|
+
const handleDrop = (event) => {
|
|
1404
|
+
event.preventDefault();
|
|
1405
|
+
setIsDragging(false);
|
|
1406
|
+
if (event.dataTransfer.files && event.dataTransfer.files.length > 0) {
|
|
1407
|
+
handleFiles(event.dataTransfer.files);
|
|
1408
|
+
event.dataTransfer.clearData();
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
const handleCancel = useCallback(() => {
|
|
1412
|
+
var _a;
|
|
1413
|
+
if (status !== "uploading") {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
cancelRef.current = true;
|
|
1417
|
+
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
1418
|
+
abortRef.current = null;
|
|
1419
|
+
setStatus("idle");
|
|
1420
|
+
setProgress(0);
|
|
1421
|
+
setFiles([]);
|
|
1422
|
+
}, [status]);
|
|
1423
|
+
const handleReset = useCallback(() => {
|
|
1424
|
+
setFiles([]);
|
|
1425
|
+
setStatus("idle");
|
|
1426
|
+
setProgress(0);
|
|
1427
|
+
setPreviewUrl(null);
|
|
1428
|
+
if (inputRef.current) {
|
|
1429
|
+
inputRef.current.value = "";
|
|
1430
|
+
}
|
|
1431
|
+
notifyChange([]);
|
|
1432
|
+
}, [notifyChange]);
|
|
1433
|
+
const handleRemoveFile = useCallback((id) => {
|
|
1434
|
+
setFiles((prev) => {
|
|
1435
|
+
const newFiles = prev.filter((f) => f.id !== id);
|
|
1436
|
+
notifyChange(newFiles);
|
|
1437
|
+
return newFiles;
|
|
1438
|
+
});
|
|
1439
|
+
}, [notifyChange]);
|
|
1440
|
+
const handleCancelFile = useCallback((id) => {
|
|
1441
|
+
setFiles(
|
|
1442
|
+
(prev) => prev.map(
|
|
1443
|
+
(f) => f.id === id ? { ...f, status: "idle", progress: 0 } : f
|
|
1444
|
+
)
|
|
1445
|
+
);
|
|
1446
|
+
}, []);
|
|
1447
|
+
useCallback(() => {
|
|
1448
|
+
if (files.length > 0 && autoUpload === false) {
|
|
1449
|
+
const pendingFiles = files.filter((f) => f.status === "idle");
|
|
1450
|
+
if (pendingFiles.length > 0) {
|
|
1451
|
+
uploadFiles(pendingFiles);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}, [files, autoUpload, uploadFiles]);
|
|
1455
|
+
useEffect(() => {
|
|
1456
|
+
var _a;
|
|
1457
|
+
if (mode !== "image") {
|
|
1458
|
+
setPreviewUrl(null);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
const file = (_a = files[0]) == null ? void 0 : _a.file;
|
|
1462
|
+
if (!file || !file.type.startsWith("image/")) {
|
|
1463
|
+
setPreviewUrl(null);
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const url = URL.createObjectURL(file);
|
|
1467
|
+
setPreviewUrl(url);
|
|
1468
|
+
return () => {
|
|
1469
|
+
URL.revokeObjectURL(url);
|
|
1470
|
+
};
|
|
1471
|
+
}, [files, mode]);
|
|
1472
|
+
const triggerProps = {
|
|
1473
|
+
inputId,
|
|
1474
|
+
variant,
|
|
1475
|
+
size,
|
|
1476
|
+
shape,
|
|
1477
|
+
className: cn(isDisabled && "cursor-not-allowed opacity-80"),
|
|
1478
|
+
status,
|
|
1479
|
+
progress,
|
|
1480
|
+
onCancel: handleCancel,
|
|
1481
|
+
onReset: handleReset
|
|
1482
|
+
};
|
|
1483
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1484
|
+
mode === "button" && /* @__PURE__ */ jsx(ButtonUpload, { ...triggerProps }),
|
|
1485
|
+
mode === "image" && /* @__PURE__ */ jsx(ImageUpload, { ...triggerProps, previewUrl }),
|
|
1486
|
+
mode === "dragger" && /* @__PURE__ */ jsx(
|
|
1487
|
+
DraggerUpload,
|
|
1488
|
+
{
|
|
1489
|
+
...triggerProps,
|
|
1490
|
+
isDragging,
|
|
1491
|
+
onDragOver: handleDragOver,
|
|
1492
|
+
onDragLeave: handleDragLeave,
|
|
1493
|
+
onDrop: handleDrop
|
|
1494
|
+
}
|
|
1495
|
+
),
|
|
1496
|
+
/* @__PURE__ */ jsx(
|
|
1497
|
+
"input",
|
|
1498
|
+
{
|
|
1499
|
+
className: "opacity-0 absolute",
|
|
1500
|
+
disabled: isDisabled,
|
|
1501
|
+
id: inputId,
|
|
1502
|
+
ref: inputRef,
|
|
1503
|
+
type: "file",
|
|
1504
|
+
multiple: maxCount !== 1,
|
|
1505
|
+
accept,
|
|
1506
|
+
webkitdirectory: directory ? "" : void 0,
|
|
1507
|
+
onChange: handleInputChange
|
|
1508
|
+
}
|
|
1509
|
+
),
|
|
1510
|
+
/* @__PURE__ */ jsx(
|
|
1511
|
+
FileList,
|
|
1512
|
+
{
|
|
1513
|
+
files,
|
|
1514
|
+
show: showFileList,
|
|
1515
|
+
onRemove: handleRemoveFile,
|
|
1516
|
+
onCancel: handleCancelFile
|
|
1517
|
+
}
|
|
1518
|
+
)
|
|
1519
|
+
] });
|
|
1520
|
+
}
|
|
1521
|
+
var Upload_default = Upload;
|
|
1522
|
+
|
|
1523
|
+
export { Avatar, Bubble, Button, playBoard_default as PlayBoard, Upload_default as Upload };
|
|
1524
|
+
//# sourceMappingURL=index.js.map
|
|
1525
|
+
//# sourceMappingURL=index.js.map
|